Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(permission): global project roles cannot view/execute/approve tickets #4117

Merged
merged 1 commit into from
Jan 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@
import com.oceanbase.odc.core.authority.permission.PermissionProvider;
import com.oceanbase.odc.core.authority.session.factory.DefaultSecuritySessionFactory;
import com.oceanbase.odc.metadb.iam.PermissionRepository;
import com.oceanbase.odc.metadb.iam.resourcerole.UserResourceRoleRepository;
import com.oceanbase.odc.service.iam.ResourcePermissionExtractor;
import com.oceanbase.odc.service.iam.ResourceRoleBasedPermissionExtractor;
import com.oceanbase.odc.service.iam.ResourceRoleService;
import com.oceanbase.odc.service.iam.auth.AuthenticationFacade;
import com.oceanbase.odc.service.iam.auth.DefaultPermissionProvider;
import com.oceanbase.odc.service.iam.auth.EmptyAuthenticator;
Expand All @@ -55,10 +55,10 @@ public abstract class BaseAuthConfiguration {

@Bean
public SecurityManager servletSecurityManager(PermissionRepository permissionRepository,
ResourcePermissionExtractor permissionMapper, UserResourceRoleRepository resourceRoleRepository,
ResourcePermissionExtractor permissionMapper, ResourceRoleService resourceRoleService,
ResourceRoleBasedPermissionExtractor resourceRoleBasedPermissionExtractor,
AuthenticationFacade authenticationFacade) {
Collection<Authorizer> authorizers = authorizers(permissionRepository, permissionMapper, resourceRoleRepository,
Collection<Authorizer> authorizers = authorizers(permissionRepository, permissionMapper, resourceRoleService,
resourceRoleBasedPermissionExtractor);
DefaultAuthorizerManager authorizerManager = new DefaultAuthorizerManager(authorizers);
PermissionStrategy permissionStrategy = permissionStrategy();
Expand Down Expand Up @@ -94,7 +94,6 @@ protected ReturnValueProvider returnValueProvider(AuthorizerManager manager, Per
}

protected abstract Collection<Authorizer> authorizers(PermissionRepository permissionRepository,
ResourcePermissionExtractor resourcePermissionExtractor, UserResourceRoleRepository resourceRoleRepository,
ResourcePermissionExtractor resourcePermissionExtractor, ResourceRoleService resourceRoleService,
ResourceRoleBasedPermissionExtractor resourceRoleBasedPermissionExtractor);

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@

import com.oceanbase.odc.core.authority.auth.Authorizer;
import com.oceanbase.odc.metadb.iam.PermissionRepository;
import com.oceanbase.odc.metadb.iam.resourcerole.UserResourceRoleRepository;
import com.oceanbase.odc.service.iam.ResourcePermissionExtractor;
import com.oceanbase.odc.service.iam.ResourceRoleBasedPermissionExtractor;
import com.oceanbase.odc.service.iam.ResourceRoleService;
import com.oceanbase.odc.service.iam.auth.ComposedAuthorizer;
import com.oceanbase.odc.service.iam.auth.DefaultAuthorizer;
import com.oceanbase.odc.service.iam.auth.ResourceRoleAuthorizer;
Expand All @@ -39,11 +39,11 @@ public class DefaultAuthConfiguration extends BaseAuthConfiguration {

@Override
protected Collection<Authorizer> authorizers(PermissionRepository permissionRepository,
ResourcePermissionExtractor resourcePermissionExtractor, UserResourceRoleRepository resourceRoleRepository,
ResourcePermissionExtractor resourcePermissionExtractor, ResourceRoleService resourceRoleService,
ResourceRoleBasedPermissionExtractor resourceRoleBasedPermissionExtractor) {
DefaultAuthorizer defaultAuthorizer = new DefaultAuthorizer(permissionRepository, resourcePermissionExtractor);
ResourceRoleAuthorizer resourceRoleAuthorizer =
new ResourceRoleAuthorizer(resourceRoleRepository, resourceRoleBasedPermissionExtractor);
new ResourceRoleAuthorizer(resourceRoleService, resourceRoleBasedPermissionExtractor);
ComposedAuthorizer composedAuthorizer = new ComposedAuthorizer(defaultAuthorizer, resourceRoleAuthorizer);
return Arrays.asList(defaultAuthorizer, resourceRoleAuthorizer, composedAuthorizer);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,6 @@ Optional<PermissionEntity> findByOrganizationIdAndActionAndResourceIdentifier(Lo
nativeQuery = true)
List<PermissionEntity> findByExpireTimeBefore(@Param("expireTime") Date expireTime);


@Query(value = "select p.* from iam_user_permission up inner join iam_permission p on up.permission_id=p.id "
+ "where up.user_id=:userId and up.organization_id=:organizationId and p.resource_identifier=:resourceIdentifier "
+ "and p.action in (:actions)",
nativeQuery = true)
List<PermissionEntity> findByUserIdAndOrganizationIdAndResourceIdentifierAndActionIn(@Param("userId") Long userId,
@Param("organizationId") Long organizationId, @Param("resourceIdentifier") String resourceIdentifier,
@Param("actions") Collection<String> actions);

@Modifying
@Transactional
@Query(value = "delete from iam_permission p where p.id in (:ids)", nativeQuery = true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,12 @@
import com.oceanbase.odc.metadb.iam.UserEntity;
import com.oceanbase.odc.metadb.iam.UserRepository;
import com.oceanbase.odc.metadb.iam.UserRoleRepository;
import com.oceanbase.odc.metadb.iam.resourcerole.UserResourceRoleEntity;
import com.oceanbase.odc.metadb.iam.resourcerole.UserResourceRoleRepository;
import com.oceanbase.odc.service.flow.model.FlowNodeStatus;
import com.oceanbase.odc.service.iam.ResourceRoleService;
import com.oceanbase.odc.service.iam.UserService;
import com.oceanbase.odc.service.iam.auth.AuthenticationFacade;
import com.oceanbase.odc.service.iam.model.UserResourceRole;

import lombok.NonNull;

Expand Down Expand Up @@ -163,12 +163,12 @@ private Map<Long, Set<UserEntity>> getUsersByFlowInstanceIdsAndStatus(@NonNull C
if (resourceRoleIdentifiers.isEmpty()) {
return new HashMap<>();
}
Map<String, Set<Long>> identifier2UserIds = userResourceRoleRepository.findByResourceIdsAndResourceRoleIdsIn(
resourceRoleIdentifiers).stream()
Map<String, Set<Long>> identifier2UserIds = resourceRoleService
.listByResourceIdentifierIn(resourceRoleIdentifiers).stream()
.collect(Collectors.groupingBy(o -> String.format("%s:%s", o.getResourceId(), o.getResourceRoleId())))
.entrySet()
.stream().collect(Collectors.toMap(entry -> entry.getKey(),
entry -> entry.getValue().stream().map(UserResourceRoleEntity::getUserId).collect(
entry -> entry.getValue().stream().map(UserResourceRole::getUserId).collect(
Collectors.toSet())));
// map approval instance ids to users ids
Map<Long, Set<Long>> instanceId2UserIds =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -727,7 +727,7 @@ public <T> T mapFlowInstanceWithReadPermission(@NonNull Long flowInstanceId, Fun
}

public <T> T mapFlowInstanceWithWritePermission(@NonNull Long flowInstanceId, Function<FlowInstance, T> mapper) {
return mapFlowInstance(flowInstanceId, mapper, flowPermissionHelper.withProjectOwnerCheck());
return mapFlowInstance(flowInstanceId, mapper, flowPermissionHelper.withExecutableCheck());
}

public <T> T mapFlowInstanceWithApprovalPermission(@NonNull Long flowInstanceId, Function<FlowInstance, T> mapper) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/
package com.oceanbase.odc.service.flow;

import java.util.Collections;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Set;
Expand Down Expand Up @@ -62,11 +62,11 @@ public Consumer<FlowInstance> withProjectMemberCheck() {
.hasProjectRole(flowInstance.getProjectId(), ResourceRoleName.all()));
}

public Consumer<FlowInstance> withProjectOwnerCheck() {
public Consumer<FlowInstance> withProjectOwnerOrDBACheck() {
return withProjectPermissionCheck(
flowInstance -> flowInstance.getProjectId() != null && projectPermissionValidator
.hasProjectRole(flowInstance.getProjectId(),
Collections.singletonList(ResourceRoleName.OWNER)));
Arrays.asList(ResourceRoleName.OWNER, ResourceRoleName.DBA)));
}

public Consumer<FlowInstance> withApprovableCheck() {
Expand All @@ -80,6 +80,16 @@ public Consumer<FlowInstance> withApprovableCheck() {
};
}

public Consumer<FlowInstance> withExecutableCheck() {
return flowInstance -> {
try {
withCreatorCheck().accept(flowInstance);
} catch (Exception ex) {
withProjectOwnerOrDBACheck().accept(flowInstance);
}
};
}

public Consumer<FlowInstance> withCreatorCheck() {
return flowInstance -> {
if (!Objects.equals(authenticationFacade.currentUserId(), flowInstance.getCreatorId())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ public class FlowTaskInstanceService {
public FlowInstanceDetailResp executeTask(@NotNull Long id) throws IOException {
List<FlowTaskInstance> instances =
filterTaskInstance(id, instance -> instance.getStatus() == FlowNodeStatus.PENDING,
flowPermissionHelper.withCreatorCheck());
flowPermissionHelper.withExecutableCheck());
PreConditions.validExists(ResourceType.ODC_FLOW_TASK_INSTANCE, "flowInstanceId", id,
() -> instances.size() > 0);
Verify.singleton(instances, "FlowTaskInstance");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,6 @@
import com.oceanbase.odc.metadb.iam.UserRepository;
import com.oceanbase.odc.metadb.iam.UserRoleEntity;
import com.oceanbase.odc.metadb.iam.UserRoleRepository;
import com.oceanbase.odc.metadb.iam.resourcerole.UserResourceRoleEntity;
import com.oceanbase.odc.metadb.iam.resourcerole.UserResourceRoleRepository;
import com.oceanbase.odc.metadb.integration.IntegrationEntity;
import com.oceanbase.odc.metadb.regulation.risklevel.RiskLevelRepository;
import com.oceanbase.odc.metadb.task.TaskEntity;
Expand All @@ -74,6 +72,7 @@
import com.oceanbase.odc.service.connection.database.model.Database;
import com.oceanbase.odc.service.connection.model.ConnectionConfig;
import com.oceanbase.odc.service.connection.util.ConnectionMapper;
import com.oceanbase.odc.service.databasechange.model.DatabaseChangeDatabase;
import com.oceanbase.odc.service.flow.ApprovalPermissionService;
import com.oceanbase.odc.service.flow.instance.FlowInstance;
import com.oceanbase.odc.service.flow.model.FlowInstanceDetailResp;
Expand All @@ -83,7 +82,10 @@
import com.oceanbase.odc.service.flow.model.FlowNodeStatus;
import com.oceanbase.odc.service.flow.model.FlowTaskExecutionStrategy;
import com.oceanbase.odc.service.flow.task.model.DBStructureComparisonParameter;
import com.oceanbase.odc.service.flow.task.model.MultipleDatabaseChangeParameters;
import com.oceanbase.odc.service.iam.ResourceRoleService;
import com.oceanbase.odc.service.iam.auth.AuthenticationFacade;
import com.oceanbase.odc.service.iam.model.UserResourceRole;
import com.oceanbase.odc.service.integration.IntegrationService;
import com.oceanbase.odc.service.integration.client.ApprovalClient;
import com.oceanbase.odc.service.integration.model.ApprovalProperties;
Expand Down Expand Up @@ -134,7 +136,7 @@ public class FlowResponseMapperFactory {
@Autowired
private FlowInstanceRepository flowInstanceRepository;
@Autowired
private UserResourceRoleRepository userResourceRoleRepository;
private ResourceRoleService resourceRoleService;
@Autowired
private RiskLevelRepository riskLevelRepository;
@Autowired
Expand Down Expand Up @@ -226,9 +228,9 @@ private FlowNodeInstanceMapper generateNodeMapper(@NonNull Collection<Long> flow
&& candidateResourceRoleIdentifiers.isEmpty()) {
return Collections.emptyList();
} else if (!candidateResourceRoleIdentifiers.isEmpty()) {
Set<Long> resourceRoleUserIds = userResourceRoleRepository
.findByResourceIdsAndResourceRoleIdsIn(candidateResourceRoleIdentifiers)
.stream().map(UserResourceRoleEntity::getUserId).collect(Collectors.toSet());
Set<Long> resourceRoleUserIds =
resourceRoleService.listByResourceIdentifierIn(candidateResourceRoleIdentifiers)
.stream().map(UserResourceRole::getUserId).collect(Collectors.toSet());
return CollectionUtils.isEmpty(resourceRoleUserIds) ? Collections.emptyList()
: userRepository.findByUserIdsAndEnabled(resourceRoleUserIds, true);
} else if (candidateUserIds.isEmpty()) {
Expand Down Expand Up @@ -329,6 +331,7 @@ private FlowInstanceMapper generateMapper(@NonNull Collection<Long> flowInstance
.map(TaskEntity::getDatabaseId)
.filter(Objects::nonNull).collect(Collectors.toSet());

databaseIds.addAll(collectMultiDatabaseChangeDatabaseIds(taskId2TaskEntity));
databaseIds.addAll(collectDBStructureComparisonDatabaseIds(taskId2TaskEntity));
Set<Long> projectIds = new HashSet<>();
Map<Long, Project> id2Project = new HashMap<>();
Expand Down Expand Up @@ -480,4 +483,17 @@ private Set<Long> collectApplyProjectIds(Map<Long, TaskEntity> taskId2TaskEntity
.collect(Collectors.toSet());
return applyProjectIds;
}

private Set<Long> collectMultiDatabaseChangeDatabaseIds(Map<Long, TaskEntity> taskId2TaskEntity) {
Set<Long> databaseIds = new HashSet<>();
taskId2TaskEntity.values().stream()
.filter(task -> task.getTaskType().equals(TaskType.MULTIPLE_ASYNC))
.forEach(task -> {
MultipleDatabaseChangeParameters parameter = JsonUtils.fromJson(
task.getParametersJson(), MultipleDatabaseChangeParameters.class);
databaseIds.addAll(parameter.getDatabases().stream().map(DatabaseChangeDatabase::getId)
.collect(Collectors.toSet()));
});
return databaseIds;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@
import com.oceanbase.odc.core.flow.util.EmptyExecutionListener;
import com.oceanbase.odc.metadb.flow.FlowInstanceApprovalViewEntity;
import com.oceanbase.odc.metadb.flow.FlowInstanceApprovalViewRepository;
import com.oceanbase.odc.metadb.iam.resourcerole.UserResourceRoleEntity;
import com.oceanbase.odc.metadb.iam.resourcerole.UserResourceRoleRepository;
import com.oceanbase.odc.metadb.task.TaskEntity;
import com.oceanbase.odc.service.flow.FlowInstanceService;
import com.oceanbase.odc.service.flow.FlowableAdaptor;
import com.oceanbase.odc.service.flow.instance.FlowApprovalInstance;
import com.oceanbase.odc.service.iam.ResourceRoleService;
import com.oceanbase.odc.service.iam.model.UserResourceRole;
import com.oceanbase.odc.service.notification.Broker;
import com.oceanbase.odc.service.notification.NotificationProperties;
import com.oceanbase.odc.service.notification.helper.EventBuilder;
Expand All @@ -59,9 +59,9 @@ public class ApprovalStatusNotifyListener extends EmptyExecutionListener {
@Autowired
private FlowableAdaptor flowableAdaptor;
@Autowired
FlowInstanceApprovalViewRepository flowInstanceApprovalViewRepository;
private FlowInstanceApprovalViewRepository flowInstanceApprovalViewRepository;
@Autowired
UserResourceRoleRepository userResourceRoleRepository;
private ResourceRoleService resourceRoleService;

@Override
protected void onExecutiuonStart(DelegateExecution execution) {
Expand All @@ -79,12 +79,12 @@ protected void onExecutiuonStart(DelegateExecution execution) {
List<FlowInstanceApprovalViewEntity> approvals =
flowInstanceApprovalViewRepository.findByIdIn(Collections.singletonList(target.getId()));
if (CollectionUtils.isNotEmpty(approvals)) {
List<UserResourceRoleEntity> userResourceRoles =
userResourceRoleRepository.findByResourceIdsAndResourceRoleIdsIn(
List<UserResourceRole> userResourceRoles =
resourceRoleService.listByResourceIdentifierIn(
approvals.stream().map(FlowInstanceApprovalViewEntity::getResourceRoleIdentifier)
.collect(Collectors.toSet()));
approverIds = CollectionUtils.isEmpty(userResourceRoles) ? null
: userResourceRoles.stream().map(UserResourceRoleEntity::getUserId).collect(Collectors.toSet());
: userResourceRoles.stream().map(UserResourceRole::getUserId).collect(Collectors.toSet());
}
Event event = eventBuilder.ofPendingApprovalTask(taskEntity, approverIds);
broker.enqueueEvent(event);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import com.oceanbase.odc.core.shared.constant.ResourceRoleName;
import com.oceanbase.odc.core.shared.constant.ResourceType;
Expand Down Expand Up @@ -62,4 +64,13 @@ public List<UserGlobalResourceRole> findGlobalResourceRoleUsersByOrganizationIdA
return userRoleRepository.findByOrganizationIdAndNameIn(
organizationId, Arrays.asList(GlobalResourceRoleUtil.getGlobalRoleName(resourceRoleName)));
}

public List<UserGlobalResourceRole> findGlobalResourceRoleUsersByOrganizationIdAndRoleIn(Long organizationId,
Set<ResourceRoleName> resourceRoleNames) {
if (CollectionUtils.isEmpty(resourceRoleNames)) {
return Collections.emptyList();
}
return userRoleRepository.findByOrganizationIdAndNameIn(organizationId,
GlobalResourceRoleUtil.getGlobalRoleName(resourceRoleNames));
}
}
Loading
Loading