diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/config/BaseAuthConfiguration.java b/server/odc-service/src/main/java/com/oceanbase/odc/config/BaseAuthConfiguration.java index 396dfb777f..5f4edb1899 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/config/BaseAuthConfiguration.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/config/BaseAuthConfiguration.java @@ -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; @@ -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 authorizers = authorizers(permissionRepository, permissionMapper, resourceRoleRepository, + Collection authorizers = authorizers(permissionRepository, permissionMapper, resourceRoleService, resourceRoleBasedPermissionExtractor); DefaultAuthorizerManager authorizerManager = new DefaultAuthorizerManager(authorizers); PermissionStrategy permissionStrategy = permissionStrategy(); @@ -94,7 +94,6 @@ protected ReturnValueProvider returnValueProvider(AuthorizerManager manager, Per } protected abstract Collection authorizers(PermissionRepository permissionRepository, - ResourcePermissionExtractor resourcePermissionExtractor, UserResourceRoleRepository resourceRoleRepository, + ResourcePermissionExtractor resourcePermissionExtractor, ResourceRoleService resourceRoleService, ResourceRoleBasedPermissionExtractor resourceRoleBasedPermissionExtractor); - } diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/config/DefaultAuthConfiguration.java b/server/odc-service/src/main/java/com/oceanbase/odc/config/DefaultAuthConfiguration.java index ef884b1598..f9e14e3d8a 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/config/DefaultAuthConfiguration.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/config/DefaultAuthConfiguration.java @@ -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; @@ -39,11 +39,11 @@ public class DefaultAuthConfiguration extends BaseAuthConfiguration { @Override protected Collection 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); } diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/metadb/iam/PermissionRepository.java b/server/odc-service/src/main/java/com/oceanbase/odc/metadb/iam/PermissionRepository.java index 7b52bce20f..c2a0856ec7 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/metadb/iam/PermissionRepository.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/metadb/iam/PermissionRepository.java @@ -97,15 +97,6 @@ Optional findByOrganizationIdAndActionAndResourceIdentifier(Lo nativeQuery = true) List 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 findByUserIdAndOrganizationIdAndResourceIdentifierAndActionIn(@Param("userId") Long userId, - @Param("organizationId") Long organizationId, @Param("resourceIdentifier") String resourceIdentifier, - @Param("actions") Collection actions); - @Modifying @Transactional @Query(value = "delete from iam_permission p where p.id in (:ids)", nativeQuery = true) diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/flow/ApprovalPermissionService.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/flow/ApprovalPermissionService.java index 070c3bb365..b173078eed 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/flow/ApprovalPermissionService.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/flow/ApprovalPermissionService.java @@ -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; @@ -163,12 +163,12 @@ private Map> getUsersByFlowInstanceIdsAndStatus(@NonNull C if (resourceRoleIdentifiers.isEmpty()) { return new HashMap<>(); } - Map> identifier2UserIds = userResourceRoleRepository.findByResourceIdsAndResourceRoleIdsIn( - resourceRoleIdentifiers).stream() + Map> 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> instanceId2UserIds = diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/flow/FlowInstanceService.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/flow/FlowInstanceService.java index 9328db7877..76620d1853 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/flow/FlowInstanceService.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/flow/FlowInstanceService.java @@ -727,7 +727,7 @@ public T mapFlowInstanceWithReadPermission(@NonNull Long flowInstanceId, Fun } public T mapFlowInstanceWithWritePermission(@NonNull Long flowInstanceId, Function mapper) { - return mapFlowInstance(flowInstanceId, mapper, flowPermissionHelper.withProjectOwnerCheck()); + return mapFlowInstance(flowInstanceId, mapper, flowPermissionHelper.withExecutableCheck()); } public T mapFlowInstanceWithApprovalPermission(@NonNull Long flowInstanceId, Function mapper) { diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/flow/FlowPermissionHelper.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/flow/FlowPermissionHelper.java index f11eaa53ce..f4a7c1b58d 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/flow/FlowPermissionHelper.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/flow/FlowPermissionHelper.java @@ -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; @@ -62,11 +62,11 @@ public Consumer withProjectMemberCheck() { .hasProjectRole(flowInstance.getProjectId(), ResourceRoleName.all())); } - public Consumer withProjectOwnerCheck() { + public Consumer withProjectOwnerOrDBACheck() { return withProjectPermissionCheck( flowInstance -> flowInstance.getProjectId() != null && projectPermissionValidator .hasProjectRole(flowInstance.getProjectId(), - Collections.singletonList(ResourceRoleName.OWNER))); + Arrays.asList(ResourceRoleName.OWNER, ResourceRoleName.DBA))); } public Consumer withApprovableCheck() { @@ -80,6 +80,16 @@ public Consumer withApprovableCheck() { }; } + public Consumer withExecutableCheck() { + return flowInstance -> { + try { + withCreatorCheck().accept(flowInstance); + } catch (Exception ex) { + withProjectOwnerOrDBACheck().accept(flowInstance); + } + }; + } + public Consumer withCreatorCheck() { return flowInstance -> { if (!Objects.equals(authenticationFacade.currentUserId(), flowInstance.getCreatorId())) { diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/flow/FlowTaskInstanceService.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/flow/FlowTaskInstanceService.java index b4b69d7ff4..617e28a50e 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/flow/FlowTaskInstanceService.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/flow/FlowTaskInstanceService.java @@ -174,7 +174,7 @@ public class FlowTaskInstanceService { public FlowInstanceDetailResp executeTask(@NotNull Long id) throws IOException { List 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"); diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/flow/factory/FlowResponseMapperFactory.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/flow/factory/FlowResponseMapperFactory.java index 2166f8f0b9..55f8a3a72e 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/flow/factory/FlowResponseMapperFactory.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/flow/factory/FlowResponseMapperFactory.java @@ -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; @@ -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; @@ -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; @@ -134,7 +136,7 @@ public class FlowResponseMapperFactory { @Autowired private FlowInstanceRepository flowInstanceRepository; @Autowired - private UserResourceRoleRepository userResourceRoleRepository; + private ResourceRoleService resourceRoleService; @Autowired private RiskLevelRepository riskLevelRepository; @Autowired @@ -226,9 +228,9 @@ private FlowNodeInstanceMapper generateNodeMapper(@NonNull Collection flow && candidateResourceRoleIdentifiers.isEmpty()) { return Collections.emptyList(); } else if (!candidateResourceRoleIdentifiers.isEmpty()) { - Set resourceRoleUserIds = userResourceRoleRepository - .findByResourceIdsAndResourceRoleIdsIn(candidateResourceRoleIdentifiers) - .stream().map(UserResourceRoleEntity::getUserId).collect(Collectors.toSet()); + Set 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()) { @@ -329,6 +331,7 @@ private FlowInstanceMapper generateMapper(@NonNull Collection flowInstance .map(TaskEntity::getDatabaseId) .filter(Objects::nonNull).collect(Collectors.toSet()); + databaseIds.addAll(collectMultiDatabaseChangeDatabaseIds(taskId2TaskEntity)); databaseIds.addAll(collectDBStructureComparisonDatabaseIds(taskId2TaskEntity)); Set projectIds = new HashSet<>(); Map id2Project = new HashMap<>(); @@ -480,4 +483,17 @@ private Set collectApplyProjectIds(Map taskId2TaskEntity .collect(Collectors.toSet()); return applyProjectIds; } + + private Set collectMultiDatabaseChangeDatabaseIds(Map taskId2TaskEntity) { + Set 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; + } } diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/flow/listener/ApprovalStatusNotifyListener.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/flow/listener/ApprovalStatusNotifyListener.java index dc63c66be1..74cfc729b6 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/flow/listener/ApprovalStatusNotifyListener.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/flow/listener/ApprovalStatusNotifyListener.java @@ -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; @@ -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) { @@ -79,12 +79,12 @@ protected void onExecutiuonStart(DelegateExecution execution) { List approvals = flowInstanceApprovalViewRepository.findByIdIn(Collections.singletonList(target.getId())); if (CollectionUtils.isNotEmpty(approvals)) { - List userResourceRoles = - userResourceRoleRepository.findByResourceIdsAndResourceRoleIdsIn( + List 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); diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/iam/GlobalResourceRoleService.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/iam/GlobalResourceRoleService.java index 483167fdf8..b2be8729b1 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/iam/GlobalResourceRoleService.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/iam/GlobalResourceRoleService.java @@ -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; @@ -62,4 +64,13 @@ public List findGlobalResourceRoleUsersByOrganizationIdA return userRoleRepository.findByOrganizationIdAndNameIn( organizationId, Arrays.asList(GlobalResourceRoleUtil.getGlobalRoleName(resourceRoleName))); } + + public List findGlobalResourceRoleUsersByOrganizationIdAndRoleIn(Long organizationId, + Set resourceRoleNames) { + if (CollectionUtils.isEmpty(resourceRoleNames)) { + return Collections.emptyList(); + } + return userRoleRepository.findByOrganizationIdAndNameIn(organizationId, + GlobalResourceRoleUtil.getGlobalRoleName(resourceRoleNames)); + } } diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/iam/ResourceRoleService.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/iam/ResourceRoleService.java index 52c5b946f6..178ada63d8 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/iam/ResourceRoleService.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/iam/ResourceRoleService.java @@ -18,6 +18,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -184,15 +185,19 @@ public Optional findResourceRoleById(@NonNull Long id) { public List listByResourceTypeAndResourceId(ResourceType resourceType, Long resourceId) { List userResourceRoles = fromEntities(userResourceRoleRepository.listByResourceTypeAndId(resourceType, resourceId)); + if (resourceType == ResourceType.ODC_DATABASE) { + return userResourceRoles; + } List globalResourceRoles = globalResourceRoleService .findGlobalResourceRoleUsersByOrganizationId(authenticationFacade.currentOrganizationId()); if (CollectionUtils.isEmpty(globalResourceRoles)) { return userResourceRoles; } + Map resourceRoleName2Id = getProjectResourceRoleName2Id(); globalResourceRoles.stream().map( i -> new UserResourceRole(i.getUserId(), resourceId, ResourceType.ODC_PROJECT, i.getResourceRole(), - true)) + resourceRoleName2Id.get(i.getResourceRole()), true)) .forEach(userResourceRoles::add); return userResourceRoles; } @@ -203,15 +208,19 @@ public List listByResourceTypeAndResourceIdIn(ResourceType res @NotEmpty Collection resourceIds) { List userResourceRoles = fromEntities(userResourceRoleRepository.listByResourceTypeAndIdIn(resourceType, resourceIds)); + if (resourceType == ResourceType.ODC_DATABASE) { + return userResourceRoles; + } List globalResourceRoles = globalResourceRoleService .findGlobalResourceRoleUsersByOrganizationId(authenticationFacade.currentOrganizationId()); if (CollectionUtils.isEmpty(globalResourceRoles)) { return userResourceRoles; } + Map resourceRoleName2Id = getProjectResourceRoleName2Id(); globalResourceRoles.stream().flatMap(i -> resourceIds.stream().map( resourceId -> new UserResourceRole(i.getUserId(), resourceId, ResourceType.ODC_PROJECT, - i.getResourceRole(), true))) + i.getResourceRole(), resourceRoleName2Id.get(i.getResourceRole()), true))) .forEach(userResourceRoles::add); return userResourceRoles; } @@ -257,10 +266,11 @@ public List listByOrganizationIdAndUserId(Long organizationId, if (CollectionUtils.isEmpty(globalResourceRoles)) { return userResourceRoles; } - projectRepository.findAllByOrganizationId(organizationId).stream().filter(p -> !p.getArchived()) + Map resourceRoleName2Id = getProjectResourceRoleName2Id(); + projectRepository.findAllByOrganizationId(organizationId).stream() .forEach(p -> globalResourceRoles.stream() .map(i -> new UserResourceRole(i.getUserId(), p.getId(), ResourceType.ODC_PROJECT, - i.getResourceRole(), true)) + i.getResourceRole(), resourceRoleName2Id.get(i.getResourceRole()), true)) .forEach(userResourceRoles::add)); return userResourceRoles; } @@ -275,10 +285,11 @@ public List listByUserId(Long userId) { if (CollectionUtils.isEmpty(globalResourceRoles)) { return userResourceRoles; } + Map resourceRoleName2Id = getProjectResourceRoleName2Id(); projectRepository.findAllByOrganizationId(authenticationFacade.currentOrganizationId()).stream() - .filter(p -> !p.getArchived()).forEach(p -> globalResourceRoles.stream() + .forEach(p -> globalResourceRoles.stream() .map(i -> new UserResourceRole(i.getUserId(), p.getId(), ResourceType.ODC_PROJECT, - i.getResourceRole(), true)) + i.getResourceRole(), resourceRoleName2Id.get(i.getResourceRole()), true)) .forEach(userResourceRoles::add)); return userResourceRoles; } @@ -288,17 +299,67 @@ public List listByResourceIdAndTypeAndName(Long resourceId, Re String roleName) { List userResourceRoles = fromEntities( userResourceRoleRepository.findByResourceIdAndTypeAndName(resourceId, resourceType, roleName)); + if (resourceType == ResourceType.ODC_DATABASE) { + return userResourceRoles; + } List globalResourceRoles = globalResourceRoleService.findGlobalResourceRoleUsersByOrganizationIdAndRole( authenticationFacade.currentOrganizationId(), resourceType, ResourceRoleName.valueOf(roleName)); if (CollectionUtils.isEmpty(globalResourceRoles)) { return userResourceRoles; } + Map resourceRoleName2Id = getProjectResourceRoleName2Id(); globalResourceRoles.stream().map(i -> new UserResourceRole(i.getUserId(), resourceId, resourceType, - i.getResourceRole(), true)).forEach(userResourceRoles::add); + i.getResourceRole(), resourceRoleName2Id.get(i.getResourceRole()), true)) + .forEach(userResourceRoles::add); return userResourceRoles; } + @SkipAuthorize("internal usage") + public List listByResourceIdentifierIn(Set resourceIdentifiers) { + List userResourceRoles = + fromEntities(userResourceRoleRepository.findByResourceIdsAndResourceRoleIdsIn(resourceIdentifiers)); + List globalUserResourceRoles = globalResourceRoleService + .findGlobalResourceRoleUsersByOrganizationIdAndRoleIn(authenticationFacade.currentOrganizationId(), + filterResourceRoleNames(ResourceType.ODC_PROJECT, resourceIdentifiers)); + if (CollectionUtils.isEmpty(globalUserResourceRoles)) { + return userResourceRoles; + } + Map resourceRoleName2Id = getProjectResourceRoleName2Id(); + projectRepository.findAllByOrganizationId(authenticationFacade.currentOrganizationId()).stream() + .forEach(p -> globalUserResourceRoles.stream() + .map(i -> new UserResourceRole(i.getUserId(), p.getId(), ResourceType.ODC_PROJECT, + i.getResourceRole(), resourceRoleName2Id.get(i.getResourceRole()), true)) + .forEach(userResourceRoles::add)); + return userResourceRoles; + } + + private Map getProjectResourceRoleName2Id() { + return resourceRoleRepository.findByResourceType(ResourceType.ODC_PROJECT).stream().collect(Collectors.toMap( + ResourceRoleEntity::getRoleName, ResourceRoleEntity::getId, (v1, v2) -> v2)); + } + + private Set filterResourceRoleNames(ResourceType resourceType, Set resourceIdentifiers) { + if (CollectionUtils.isEmpty(resourceIdentifiers)) { + return Collections.emptySet(); + } + Map id2ResourceRoleName = resourceRoleRepository.findByResourceType(resourceType) + .stream() + .collect(Collectors.toMap(ResourceRoleEntity::getId, ResourceRoleEntity::getRoleName, (v1, v2) -> v2)); + Set filtered = new HashSet<>(); + resourceIdentifiers.stream().forEach(identifier -> { + String[] parts = identifier.split(":"); + if (parts.length != 2) { + throw new UnexpectedException("invalid resource identifier, identifier=" + identifier); + } + Long roleId = Long.parseLong(parts[1]); + if (id2ResourceRoleName.containsKey(roleId)) { + filtered.add(id2ResourceRoleName.get(roleId)); + } + }); + return filtered; + } + private List fromEntities(Collection entities) { if (CollectionUtils.isEmpty(entities)) { return new ArrayList<>(); @@ -320,7 +381,16 @@ private UserResourceRole fromEntity(UserResourceRoleEntity entity, ResourceRoleE model.setResourceType(resourceRole.getResourceType()); model.setResourceId(entity.getResourceId()); model.setUserId(entity.getUserId()); + model.setResourceRoleId(resourceRole.getId()); return model; } + public static UserResourceRoleEntity toEntity(UserResourceRole model) { + UserResourceRoleEntity entity = new UserResourceRoleEntity(); + entity.setResourceId(model.getResourceId()); + entity.setUserId(model.getUserId()); + entity.setResourceRoleId(model.getResourceRoleId()); + return entity; + } + } diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/iam/auth/DefaultAuthorizationFacade.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/iam/auth/DefaultAuthorizationFacade.java index 0ed97ce37a..e27222b67b 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/iam/auth/DefaultAuthorizationFacade.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/iam/auth/DefaultAuthorizationFacade.java @@ -60,9 +60,9 @@ import com.oceanbase.odc.metadb.iam.UserEntity; import com.oceanbase.odc.metadb.iam.UserRepository; import com.oceanbase.odc.metadb.iam.resourcerole.UserResourceRoleEntity; -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.model.User; import com.oceanbase.odc.service.resourcegroup.model.ResourceIdentifier; @@ -79,7 +79,7 @@ public abstract class DefaultAuthorizationFacade implements AuthorizationFacade @Autowired private PermissionRepository repository; @Autowired - private UserResourceRoleRepository resourceRoleService; + private ResourceRoleService resourceRoleService; @Autowired private UserRepository userRepository; @Autowired @@ -205,9 +205,9 @@ private List getAllPermissions(Principal principal) { .stream().filter(permission -> !Objects.isNull(permission)).collect(Collectors.toList()); List resourceRoles = resourceRoleService - .findByOrganizationIdAndUserId(authenticationFacade.currentOrganizationId(), odcUser.getId()) + .listByOrganizationIdAndUserId(authenticationFacade.currentOrganizationId(), odcUser.getId()) .stream() - .filter(Objects::nonNull).collect(Collectors.toList()); + .filter(Objects::nonNull).map(ResourceRoleService::toEntity).collect(Collectors.toList()); return ListUtils.union(permissionMapper.getResourcePermissions(permissionEntityList), resourceRoleBasedPermissionExtractor.getResourcePermissions(resourceRoles)); } diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/iam/auth/ResourceRoleAuthorizer.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/iam/auth/ResourceRoleAuthorizer.java index 25477cbbbe..2a3ae8b03f 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/iam/auth/ResourceRoleAuthorizer.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/iam/auth/ResourceRoleAuthorizer.java @@ -23,8 +23,8 @@ import com.oceanbase.odc.core.authority.permission.Permission; import com.oceanbase.odc.metadb.iam.resourcerole.UserResourceRoleEntity; -import com.oceanbase.odc.metadb.iam.resourcerole.UserResourceRoleRepository; import com.oceanbase.odc.service.iam.ResourceRoleBasedPermissionExtractor; +import com.oceanbase.odc.service.iam.ResourceRoleService; import com.oceanbase.odc.service.iam.model.User; /** @@ -33,12 +33,12 @@ * @Description: [] */ public class ResourceRoleAuthorizer extends BaseAuthorizer { - protected final UserResourceRoleRepository repository; + protected final ResourceRoleService resourceRoleService; protected final ResourceRoleBasedPermissionExtractor permissionMapper; - public ResourceRoleAuthorizer(UserResourceRoleRepository repository, + public ResourceRoleAuthorizer(ResourceRoleService resourceRoleService, ResourceRoleBasedPermissionExtractor permissionMapper) { - this.repository = repository; + this.resourceRoleService = resourceRoleService; this.permissionMapper = permissionMapper; } @@ -52,8 +52,8 @@ protected List listPermittedPermissions(Principal principal) { * find all user-related resource role, and implies with permissions respectively */ List resourceRoles = - repository.findByUserId(odcUser.getId()).stream() - .filter(Objects::nonNull).collect(Collectors.toList()); + resourceRoleService.listByUserId(odcUser.getId()).stream() + .filter(Objects::nonNull).map(ResourceRoleService::toEntity).collect(Collectors.toList()); if (resourceRoles.isEmpty()) { return Collections.emptyList(); } diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/iam/model/UserResourceRole.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/iam/model/UserResourceRole.java index 83999e7539..d1ee19428c 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/iam/model/UserResourceRole.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/iam/model/UserResourceRole.java @@ -44,6 +44,8 @@ public class UserResourceRole implements PermissionConfiguration { private ResourceRoleName resourceRole; + private Long resourceRoleId; + private boolean derivedFromGlobalProjectRole = false; public boolean isProjectMember() { diff --git a/server/odc-service/src/main/java/com/oceanbase/odc/service/iam/util/GlobalResourceRoleUtil.java b/server/odc-service/src/main/java/com/oceanbase/odc/service/iam/util/GlobalResourceRoleUtil.java index 22f955aeca..77b82b9f52 100644 --- a/server/odc-service/src/main/java/com/oceanbase/odc/service/iam/util/GlobalResourceRoleUtil.java +++ b/server/odc-service/src/main/java/com/oceanbase/odc/service/iam/util/GlobalResourceRoleUtil.java @@ -17,6 +17,8 @@ import java.util.HashMap; import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; import com.oceanbase.odc.core.shared.constant.ResourceRoleName; @@ -52,4 +54,8 @@ public static String getGlobalRoleName(ResourceRoleName resourceRoleName) { return resourceRoleName2GlobalRoleName.get(resourceRoleName); } + public static Set getGlobalRoleName(Set resourceRoleNames) { + return resourceRoleNames.stream().map(resourceRoleName2GlobalRoleName::get).collect(Collectors.toSet()); + } + }