diff --git a/CHANGES.md b/CHANGES.md index f5d8f57a0d5..064fa0b07fb 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -19,5 +19,6 @@ Apollo 2.5.0 * [Security: Hide password when registering or modifying users](https://github.com/apolloconfig/apollo/pull/5414) * [Fix: the logical judgment for configuration addition, deletion, and modification.](https://github.com/apolloconfig/apollo/pull/5432) * [Feature support incremental configuration synchronization client](https://github.com/apolloconfig/apollo/pull/5288) +* [optimize: Implement unified permission verification logic and Optimize the implementation of permission verification](https://github.com/apolloconfig/apollo/pull/5456) ------------------ All issues and pull requests are [here](https://github.com/apolloconfig/apollo/milestone/16?closed=1) diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/auth/ConsumerPermissionValidator.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/auth/ConsumerPermissionValidator.java index 71d5fe915a9..8d33a500955 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/auth/ConsumerPermissionValidator.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/auth/ConsumerPermissionValidator.java @@ -21,13 +21,17 @@ import com.ctrip.framework.apollo.common.entity.AppNamespace; import com.ctrip.framework.apollo.openapi.service.ConsumerRolePermissionService; import com.ctrip.framework.apollo.openapi.util.ConsumerAuthUtil; +import com.ctrip.framework.apollo.portal.component.AbstractPermissionValidator; import com.ctrip.framework.apollo.portal.component.PermissionValidator; import com.ctrip.framework.apollo.portal.constant.PermissionType; -import com.ctrip.framework.apollo.portal.util.RoleUtils; +import com.ctrip.framework.apollo.portal.entity.po.Permission; import org.springframework.stereotype.Component; +import java.util.List; + @Component("consumerPermissionValidator") -public class ConsumerPermissionValidator implements PermissionValidator { +public class ConsumerPermissionValidator extends AbstractPermissionValidator implements + PermissionValidator { private final ConsumerRolePermissionService permissionService; private final ConsumerAuthUtil consumerAuthUtil; @@ -39,41 +43,19 @@ public ConsumerPermissionValidator(final ConsumerRolePermissionService permissio } @Override - public boolean hasModifyNamespacePermission(String appId, String env, String clusterName, - String namespaceName) { - if (hasCreateNamespacePermission(appId)) { + public boolean hasModifyNamespacePermission(String appId, String env, String clusterName, String namespaceName) { + if (hasCreateNamespacePermission(appId)){ return true; } - return permissionService.consumerHasPermission(consumerAuthUtil.retrieveConsumerIdFromCtx(), - PermissionType.MODIFY_NAMESPACE, RoleUtils.buildNamespaceTargetId(appId, namespaceName)) - || permissionService.consumerHasPermission(consumerAuthUtil.retrieveConsumerIdFromCtx(), - PermissionType.MODIFY_NAMESPACE, - RoleUtils.buildNamespaceTargetId(appId, namespaceName, env)); + return super.hasModifyNamespacePermission(appId, env, clusterName, namespaceName); } @Override - public boolean hasReleaseNamespacePermission(String appId, String env, String clusterName, - String namespaceName) { - if (hasCreateNamespacePermission(appId)) { + public boolean hasReleaseNamespacePermission(String appId, String env, String clusterName, String namespaceName) { + if (hasCreateNamespacePermission(appId)){ return true; } - return permissionService.consumerHasPermission(consumerAuthUtil.retrieveConsumerIdFromCtx(), - PermissionType.RELEASE_NAMESPACE, RoleUtils.buildNamespaceTargetId(appId, namespaceName)) - || permissionService.consumerHasPermission(consumerAuthUtil.retrieveConsumerIdFromCtx(), - PermissionType.RELEASE_NAMESPACE, - RoleUtils.buildNamespaceTargetId(appId, namespaceName, env)); - } - - @Override - public boolean hasAssignRolePermission(String appId) { - return permissionService.consumerHasPermission(consumerAuthUtil.retrieveConsumerIdFromCtx(), - PermissionType.ASSIGN_ROLE, appId); - } - - @Override - public boolean hasCreateNamespacePermission(String appId) { - return permissionService.consumerHasPermission(consumerAuthUtil.retrieveConsumerIdFromCtx(), - PermissionType.CREATE_NAMESPACE, appId); + return super.hasReleaseNamespacePermission(appId, env, clusterName, namespaceName); } @Override @@ -81,12 +63,6 @@ public boolean hasCreateAppNamespacePermission(String appId, AppNamespace appNam throw new UnsupportedOperationException("Not supported operation"); } - @Override - public boolean hasCreateClusterPermission(String appId) { - return permissionService.consumerHasPermission(consumerAuthUtil.retrieveConsumerIdFromCtx(), - PermissionType.CREATE_CLUSTER, appId); - } - @Override public boolean isSuperAdmin() { // openapi shouldn't be @@ -105,8 +81,22 @@ public boolean hasCreateApplicationPermission() { return permissionService.consumerHasPermission(consumerId, PermissionType.CREATE_APPLICATION, SYSTEM_PERMISSION_TARGET_ID); } + @Override + public boolean hasCreateApplicationPermission(String userId) { + return false; + } + @Override public boolean hasManageAppMasterPermission(String appId) { throw new UnsupportedOperationException("Not supported operation"); } + + @Override + protected boolean hasPermissions(List requiredPerms) { + if (requiredPerms == null || requiredPerms.isEmpty()) { + return false; + } + long consumerId = consumerAuthUtil.retrieveConsumerIdFromCtx(); + return permissionService.hasAnyPermission(consumerId, requiredPerms); + } } diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/service/ConsumerRolePermissionService.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/service/ConsumerRolePermissionService.java index e39321230e2..534752a1105 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/service/ConsumerRolePermissionService.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/service/ConsumerRolePermissionService.java @@ -22,6 +22,7 @@ import com.ctrip.framework.apollo.portal.entity.po.RolePermission; import com.ctrip.framework.apollo.portal.repository.PermissionRepository; import com.ctrip.framework.apollo.portal.repository.RolePermissionRepository; +import com.google.common.collect.Sets; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; @@ -77,4 +78,19 @@ public boolean consumerHasPermission(long consumerId, String permissionType, Str return false; } + + public boolean hasAnyPermission(long consumerId, List permissions) { + if (CollectionUtils.isEmpty(permissions)) { + return false; + } + List consumerPermissions = permissionRepository.findConsumerPermissions(consumerId); + + if (CollectionUtils.isEmpty(consumerPermissions)) { + return false; + } + + Set userPermissionSet = Sets.newHashSet(consumerPermissions); + + return permissions.stream().anyMatch(userPermissionSet::contains); + } } diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/util/ConsumerAuthUtil.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/util/ConsumerAuthUtil.java index 30009304366..e9a1e375fed 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/util/ConsumerAuthUtil.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/util/ConsumerAuthUtil.java @@ -29,7 +29,7 @@ */ @Service public class ConsumerAuthUtil { - static final String CONSUMER_ID = "ApolloConsumerId"; + public static final String CONSUMER_ID = "ApolloConsumerId"; private final ConsumerService consumerService; public ConsumerAuthUtil(final ConsumerService consumerService) { @@ -67,4 +67,4 @@ public long retrieveConsumerIdFromCtx() { HttpServletRequest request = attributes.getRequest(); return retrieveConsumerId(request); } -} +} \ No newline at end of file diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/AppController.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/AppController.java index e8eab8d78f5..e80f0936812 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/AppController.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/AppController.java @@ -65,7 +65,7 @@ public AppController( * @see com.ctrip.framework.apollo.portal.controller.AppController#create(AppModel) */ @Transactional - @PreAuthorize(value = "@consumerPermissionValidator.hasCreateApplicationPermission()") + @PreAuthorize(value = "@unifiedPermissionValidator.hasCreateApplicationPermission()") @Override public ResponseEntity createApp(OpenCreateAppDTO req) { if (null == req.getApp()) { @@ -126,7 +126,7 @@ public ResponseEntity getApp(String appId) { * update app (new added) */ @Override - @PreAuthorize(value = "@consumerPermissionValidator.isAppAdmin(#appId)") + @PreAuthorize(value = "@unifiedPermissionValidator.isAppAdmin(#appId)") @ApolloAuditLog(type = OpType.UPDATE, name = "App.update") public ResponseEntity updateApp(String appId, String operator, OpenAppDTO dto) { if (!Objects.equals(appId, dto.getAppId())) { @@ -156,7 +156,7 @@ public ResponseEntity> getAppsBySelf(Integer page, Integer size * POST /openapi/v1/apps/envs/{env} */ @Override - @PreAuthorize(value = "@consumerPermissionValidator.hasCreateApplicationPermission()") + @PreAuthorize(value = "@unifiedPermissionValidator.hasCreateApplicationPermission()") @ApolloAuditLog(type = OpType.CREATE, name = "App.create.forEnv") public ResponseEntity createAppInEnv(String env, String operator, OpenAppDTO app) { if (userService.findByUserId(operator) == null) { @@ -171,7 +171,7 @@ public ResponseEntity createAppInEnv(String env, String operator, OpenAp * Delete App (new added) */ @Override - @PreAuthorize(value = "@consumerPermissionValidator.isAppAdmin(#appId)") + @PreAuthorize(value = "@unifiedPermissionValidator.isAppAdmin(#appId)") @ApolloAuditLog(type = OpType.DELETE, name = "App.delete") public ResponseEntity deleteApp(String appId, String operator) { if (userService.findByUserId(operator) == null) { diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/ClusterController.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/ClusterController.java index 6e06c9301de..c913284236b 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/ClusterController.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/ClusterController.java @@ -49,7 +49,7 @@ public ResponseEntity getCluster(String appId, String clusterNam return ResponseEntity.ok(this.clusterOpenApiService.getCluster(appId, env, clusterName)); } - @PreAuthorize(value = "@consumerPermissionValidator.hasCreateClusterPermission(#appId)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasCreateClusterPermission(#appId)") @Override public ResponseEntity createCluster(String appId, String env, OpenClusterDTO cluster) { @@ -78,7 +78,7 @@ public ResponseEntity createCluster(String appId, String env, Op /** * Delete Clusters */ - @PreAuthorize(value = "@consumerPermissionValidator.isAppAdmin(#appId)") + @PreAuthorize(value = "@unifiedPermissionValidator.isAppAdmin(#appId)") @ApolloAuditLog(type = OpType.DELETE, name = "Cluster.delete") @Override public ResponseEntity deleteCluster(String env, String appId, String clusterName, String operator) { diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/ItemController.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/ItemController.java index c319b49d818..74c9ddaf969 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/ItemController.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/ItemController.java @@ -78,7 +78,7 @@ public OpenItemDTO getItemByEncodedKey(@PathVariable String appId, @PathVariable new String(Base64.getDecoder().decode(key.getBytes(StandardCharsets.UTF_8)))); } - @PreAuthorize(value = "@consumerPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)") @PostMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/items") public OpenItemDTO createItem(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName, @PathVariable String namespaceName, @@ -101,7 +101,7 @@ public OpenItemDTO createItem(@PathVariable String appId, @PathVariable String e return this.itemOpenApiService.createItem(appId, env, clusterName, namespaceName, item); } - @PreAuthorize(value = "@consumerPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)") @PutMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/items/{key:.+}") public void updateItem(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName, @PathVariable String namespaceName, @@ -135,7 +135,7 @@ public void updateItem(@PathVariable String appId, @PathVariable String env, } } - @PreAuthorize(value = "@consumerPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)") @PutMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/encodedItems/{key:.+}") public void updateItemByEncodedKey(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName, @PathVariable String namespaceName, @@ -146,7 +146,7 @@ public void updateItemByEncodedKey(@PathVariable String appId, @PathVariable Str createIfNotExists); } - @PreAuthorize(value = "@consumerPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)") @DeleteMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/items/{key:.+}") public void deleteItem(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName, @PathVariable String namespaceName, @@ -164,7 +164,7 @@ public void deleteItem(@PathVariable String appId, @PathVariable String env, this.itemOpenApiService.removeItem(appId, env, clusterName, namespaceName, key, operator); } - @PreAuthorize(value = "@consumerPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)") @DeleteMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/encodedItems/{key:.+}") public void deleteItemByEncodedKey(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName, @PathVariable String namespaceName, diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/NamespaceBranchController.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/NamespaceBranchController.java index b22111e7b4d..cb911037126 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/NamespaceBranchController.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/NamespaceBranchController.java @@ -21,9 +21,9 @@ import com.ctrip.framework.apollo.common.exception.BadRequestException; import com.ctrip.framework.apollo.common.utils.BeanUtils; import com.ctrip.framework.apollo.common.utils.RequestPrecondition; +import com.ctrip.framework.apollo.portal.component.UnifiedPermissionValidator; import com.ctrip.framework.apollo.portal.environment.Env; import com.ctrip.framework.apollo.core.utils.StringUtils; -import com.ctrip.framework.apollo.openapi.auth.ConsumerPermissionValidator; import com.ctrip.framework.apollo.openapi.dto.OpenGrayReleaseRuleDTO; import com.ctrip.framework.apollo.openapi.dto.OpenNamespaceDTO; import com.ctrip.framework.apollo.openapi.util.OpenApiBeanUtils; @@ -43,23 +43,21 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import javax.servlet.http.HttpServletRequest; - @RestController("openapiNamespaceBranchController") @RequestMapping("/openapi/v1/envs/{env}") public class NamespaceBranchController { - private final ConsumerPermissionValidator consumerPermissionValidator; + private final UnifiedPermissionValidator unifiedPermissionValidator; private final ReleaseService releaseService; private final NamespaceBranchService namespaceBranchService; private final UserService userService; public NamespaceBranchController( - final ConsumerPermissionValidator consumerPermissionValidator, + final UnifiedPermissionValidator unifiedPermissionValidator, final ReleaseService releaseService, final NamespaceBranchService namespaceBranchService, final UserService userService) { - this.consumerPermissionValidator = consumerPermissionValidator; + this.unifiedPermissionValidator = unifiedPermissionValidator; this.releaseService = releaseService; this.namespaceBranchService = namespaceBranchService; this.userService = userService; @@ -77,7 +75,7 @@ public OpenNamespaceDTO findBranch(@PathVariable String appId, return OpenApiBeanUtils.transformFromNamespaceBO(namespaceBO); } - @PreAuthorize(value = "@consumerPermissionValidator.hasCreateNamespacePermission(#appId)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasCreateNamespacePermission(#appId)") @PostMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/branches") public OpenNamespaceDTO createBranch(@PathVariable String appId, @PathVariable String env, @@ -97,7 +95,7 @@ public OpenNamespaceDTO createBranch(@PathVariable String appId, return BeanUtils.transform(OpenNamespaceDTO.class, namespaceDTO); } - @PreAuthorize(value = "@consumerPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)") @DeleteMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/branches/{branchName}") public void deleteBranch(@PathVariable String appId, @PathVariable String env, @@ -111,8 +109,8 @@ public void deleteBranch(@PathVariable String appId, throw BadRequestException.userNotExists(operator); } - boolean canDelete = consumerPermissionValidator.hasReleaseNamespacePermission(appId, env, clusterName, namespaceName) || - (consumerPermissionValidator.hasModifyNamespacePermission(appId, env, clusterName, namespaceName) && + boolean canDelete = unifiedPermissionValidator.hasReleaseNamespacePermission(appId, env, clusterName, namespaceName) || + (unifiedPermissionValidator.hasModifyNamespacePermission(appId, env, clusterName, namespaceName) && releaseService.loadLatestRelease(appId, Env.valueOf(env), branchName, namespaceName) == null); if (!canDelete) { @@ -137,7 +135,7 @@ public OpenGrayReleaseRuleDTO getBranchGrayRules(@PathVariable String appId, @Pa return OpenApiBeanUtils.transformFromGrayReleaseRuleDTO(grayReleaseRuleDTO); } - @PreAuthorize(value = "@consumerPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)") @PutMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/branches/{branchName}/rules") public void updateBranchRules(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName, @PathVariable String namespaceName, diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/NamespaceController.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/NamespaceController.java index b1bcb92f21f..9007f0642c2 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/NamespaceController.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/NamespaceController.java @@ -50,7 +50,7 @@ public NamespaceController( this.namespaceOpenApiService = namespaceOpenApiService; } - @PreAuthorize(value = "@consumerPermissionValidator.hasCreateNamespacePermission(#appId)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasCreateNamespacePermission(#appId)") @PostMapping(value = "/openapi/v1/apps/{appId}/appnamespaces") public OpenAppNamespaceDTO createNamespace(@PathVariable String appId, @RequestBody OpenAppNamespaceDTO appNamespaceDTO) { diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/ReleaseController.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/ReleaseController.java index d1f6fa57433..4252782cc87 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/ReleaseController.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/ReleaseController.java @@ -21,9 +21,9 @@ import com.ctrip.framework.apollo.common.utils.BeanUtils; import com.ctrip.framework.apollo.common.utils.RequestPrecondition; import com.ctrip.framework.apollo.openapi.api.ReleaseOpenApiService; +import com.ctrip.framework.apollo.portal.component.UnifiedPermissionValidator; import com.ctrip.framework.apollo.portal.environment.Env; import com.ctrip.framework.apollo.core.utils.StringUtils; -import com.ctrip.framework.apollo.openapi.auth.ConsumerPermissionValidator; import com.ctrip.framework.apollo.openapi.dto.NamespaceGrayDelReleaseDTO; import com.ctrip.framework.apollo.openapi.dto.NamespaceReleaseDTO; import com.ctrip.framework.apollo.openapi.dto.OpenReleaseDTO; @@ -54,26 +54,26 @@ public class ReleaseController { private final ReleaseService releaseService; private final UserService userService; private final NamespaceBranchService namespaceBranchService; - private final ConsumerPermissionValidator consumerPermissionValidator; private final ReleaseOpenApiService releaseOpenApiService; private final ApplicationEventPublisher publisher; + private final UnifiedPermissionValidator unifiedPermissionValidator; public ReleaseController( final ReleaseService releaseService, final UserService userService, final NamespaceBranchService namespaceBranchService, - final ConsumerPermissionValidator consumerPermissionValidator, + final UnifiedPermissionValidator unifiedPermissionValidator, ReleaseOpenApiService releaseOpenApiService, ApplicationEventPublisher publisher) { this.releaseService = releaseService; this.userService = userService; this.namespaceBranchService = namespaceBranchService; - this.consumerPermissionValidator = consumerPermissionValidator; + this.unifiedPermissionValidator = unifiedPermissionValidator; this.releaseOpenApiService = releaseOpenApiService; this.publisher = publisher; } - @PreAuthorize(value = "@consumerPermissionValidator.hasReleaseNamespacePermission(#appId, #env, #clusterName, #namespaceName)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasReleaseNamespacePermission(#appId, #env, #clusterName, #namespaceName)") @PostMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases") public OpenReleaseDTO createRelease(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName, @@ -109,7 +109,7 @@ public OpenReleaseDTO loadLatestActiveRelease(@PathVariable String appId, @PathV return this.releaseOpenApiService.getLatestActiveRelease(appId, env, clusterName, namespaceName); } - @PreAuthorize(value = "@consumerPermissionValidator.hasReleaseNamespacePermission(#appId, #env, #clusterName, #namespaceName)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasReleaseNamespacePermission(#appId, #env, #clusterName, #namespaceName)") @PostMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/branches/{branchName}/merge") public OpenReleaseDTO merge(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName, @PathVariable String namespaceName, @@ -136,7 +136,7 @@ public OpenReleaseDTO merge(@PathVariable String appId, @PathVariable String env return OpenApiBeanUtils.transformFromReleaseDTO(mergedRelease); } - @PreAuthorize(value = "@consumerPermissionValidator.hasReleaseNamespacePermission(#appId, #env, #clusterName, #namespaceName)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasReleaseNamespacePermission(#appId, #env, #clusterName, #namespaceName)") @PostMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/branches/{branchName}/releases") public OpenReleaseDTO createGrayRelease(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName, @PathVariable String namespaceName, @@ -166,7 +166,7 @@ public OpenReleaseDTO createGrayRelease(@PathVariable String appId, @PathVariabl return OpenApiBeanUtils.transformFromReleaseDTO(releaseDTO); } - @PreAuthorize(value = "@consumerPermissionValidator.hasReleaseNamespacePermission(#appId, #env, #clusterName, #namespaceName)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasReleaseNamespacePermission(#appId, #env, #clusterName, #namespaceName)") @PostMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/branches/{branchName}/gray-del-releases") public OpenReleaseDTO createGrayDelRelease(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName, @PathVariable String namespaceName, @@ -208,7 +208,7 @@ public void rollback(@PathVariable String env, throw new BadRequestException("release not found"); } - if (!consumerPermissionValidator.hasReleaseNamespacePermission(release.getAppId(), env, release.getClusterName(), release.getNamespaceName())) { + if (!unifiedPermissionValidator.hasReleaseNamespacePermission(release.getAppId(), env, release.getClusterName(), release.getNamespaceName())) { throw new AccessDeniedException("Forbidden operation. you don't have release permission"); } diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/audit/ApolloAuditLogQueryApiPortalPreAuthorizer.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/audit/ApolloAuditLogQueryApiPortalPreAuthorizer.java index f39aaf4561e..288fdc6fef5 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/audit/ApolloAuditLogQueryApiPortalPreAuthorizer.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/audit/ApolloAuditLogQueryApiPortalPreAuthorizer.java @@ -17,7 +17,7 @@ package com.ctrip.framework.apollo.portal.audit; import com.ctrip.framework.apollo.audit.spi.ApolloAuditLogQueryApiPreAuthorizer; -import com.ctrip.framework.apollo.portal.component.UserPermissionValidator; +import com.ctrip.framework.apollo.portal.component.UnifiedPermissionValidator; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Component; @@ -25,14 +25,14 @@ @ConditionalOnProperty(prefix = "apollo.audit.log", name = "enabled", havingValue = "true") public class ApolloAuditLogQueryApiPortalPreAuthorizer implements ApolloAuditLogQueryApiPreAuthorizer { - private final UserPermissionValidator userPermissionValidator; + private final UnifiedPermissionValidator unifiedPermissionValidator; - public ApolloAuditLogQueryApiPortalPreAuthorizer(UserPermissionValidator userPermissionValidator) { - this.userPermissionValidator = userPermissionValidator; + public ApolloAuditLogQueryApiPortalPreAuthorizer(UnifiedPermissionValidator unifiedPermissionValidator) { + this.unifiedPermissionValidator = unifiedPermissionValidator; } @Override public boolean hasQueryPermission() { - return userPermissionValidator.isSuperAdmin(); + return unifiedPermissionValidator.isSuperAdmin(); } } diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/component/AbstractPermissionValidator.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/component/AbstractPermissionValidator.java new file mode 100644 index 00000000000..47d428c7cfe --- /dev/null +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/component/AbstractPermissionValidator.java @@ -0,0 +1,100 @@ +/* + * Copyright 2025 Apollo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package com.ctrip.framework.apollo.portal.component; + +import com.ctrip.framework.apollo.common.entity.AppNamespace; +import com.ctrip.framework.apollo.portal.constant.PermissionType; +import com.ctrip.framework.apollo.portal.entity.po.Permission; +import com.ctrip.framework.apollo.portal.util.RoleUtils; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public abstract class AbstractPermissionValidator implements PermissionValidator { + + @Override + public boolean hasModifyNamespacePermission(String appId, String env, String clusterName, + String namespaceName) { + List requiredPermissions = Arrays.asList( + new Permission(PermissionType.MODIFY_NAMESPACE, + RoleUtils.buildNamespaceTargetId(appId, namespaceName)), + new Permission(PermissionType.MODIFY_NAMESPACE, + RoleUtils.buildNamespaceTargetId(appId, namespaceName, env)), + new Permission(PermissionType.MODIFY_NAMESPACES_IN_CLUSTER, + RoleUtils.buildClusterTargetId(appId, env, clusterName)) + ); + return hasPermissions(requiredPermissions); + } + + @Override + public boolean hasReleaseNamespacePermission(String appId, String env, String clusterName, + String namespaceName) { + List requiredPermissions = Arrays.asList( + new Permission(PermissionType.RELEASE_NAMESPACE, + RoleUtils.buildNamespaceTargetId(appId, namespaceName)), + new Permission(PermissionType.RELEASE_NAMESPACE, + RoleUtils.buildNamespaceTargetId(appId, namespaceName, env)), + new Permission(PermissionType.RELEASE_NAMESPACES_IN_CLUSTER, + RoleUtils.buildClusterTargetId(appId, env, clusterName)) + ); + return hasPermissions(requiredPermissions); + } + + @Override + public boolean hasAssignRolePermission(String appId) { + List requiredPermissions = Collections.singletonList( + new Permission(PermissionType.ASSIGN_ROLE, appId) + ); + return hasPermissions(requiredPermissions); + } + + @Override + public boolean hasCreateNamespacePermission(String appId) { + List requiredPermissions = Collections.singletonList( + new Permission(PermissionType.CREATE_NAMESPACE, appId) + ); + return hasPermissions(requiredPermissions); + } + + @Override + public abstract boolean hasCreateAppNamespacePermission(String appId, AppNamespace appNamespace); + + @Override + public boolean hasCreateClusterPermission(String appId) { + List requiredPermissions = Collections.singletonList( + new Permission(PermissionType.CREATE_CLUSTER, appId) + ); + return hasPermissions(requiredPermissions); + } + + @Override + public abstract boolean isSuperAdmin(); + + @Override + public boolean shouldHideConfigToCurrentUser(String appId, String env, String clusterName, + String namespaceName) { + return false; + } + + @Override + public abstract boolean hasCreateApplicationPermission(); + + @Override + public abstract boolean hasManageAppMasterPermission(String appId); + + protected abstract boolean hasPermissions(List requiredPerms); +} diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/component/PermissionValidator.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/component/PermissionValidator.java index 936bf91a7fc..874562a0376 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/component/PermissionValidator.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/component/PermissionValidator.java @@ -55,5 +55,7 @@ boolean shouldHideConfigToCurrentUser(String appId, String env, String clusterNa boolean hasCreateApplicationPermission(); + boolean hasCreateApplicationPermission(String userId); + boolean hasManageAppMasterPermission(String appId); } diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/component/UnifiedPermissionValidator.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/component/UnifiedPermissionValidator.java new file mode 100644 index 00000000000..666128aafea --- /dev/null +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/component/UnifiedPermissionValidator.java @@ -0,0 +1,110 @@ +/* + * Copyright 2025 Apollo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.ctrip.framework.apollo.portal.component; + +import com.ctrip.framework.apollo.common.entity.AppNamespace; +import com.ctrip.framework.apollo.openapi.auth.ConsumerPermissionValidator; +import com.ctrip.framework.apollo.portal.constant.UserIdentityConstants; +import org.springframework.stereotype.Component; + +@Component("unifiedPermissionValidator") +public class UnifiedPermissionValidator implements PermissionValidator { + + private final UserPermissionValidator userPermissionValidator; + private final ConsumerPermissionValidator consumerPermissionValidator; + + public UnifiedPermissionValidator(UserPermissionValidator userPermissionValidator, + ConsumerPermissionValidator consumerPermissionValidator) { + this.userPermissionValidator = userPermissionValidator; + this.consumerPermissionValidator = consumerPermissionValidator; + } + + private PermissionValidator getDelegate() { + String type = UserIdentityContextHolder.getAuthType(); + if (UserIdentityConstants.USER.equals(type)) { + return userPermissionValidator; + } + if (UserIdentityConstants.CONSUMER.equals(type)) { + return consumerPermissionValidator; + } + throw new IllegalStateException("Unknown authentication type"); + } + + @Override + public boolean hasModifyNamespacePermission(String appId, String env, String clusterName, + String namespaceName) { + return getDelegate().hasModifyNamespacePermission(appId, env, clusterName, namespaceName); + } + + @Override + public boolean hasReleaseNamespacePermission(String appId, String env, String clusterName, + String namespaceName) { + return getDelegate().hasReleaseNamespacePermission(appId, env, clusterName, namespaceName); + } + + @Override + public boolean hasAssignRolePermission(String appId) { + return getDelegate().hasAssignRolePermission(appId); + } + + @Override + public boolean hasCreateNamespacePermission(String appId) { + return getDelegate().hasCreateNamespacePermission(appId); + } + + @Override + public boolean hasCreateAppNamespacePermission(String appId, AppNamespace appNamespace) { + return getDelegate().hasCreateAppNamespacePermission(appId, appNamespace); + } + + @Override + public boolean hasCreateClusterPermission(String appId) { + return getDelegate().hasCreateClusterPermission(appId); + } + + @Override + public boolean isSuperAdmin() { + return getDelegate().isSuperAdmin(); + } + + @Override + public boolean shouldHideConfigToCurrentUser(String appId, String env, String clusterName, + String namespaceName) { + return getDelegate().shouldHideConfigToCurrentUser(appId, env, clusterName, namespaceName); + } + + @Override + public boolean hasCreateApplicationPermission() { + return getDelegate().hasCreateApplicationPermission(); + } + + @Override + public boolean hasCreateApplicationPermission(String userId) { + return getDelegate().hasCreateApplicationPermission(userId); + } + + @Override + public boolean hasDeleteNamespacePermission(String appId) { + return getDelegate().hasDeleteNamespacePermission(appId); + } + + @Override + public boolean hasManageAppMasterPermission(String appId) { + return getDelegate().hasManageAppMasterPermission(appId); + } +} diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/component/UserIdentityContextHolder.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/component/UserIdentityContextHolder.java new file mode 100644 index 00000000000..2eef7f253a4 --- /dev/null +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/component/UserIdentityContextHolder.java @@ -0,0 +1,47 @@ +/* + * Copyright 2025 Apollo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package com.ctrip.framework.apollo.portal.component; + +public final class UserIdentityContextHolder { + + private static final ThreadLocal AUTH_TYPE_HOLDER = new ThreadLocal<>(); + + private UserIdentityContextHolder() { + // Prevent instantiation + } + + /** + * Read authentication source identifier for current thread + */ + public static String getAuthType() { + return AUTH_TYPE_HOLDER.get(); + } + + /** + * Write authentication source identifier for current thread + */ + public static void setAuthType(String authType) { + AUTH_TYPE_HOLDER.set(authType); + } + + /** + * Clean up current thread variable to prevent memory leaks + */ + public static void clear() { + AUTH_TYPE_HOLDER.remove(); + } +} \ No newline at end of file diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/component/UserPermissionValidator.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/component/UserPermissionValidator.java index a284b1cb7cb..665172a419d 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/component/UserPermissionValidator.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/component/UserPermissionValidator.java @@ -18,16 +18,17 @@ import com.ctrip.framework.apollo.common.entity.AppNamespace; import com.ctrip.framework.apollo.portal.component.config.PortalConfig; -import com.ctrip.framework.apollo.portal.constant.PermissionType; +import com.ctrip.framework.apollo.portal.entity.po.Permission; import com.ctrip.framework.apollo.portal.service.AppNamespaceService; import com.ctrip.framework.apollo.portal.service.RolePermissionService; import com.ctrip.framework.apollo.portal.service.SystemRoleManagerService; import com.ctrip.framework.apollo.portal.spi.UserInfoHolder; -import com.ctrip.framework.apollo.portal.util.RoleUtils; +import java.util.List; import org.springframework.stereotype.Component; @Component("userPermissionValidator") -public class UserPermissionValidator implements PermissionValidator { +public class UserPermissionValidator extends AbstractPermissionValidator implements + PermissionValidator { private final UserInfoHolder userInfoHolder; private final RolePermissionService rolePermissionService; @@ -48,83 +49,6 @@ public UserPermissionValidator( this.systemRoleManagerService = systemRoleManagerService; } - private boolean hasModifyNamespacePermission(String appId, String namespaceName) { - return rolePermissionService.userHasPermission(userInfoHolder.getUser().getUserId(), - PermissionType.MODIFY_NAMESPACE, - RoleUtils.buildNamespaceTargetId(appId, namespaceName)); - } - - private boolean hasModifyNamespacePermission(String appId, String namespaceName, String env) { - return rolePermissionService.userHasPermission(userInfoHolder.getUser().getUserId(), - PermissionType.MODIFY_NAMESPACE, - RoleUtils.buildNamespaceTargetId(appId, namespaceName, env)); - } - - private boolean hasModifyNamespacesInClusterPermission(String appId, String env, String clusterName) { - return rolePermissionService.userHasPermission(userInfoHolder.getUser().getUserId(), - PermissionType.MODIFY_NAMESPACES_IN_CLUSTER, - RoleUtils.buildClusterTargetId(appId, env, clusterName)); - } - - @Override - public boolean hasModifyNamespacePermission(String appId, String env, String clusterName, String namespaceName) { - if (hasModifyNamespacePermission(appId, namespaceName)) { - return true; - } - if (hasModifyNamespacePermission(appId, namespaceName, env)) { - return true; - } - if (hasModifyNamespacesInClusterPermission(appId, env, clusterName)) { - return true; - } - return false; - } - - private boolean hasReleaseNamespacePermission(String appId, String namespaceName) { - return rolePermissionService.userHasPermission(userInfoHolder.getUser().getUserId(), - PermissionType.RELEASE_NAMESPACE, - RoleUtils.buildNamespaceTargetId(appId, namespaceName)); - } - - private boolean hasReleaseNamespacePermission(String appId, String namespaceName, String env) { - return rolePermissionService.userHasPermission(userInfoHolder.getUser().getUserId(), - PermissionType.RELEASE_NAMESPACE, - RoleUtils.buildNamespaceTargetId(appId, namespaceName, env)); - } - - private boolean hasReleaseNamespacesInClusterPermission(String appId, String env, String clusterName) { - return rolePermissionService.userHasPermission(userInfoHolder.getUser().getUserId(), - PermissionType.RELEASE_NAMESPACES_IN_CLUSTER, - RoleUtils.buildClusterTargetId(appId, env, clusterName)); - } - - @Override - public boolean hasReleaseNamespacePermission(String appId, String env, String clusterName, String namespaceName) { - if (hasReleaseNamespacePermission(appId, namespaceName)) { - return true; - } - if (hasReleaseNamespacePermission(appId, namespaceName, env)) { - return true; - } - if (hasReleaseNamespacesInClusterPermission(appId, env, clusterName)) { - return true; - } - return false; - } - - @Override - public boolean hasAssignRolePermission(String appId) { - return rolePermissionService.userHasPermission(userInfoHolder.getUser().getUserId(), - PermissionType.ASSIGN_ROLE, - appId); - } - - @Override - public boolean hasCreateNamespacePermission(String appId) { - return rolePermissionService.userHasPermission(userInfoHolder.getUser().getUserId(), - PermissionType.CREATE_NAMESPACE, - appId); - } @Override public boolean hasCreateAppNamespacePermission(String appId, AppNamespace appNamespace) { @@ -138,12 +62,6 @@ public boolean hasCreateAppNamespacePermission(String appId, AppNamespace appNam return isSuperAdmin(); } - @Override - public boolean hasCreateClusterPermission(String appId) { - return rolePermissionService.userHasPermission(userInfoHolder.getUser().getUserId(), - PermissionType.CREATE_CLUSTER, - appId); - } @Override public boolean isSuperAdmin() { @@ -170,9 +88,10 @@ public boolean shouldHideConfigToCurrentUser(String appId, String env, String cl @Override public boolean hasCreateApplicationPermission() { - return hasCreateApplicationPermission(userInfoHolder.getUser().getUserId()); + return systemRoleManagerService.hasCreateApplicationPermission(userInfoHolder.getUser().getUserId()); } + @Override public boolean hasCreateApplicationPermission(String userId) { return systemRoleManagerService.hasCreateApplicationPermission(userId); } @@ -185,4 +104,13 @@ public boolean hasManageAppMasterPermission(String appId) { systemRoleManagerService.hasManageAppMasterPermission(userInfoHolder.getUser().getUserId(), appId) ); } + + @Override + protected boolean hasPermissions(List requiredPerms) { + if (requiredPerms == null || requiredPerms.isEmpty()) { + return false; + } + String userId = userInfoHolder.getUser().getUserId(); + return rolePermissionService.hasAnyPermission(userId, requiredPerms); + } } diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/constant/UserIdentityConstants.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/constant/UserIdentityConstants.java new file mode 100644 index 00000000000..ed895cdd0c8 --- /dev/null +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/constant/UserIdentityConstants.java @@ -0,0 +1,27 @@ +/* + * Copyright 2025 Apollo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package com.ctrip.framework.apollo.portal.constant; + +public final class UserIdentityConstants { + + public static final String USER = "USER"; + public static final String CONSUMER = "CONSUMER"; + public static final String ANONYMOUS = "ANONYMOUS"; + + private UserIdentityConstants() { + } +} diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/AccessKeyController.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/AccessKeyController.java index 122ebdbb913..544e442fed4 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/AccessKeyController.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/AccessKeyController.java @@ -46,7 +46,7 @@ public AccessKeyController( this.accessKeyService = accessKeyService; } - @PreAuthorize(value = "@userPermissionValidator.isAppAdmin(#appId)") + @PreAuthorize(value = "@unifiedPermissionValidator.isAppAdmin(#appId)") @PostMapping(value = "/apps/{appId}/envs/{env}/accesskeys") @ApolloAuditLog(type = OpType.CREATE, name = "AccessKey.create") public AccessKeyDTO save(@PathVariable String appId, @PathVariable String env, @@ -57,14 +57,14 @@ public AccessKeyDTO save(@PathVariable String appId, @PathVariable String env, return accessKeyService.createAccessKey(Env.valueOf(env), accessKeyDTO); } - @PreAuthorize(value = "@userPermissionValidator.isAppAdmin(#appId)") + @PreAuthorize(value = "@unifiedPermissionValidator.isAppAdmin(#appId)") @GetMapping(value = "/apps/{appId}/envs/{env}/accesskeys") public List findByAppId(@PathVariable String appId, @PathVariable String env) { return accessKeyService.findByAppId(Env.valueOf(env), appId); } - @PreAuthorize(value = "@userPermissionValidator.isAppAdmin(#appId)") + @PreAuthorize(value = "@unifiedPermissionValidator.isAppAdmin(#appId)") @DeleteMapping(value = "/apps/{appId}/envs/{env}/accesskeys/{id}") @ApolloAuditLog(type = OpType.DELETE, name = "AccessKey.delete") public void delete(@PathVariable String appId, @@ -74,7 +74,7 @@ public void delete(@PathVariable String appId, accessKeyService.deleteAccessKey(Env.valueOf(env), appId, id, operator); } - @PreAuthorize(value = "@userPermissionValidator.isAppAdmin(#appId)") + @PreAuthorize(value = "@unifiedPermissionValidator.isAppAdmin(#appId)") @PutMapping(value = "/apps/{appId}/envs/{env}/accesskeys/{id}/enable") @ApolloAuditLog(type = OpType.UPDATE, name = "AccessKey.enable") public void enable(@PathVariable String appId, @@ -85,7 +85,7 @@ public void enable(@PathVariable String appId, accessKeyService.enable(Env.valueOf(env), appId, id, mode, operator); } - @PreAuthorize(value = "@userPermissionValidator.isAppAdmin(#appId)") + @PreAuthorize(value = "@unifiedPermissionValidator.isAppAdmin(#appId)") @PutMapping(value = "/apps/{appId}/envs/{env}/accesskeys/{id}/disable") @ApolloAuditLog(type = OpType.UPDATE, name = "AccessKey.disable") public void disable(@PathVariable String appId, diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/AppController.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/AppController.java index 40facd92dc1..058b479f642 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/AppController.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/AppController.java @@ -123,7 +123,7 @@ public List findAppsBySelf(Pageable page) { return appService.findByAppIds(appIds, page); } - @PreAuthorize(value = "@userPermissionValidator.hasCreateApplicationPermission()") + @PreAuthorize(value = "@unifiedPermissionValidator.hasCreateApplicationPermission()") @PostMapping @ApolloAuditLog(type = OpType.CREATE, name = "App.create") public App create(@Valid @RequestBody AppModel appModel) { @@ -132,7 +132,7 @@ public App create(@Valid @RequestBody AppModel appModel) { return appService.createAppAndAddRolePermission(app, appModel.getAdmins()); } - @PreAuthorize(value = "@userPermissionValidator.isAppAdmin(#appId)") + @PreAuthorize(value = "@unifiedPermissionValidator.isAppAdmin(#appId)") @PutMapping("/{appId:.+}") @ApolloAuditLog(type = OpType.UPDATE, name = "App.update") public void update(@PathVariable String appId, @Valid @RequestBody AppModel appModel) { @@ -185,7 +185,7 @@ public AppDTO load(@PathVariable String appId) { } - @PreAuthorize(value = "@userPermissionValidator.isSuperAdmin()") + @PreAuthorize(value = "@unifiedPermissionValidator.isSuperAdmin()") @DeleteMapping("/{appId:.+}") @ApolloAuditLog(type = OpType.RPC, name = "App.delete") public void deleteApp(@PathVariable String appId) { diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ClusterController.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ClusterController.java index 5a995709263..98ab208b2e1 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ClusterController.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ClusterController.java @@ -44,7 +44,7 @@ public ClusterController(final ClusterService clusterService, final UserInfoHold this.userInfoHolder = userInfoHolder; } - @PreAuthorize(value = "@userPermissionValidator.hasCreateClusterPermission(#appId)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasCreateClusterPermission(#appId)") @PostMapping(value = "apps/{appId}/envs/{env}/clusters") @ApolloAuditLog(type = OpType.CREATE, name = "Cluster.create") public ClusterDTO createCluster(@PathVariable String appId, @PathVariable String env, @@ -56,7 +56,7 @@ public ClusterDTO createCluster(@PathVariable String appId, @PathVariable String return clusterService.createCluster(Env.valueOf(env), cluster); } - @PreAuthorize(value = "@userPermissionValidator.isSuperAdmin()") + @PreAuthorize(value = "@unifiedPermissionValidator.isSuperAdmin()") @DeleteMapping(value = "apps/{appId}/envs/{env}/clusters/{clusterName:.+}") @ApolloAuditLog(type = OpType.DELETE, name = "Cluster.delete") public ResponseEntity deleteCluster(@PathVariable String appId, @PathVariable String env, diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/CommitController.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/CommitController.java index 445e357e9f6..fdd0548a94d 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/CommitController.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/CommitController.java @@ -18,8 +18,8 @@ import com.ctrip.framework.apollo.common.dto.CommitDTO; import com.ctrip.framework.apollo.core.utils.StringUtils; +import com.ctrip.framework.apollo.portal.component.UnifiedPermissionValidator; import com.ctrip.framework.apollo.portal.environment.Env; -import com.ctrip.framework.apollo.portal.component.UserPermissionValidator; import com.ctrip.framework.apollo.portal.service.CommitService; import javax.validation.Valid; import javax.validation.constraints.Positive; @@ -38,11 +38,11 @@ public class CommitController { private final CommitService commitService; - private final UserPermissionValidator userPermissionValidator; + private final UnifiedPermissionValidator unifiedPermissionValidator; - public CommitController(final CommitService commitService, final UserPermissionValidator userPermissionValidator) { + public CommitController(final CommitService commitService, final UnifiedPermissionValidator unifiedPermissionValidator) { this.commitService = commitService; - this.userPermissionValidator = userPermissionValidator; + this.unifiedPermissionValidator = unifiedPermissionValidator; } @GetMapping("/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/commits") @@ -51,7 +51,7 @@ public List find(@PathVariable String appId, @PathVariable String env @RequestParam(required = false) String key, @Valid @PositiveOrZero(message = "page should be positive or 0") @RequestParam(defaultValue = "0") int page, @Valid @Positive(message = "size should be positive number") @RequestParam(defaultValue = "10") int size) { - if (userPermissionValidator.shouldHideConfigToCurrentUser(appId, env, clusterName, namespaceName)) { + if (unifiedPermissionValidator.shouldHideConfigToCurrentUser(appId, env, clusterName, namespaceName)) { return Collections.emptyList(); } diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ConfigsExportController.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ConfigsExportController.java index 1b13947f9eb..d3730e57922 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ConfigsExportController.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ConfigsExportController.java @@ -78,7 +78,7 @@ public ConfigsExportController( * application.json * */ - @PreAuthorize(value = "!@userPermissionValidator.shouldHideConfigToCurrentUser(#appId, #env, #clusterName, #namespaceName)") + @PreAuthorize(value = "!@unifiedPermissionValidator.shouldHideConfigToCurrentUser(#appId, #env, #clusterName, #namespaceName)") @GetMapping("/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/items/export") public void exportItems(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName, @PathVariable String namespaceName, @@ -111,7 +111,7 @@ public void exportItems(@PathVariable String appId, @PathVariable String env, * Export all configs in a compressed file. Just export namespace which current exists read permission. The permission * check in service. */ - @PreAuthorize(value = "@userPermissionValidator.isSuperAdmin()") + @PreAuthorize(value = "@unifiedPermissionValidator.isSuperAdmin()") @GetMapping("/configs/export") public void exportAll(@RequestParam(value = "envs") String envs, HttpServletRequest request, HttpServletResponse response) throws IOException { diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ConfigsImportController.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ConfigsImportController.java index 71514d3a657..90d73ffe586 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ConfigsImportController.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ConfigsImportController.java @@ -61,7 +61,7 @@ public ConfigsImportController( * etc. * @throws IOException */ - @PreAuthorize(value = "@userPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)") @PostMapping("/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/items/import") public void importConfigFile(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName, @PathVariable String namespaceName, @@ -76,7 +76,7 @@ public void importConfigFile(@PathVariable String appId, @PathVariable String en configsImportService.forceImportNamespaceFromFile(Env.valueOf(env), standardFilename, file.getInputStream()); } - @PreAuthorize(value = "@userPermissionValidator.isSuperAdmin()") + @PreAuthorize(value = "@unifiedPermissionValidator.isSuperAdmin()") @PostMapping(value = "/configs/import", params = "conflictAction=cover") public void importConfigByZipWithCoverConflictNamespace(@RequestParam(value = "envs") String envs, @RequestParam("file") MultipartFile file) throws IOException { @@ -91,7 +91,7 @@ public void importConfigByZipWithCoverConflictNamespace(@RequestParam(value = "e } } - @PreAuthorize(value = "@userPermissionValidator.isSuperAdmin()") + @PreAuthorize(value = "@unifiedPermissionValidator.isSuperAdmin()") @PostMapping(value = "/configs/import", params = "conflictAction=ignore") public void importConfigByZipWithIgnoreConflictNamespace(@RequestParam(value = "envs") String envs, @RequestParam("file") MultipartFile file) throws IOException { diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ConsumerController.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ConsumerController.java index f1aba8bec82..2d231207d50 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ConsumerController.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ConsumerController.java @@ -61,7 +61,7 @@ private Consumer convertToConsumer(ConsumerCreateRequestVO requestVO) { } @Transactional - @PreAuthorize(value = "@userPermissionValidator.isSuperAdmin()") + @PreAuthorize(value = "@unifiedPermissionValidator.isSuperAdmin()") @PostMapping(value = "/consumers") public ConsumerInfo create( @RequestBody ConsumerCreateRequestVO requestVO, @@ -102,19 +102,19 @@ public ConsumerInfo create( return consumerService.getConsumerInfoByAppId(requestVO.getAppId()); } - @PreAuthorize(value = "@userPermissionValidator.isSuperAdmin()") + @PreAuthorize(value = "@unifiedPermissionValidator.isSuperAdmin()") @GetMapping(value = "/consumer-tokens/by-appId") public ConsumerToken getConsumerTokenByAppId(@RequestParam String appId) { return consumerService.getConsumerTokenByAppId(appId); } - @PreAuthorize(value = "@userPermissionValidator.isSuperAdmin()") + @PreAuthorize(value = "@unifiedPermissionValidator.isSuperAdmin()") @GetMapping(value = "/consumer/info/by-appId") public ConsumerInfo getConsumerInfoByAppId(@RequestParam String appId) { return consumerService.getConsumerInfoByAppId(appId); } - @PreAuthorize(value = "@userPermissionValidator.isSuperAdmin()") + @PreAuthorize(value = "@unifiedPermissionValidator.isSuperAdmin()") @PostMapping(value = "/consumers/{token}/assign-role") public List assignNamespaceRoleToConsumer( @PathVariable String token, @@ -163,13 +163,13 @@ public List assignNamespaceRoleToConsumer( } @GetMapping("/consumers") - @PreAuthorize(value = "@userPermissionValidator.isSuperAdmin()") + @PreAuthorize(value = "@unifiedPermissionValidator.isSuperAdmin()") public List getConsumerList(Pageable page) { return consumerService.findConsumerInfoList(page); } @DeleteMapping(value = "/consumers/by-appId") - @PreAuthorize(value = "@userPermissionValidator.isSuperAdmin()") + @PreAuthorize(value = "@unifiedPermissionValidator.isSuperAdmin()") public void deleteConsumers(@RequestParam String appId) { consumerService.deleteConsumer(appId); } diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/GlobalSearchController.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/GlobalSearchController.java index e4c850ef469..a499f758a0a 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/GlobalSearchController.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/GlobalSearchController.java @@ -39,7 +39,7 @@ public GlobalSearchController(final GlobalSearchService globalSearchService, fin this.portalConfig = portalConfig; } - @PreAuthorize(value = "@userPermissionValidator.isSuperAdmin()") + @PreAuthorize(value = "@unifiedPermissionValidator.isSuperAdmin()") @GetMapping("/global-search/item-info/by-key-or-value") public SearchResponseEntity> getItemInfoBySearch(@RequestParam(value = "key", required = false, defaultValue = "") String key, @RequestParam(value = "value", required = false , defaultValue = "") String value) { diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ItemController.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ItemController.java index 99eea228be3..196c90c2148 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ItemController.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ItemController.java @@ -21,9 +21,9 @@ import com.ctrip.framework.apollo.common.dto.NamespaceDTO; import com.ctrip.framework.apollo.common.exception.BadRequestException; import com.ctrip.framework.apollo.core.enums.ConfigFileFormat; +import com.ctrip.framework.apollo.portal.component.UnifiedPermissionValidator; import com.ctrip.framework.apollo.portal.environment.Env; import com.ctrip.framework.apollo.core.utils.StringUtils; -import com.ctrip.framework.apollo.portal.component.UserPermissionValidator; import com.ctrip.framework.apollo.portal.entity.model.NamespaceSyncModel; import com.ctrip.framework.apollo.portal.entity.model.NamespaceTextModel; import com.ctrip.framework.apollo.portal.entity.vo.ItemDiffs; @@ -63,17 +63,18 @@ public class ItemController { private final ItemService configService; private final NamespaceService namespaceService; private final UserInfoHolder userInfoHolder; - private final UserPermissionValidator userPermissionValidator; + private final UnifiedPermissionValidator unifiedPermissionValidator; public ItemController(final ItemService configService, final UserInfoHolder userInfoHolder, - final UserPermissionValidator userPermissionValidator, final NamespaceService namespaceService) { + final NamespaceService namespaceService, + final UnifiedPermissionValidator unifiedPermissionValidator) { this.configService = configService; this.userInfoHolder = userInfoHolder; - this.userPermissionValidator = userPermissionValidator; + this.unifiedPermissionValidator = unifiedPermissionValidator; this.namespaceService = namespaceService; } - @PreAuthorize(value = "@userPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)") @PutMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/items", consumes = { "application/json"}) public void modifyItemsByText(@PathVariable String appId, @PathVariable String env, @@ -87,7 +88,7 @@ public void modifyItemsByText(@PathVariable String appId, @PathVariable String e configService.updateConfigItemByText(model); } - @PreAuthorize(value = "@userPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)") @PostMapping("/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/item") public ItemDTO createItem(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName, @PathVariable String namespaceName, @@ -106,7 +107,7 @@ public ItemDTO createItem(@PathVariable String appId, @PathVariable String env, return configService.createItem(appId, Env.valueOf(env), clusterName, namespaceName, item); } - @PreAuthorize(value = "@userPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)") @PutMapping("/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/item") public void updateItem(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName, @PathVariable String namespaceName, @@ -120,7 +121,7 @@ public void updateItem(@PathVariable String appId, @PathVariable String env, } - @PreAuthorize(value = "@userPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)") @DeleteMapping("/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/items/{itemId}") public void deleteItem(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName, @PathVariable String namespaceName, @@ -142,7 +143,7 @@ public List findItems(@PathVariable String appId, @PathVariable String @PathVariable String clusterName, @PathVariable String namespaceName, @RequestParam(defaultValue = "lineNum") String orderBy) { - if (userPermissionValidator.shouldHideConfigToCurrentUser(appId, env, clusterName, namespaceName)) { + if (unifiedPermissionValidator.shouldHideConfigToCurrentUser(appId, env, clusterName, namespaceName)) { return Collections.emptyList(); } @@ -182,7 +183,7 @@ public List diff(@RequestBody NamespaceSyncModel model) { continue; } - if (userPermissionValidator + if (unifiedPermissionValidator .shouldHideConfigToCurrentUser(namespace.getAppId(), namespace.getEnv().getName(), namespace.getClusterName(), namespace.getNamespaceName())) { diff.setDiffs(new ItemChangeSets()); @@ -202,7 +203,7 @@ public ResponseEntity update(@PathVariable String appId, @PathVariable Str boolean hasPermission = true; for (NamespaceIdentifier namespaceIdentifier : model.getSyncToNamespaces()) { // once user has not one of the namespace's ModifyNamespace permission, then break the loop - hasPermission = userPermissionValidator.hasModifyNamespacePermission( + hasPermission = unifiedPermissionValidator.hasModifyNamespacePermission( namespaceIdentifier.getAppId(), namespaceIdentifier.getEnv().getName(), namespaceIdentifier.getClusterName(), @@ -220,7 +221,7 @@ public ResponseEntity update(@PathVariable String appId, @PathVariable Str throw new AccessDeniedException(String.format("You don't have the permission to modify namespace: %s", noPermissionNamespace)); } - @PreAuthorize(value = "@userPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)") @PostMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/syntax-check", consumes = { "application/json"}) public ResponseEntity syntaxCheckText(@PathVariable String appId, @PathVariable String env, @@ -231,7 +232,7 @@ public ResponseEntity syntaxCheckText(@PathVariable String appId, @PathVar return ResponseEntity.ok().build(); } - @PreAuthorize(value = "@userPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)") @PutMapping("/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/revoke-items") public void revokeItems(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName, @PathVariable String namespaceName) { diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/NamespaceBranchController.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/NamespaceBranchController.java index 2e8305af04f..9d022a4de25 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/NamespaceBranchController.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/NamespaceBranchController.java @@ -22,8 +22,8 @@ import com.ctrip.framework.apollo.common.dto.NamespaceDTO; import com.ctrip.framework.apollo.common.dto.ReleaseDTO; import com.ctrip.framework.apollo.common.exception.BadRequestException; +import com.ctrip.framework.apollo.portal.component.UnifiedPermissionValidator; import com.ctrip.framework.apollo.portal.environment.Env; -import com.ctrip.framework.apollo.portal.component.UserPermissionValidator; import com.ctrip.framework.apollo.portal.component.config.PortalConfig; import com.ctrip.framework.apollo.portal.entity.bo.NamespaceBO; import com.ctrip.framework.apollo.portal.entity.model.NamespaceReleaseModel; @@ -45,23 +45,23 @@ @RestController public class NamespaceBranchController { - private final UserPermissionValidator userPermissionValidator; + private final ReleaseService releaseService; private final NamespaceBranchService namespaceBranchService; private final ApplicationEventPublisher publisher; private final PortalConfig portalConfig; + private final UnifiedPermissionValidator unifiedPermissionValidator; public NamespaceBranchController( - final UserPermissionValidator userPermissionValidator, final ReleaseService releaseService, final NamespaceBranchService namespaceBranchService, final ApplicationEventPublisher publisher, - final PortalConfig portalConfig) { - this.userPermissionValidator = userPermissionValidator; + final PortalConfig portalConfig, UnifiedPermissionValidator unifiedPermissionValidator) { this.releaseService = releaseService; this.namespaceBranchService = namespaceBranchService; this.publisher = publisher; this.portalConfig = portalConfig; + this.unifiedPermissionValidator = unifiedPermissionValidator; } @GetMapping("/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/branches") @@ -71,14 +71,14 @@ public NamespaceBO findBranch(@PathVariable String appId, @PathVariable String namespaceName) { NamespaceBO namespaceBO = namespaceBranchService.findBranch(appId, Env.valueOf(env), clusterName, namespaceName); - if (namespaceBO != null && userPermissionValidator.shouldHideConfigToCurrentUser(appId, env, clusterName, namespaceName)) { + if (namespaceBO != null && unifiedPermissionValidator.shouldHideConfigToCurrentUser(appId, env, clusterName, namespaceName)) { namespaceBO.hideItems(); } return namespaceBO; } - @PreAuthorize(value = "@userPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)") @PostMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/branches") @ApolloAuditLog(type = OpType.CREATE, name = "NamespaceBranch.create") public NamespaceDTO createBranch(@PathVariable String appId, @@ -97,8 +97,8 @@ public void deleteBranch(@PathVariable String appId, @PathVariable String namespaceName, @PathVariable String branchName) { - boolean hasModifyPermission = userPermissionValidator.hasModifyNamespacePermission(appId, env, clusterName, namespaceName); - boolean hasReleasePermission = userPermissionValidator.hasReleaseNamespacePermission(appId, env, clusterName, namespaceName); + boolean hasModifyPermission = unifiedPermissionValidator.hasModifyNamespacePermission(appId, env, clusterName, namespaceName); + boolean hasReleasePermission = unifiedPermissionValidator.hasReleaseNamespacePermission(appId, env, clusterName, namespaceName); boolean canDelete = hasReleasePermission || (hasModifyPermission && releaseService.loadLatestRelease(appId, Env.valueOf(env), branchName, namespaceName) == null); @@ -116,7 +116,7 @@ public void deleteBranch(@PathVariable String appId, - @PreAuthorize(value = "@userPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasModifyNamespacePermission(#appId, #env, #clusterName, #namespaceName)") @PostMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/branches/{branchName}/merge") @ApolloAuditLog(type = OpType.UPDATE, name = "NamespaceBranch.merge") public ReleaseDTO merge(@PathVariable String appId, @PathVariable String env, @@ -156,7 +156,7 @@ public GrayReleaseRuleDTO getBranchGrayRules(@PathVariable String appId, @PathVa } - @PreAuthorize(value = "@userPermissionValidator.hasOperateNamespacePermission(#appId, #env, #clusterName, #namespaceName)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasOperateNamespacePermission(#appId, #env, #clusterName, #namespaceName)") @PutMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/branches/{branchName}/rules") @ApolloAuditLog(type = OpType.UPDATE, name = "NamespaceBranch.updateBranchRules") public void updateBranchRules(@PathVariable String appId, @PathVariable String env, diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/NamespaceController.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/NamespaceController.java index 86eb0ed1508..4ddd65fab14 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/NamespaceController.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/NamespaceController.java @@ -27,10 +27,10 @@ import com.ctrip.framework.apollo.common.utils.BeanUtils; import com.ctrip.framework.apollo.common.utils.InputValidator; import com.ctrip.framework.apollo.common.utils.RequestPrecondition; +import com.ctrip.framework.apollo.portal.component.UnifiedPermissionValidator; import com.ctrip.framework.apollo.portal.entity.vo.NamespaceUsage; import com.ctrip.framework.apollo.portal.environment.Env; import com.ctrip.framework.apollo.portal.api.AdminServiceAPI; -import com.ctrip.framework.apollo.portal.component.UserPermissionValidator; import com.ctrip.framework.apollo.portal.component.config.PortalConfig; import com.ctrip.framework.apollo.portal.entity.bo.NamespaceBO; import com.ctrip.framework.apollo.portal.entity.model.NamespaceCreationModel; @@ -76,7 +76,7 @@ public class NamespaceController { private final AppNamespaceService appNamespaceService; private final RoleInitializationService roleInitializationService; private final PortalConfig portalConfig; - private final UserPermissionValidator userPermissionValidator; + private final UnifiedPermissionValidator unifiedPermissionValidator; private final AdminServiceAPI.NamespaceAPI namespaceAPI; public NamespaceController( @@ -86,7 +86,7 @@ public NamespaceController( final AppNamespaceService appNamespaceService, final RoleInitializationService roleInitializationService, final PortalConfig portalConfig, - final UserPermissionValidator userPermissionValidator, + final UnifiedPermissionValidator unifiedPermissionValidator, final AdminServiceAPI.NamespaceAPI namespaceAPI) { this.publisher = publisher; this.userInfoHolder = userInfoHolder; @@ -94,7 +94,7 @@ public NamespaceController( this.appNamespaceService = appNamespaceService; this.roleInitializationService = roleInitializationService; this.portalConfig = portalConfig; - this.userPermissionValidator = userPermissionValidator; + this.unifiedPermissionValidator = unifiedPermissionValidator; this.namespaceAPI = namespaceAPI; } @@ -111,7 +111,7 @@ public List findNamespaces(@PathVariable String appId, @PathVariabl List namespaceBOs = namespaceService.findNamespaceBOs(appId, Env.valueOf(env), clusterName); for (NamespaceBO namespaceBO : namespaceBOs) { - if (userPermissionValidator.shouldHideConfigToCurrentUser(appId, env, clusterName, namespaceBO.getBaseInfo().getNamespaceName())) { + if (unifiedPermissionValidator.shouldHideConfigToCurrentUser(appId, env, clusterName, namespaceBO.getBaseInfo().getNamespaceName())) { namespaceBO.hideItems(); } } @@ -125,7 +125,7 @@ public NamespaceBO findNamespace(@PathVariable String appId, @PathVariable Strin NamespaceBO namespaceBO = namespaceService.loadNamespaceBO(appId, Env.valueOf(env), clusterName, namespaceName); - if (namespaceBO != null && userPermissionValidator.shouldHideConfigToCurrentUser(appId, env, clusterName, namespaceName)) { + if (namespaceBO != null && unifiedPermissionValidator.shouldHideConfigToCurrentUser(appId, env, clusterName, namespaceName)) { namespaceBO.hideItems(); } @@ -141,7 +141,7 @@ public NamespaceBO findPublicNamespaceForAssociatedNamespace(@PathVariable Strin return namespaceService.findPublicNamespaceForAssociatedNamespace(Env.valueOf(env), appId, clusterName, namespaceName); } - @PreAuthorize(value = "@userPermissionValidator.hasCreateNamespacePermission(#appId)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasCreateNamespacePermission(#appId)") @PostMapping("/apps/{appId}/namespaces") @ApolloAuditLog(type = OpType.CREATE, name = "Namespace.create") public ResponseEntity createNamespace(@PathVariable String appId, @@ -172,7 +172,7 @@ public ResponseEntity createNamespace(@PathVariable String appId, return ResponseEntity.ok().build(); } - @PreAuthorize(value = "@userPermissionValidator.hasDeleteNamespacePermission(#appId)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasDeleteNamespacePermission(#appId)") @DeleteMapping("/apps/{appId}/envs/{env}/clusters/{clusterName}/linked-namespaces/{namespaceName:.+}") @ApolloAuditLog(type = OpType.DELETE, name = "Namespace.deleteLinkedNamespace") public ResponseEntity deleteLinkedNamespace(@PathVariable String appId, @PathVariable String env, @@ -195,7 +195,7 @@ public List findNamespaceUsage(@PathVariable String appId, @Path return namespaceService.getNamespaceUsageByAppId(appId, namespaceName); } - @PreAuthorize(value = "@userPermissionValidator.hasDeleteNamespacePermission(#appId)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasDeleteNamespacePermission(#appId)") @DeleteMapping("/apps/{appId}/appnamespaces/{namespaceName:.+}") @ApolloAuditLog(type = OpType.DELETE, name = "AppNamespace.delete") public ResponseEntity deleteAppNamespace(@PathVariable String appId, @PathVariable String namespaceName) { @@ -218,7 +218,7 @@ public AppNamespaceDTO findAppNamespace(@PathVariable String appId, @PathVariabl return BeanUtils.transform(AppNamespaceDTO.class, appNamespace); } - @PreAuthorize(value = "@userPermissionValidator.hasCreateAppNamespacePermission(#appId, #appNamespace)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasCreateAppNamespacePermission(#appId, #appNamespace)") @PostMapping("/apps/{appId}/appnamespaces") @ApolloAuditLog(type = OpType.CREATE, name = "AppNamespace.create") public AppNamespace createAppNamespace(@PathVariable String appId, diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/PermissionController.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/PermissionController.java index 56f92a47b26..ec8bd718ea5 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/PermissionController.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/PermissionController.java @@ -20,7 +20,7 @@ import com.ctrip.framework.apollo.audit.annotation.OpType; import com.ctrip.framework.apollo.common.exception.BadRequestException; import com.ctrip.framework.apollo.common.utils.RequestPrecondition; -import com.ctrip.framework.apollo.portal.component.UserPermissionValidator; +import com.ctrip.framework.apollo.portal.component.UnifiedPermissionValidator; import com.ctrip.framework.apollo.portal.constant.PermissionType; import com.ctrip.framework.apollo.portal.constant.RoleType; import com.ctrip.framework.apollo.portal.entity.bo.UserInfo; @@ -57,21 +57,21 @@ public class PermissionController { private final UserService userService; private final RoleInitializationService roleInitializationService; private final SystemRoleManagerService systemRoleManagerService; - private final UserPermissionValidator userPermissionValidator; + private final UnifiedPermissionValidator unifiedPermissionValidator; public PermissionController( - final UserInfoHolder userInfoHolder, - final RolePermissionService rolePermissionService, - final UserService userService, - final RoleInitializationService roleInitializationService, - final SystemRoleManagerService systemRoleManagerService, - final UserPermissionValidator userPermissionValidator) { + final UserInfoHolder userInfoHolder, + final RolePermissionService rolePermissionService, + final UserService userService, + final RoleInitializationService roleInitializationService, + final SystemRoleManagerService systemRoleManagerService, + final UnifiedPermissionValidator unifiedPermissionValidator) { this.userInfoHolder = userInfoHolder; this.rolePermissionService = rolePermissionService; this.userService = userService; this.roleInitializationService = roleInitializationService; this.systemRoleManagerService = systemRoleManagerService; - this.userPermissionValidator = userPermissionValidator; + this.unifiedPermissionValidator = unifiedPermissionValidator; } @PostMapping("/apps/{appId}/initPermission") @@ -166,7 +166,7 @@ public NamespaceEnvRolesAssignedUsers getNamespaceEnvRoles(@PathVariable String return assignedUsers; } - @PreAuthorize(value = "@userPermissionValidator.hasAssignRolePermission(#appId)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasAssignRolePermission(#appId)") @PostMapping("/apps/{appId}/envs/{env}/namespaces/{namespaceName}/roles/{roleType}") @ApolloAuditLog(type = OpType.CREATE, name = "Auth.assignNamespaceEnvRoleToUser") public ResponseEntity assignNamespaceEnvRoleToUser(@PathVariable String appId, @PathVariable String env, @PathVariable String namespaceName, @@ -191,7 +191,7 @@ public ResponseEntity assignNamespaceEnvRoleToUser(@PathVariable String ap return ResponseEntity.ok().build(); } - @PreAuthorize(value = "@userPermissionValidator.hasAssignRolePermission(#appId)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasAssignRolePermission(#appId)") @DeleteMapping("/apps/{appId}/envs/{env}/namespaces/{namespaceName}/roles/{roleType}") @ApolloAuditLog(type = OpType.DELETE, name = "Auth.removeNamespaceEnvRoleFromUser") public ResponseEntity removeNamespaceEnvRoleFromUser(@PathVariable String appId, @PathVariable String env, @PathVariable String namespaceName, @@ -234,7 +234,7 @@ public ClusterNamespaceRolesAssignedUsers getClusterNamespaceRoles(@PathVariable return assignedUsers; } - @PreAuthorize(value = "@userPermissionValidator.hasAssignRolePermission(#appId)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasAssignRolePermission(#appId)") @PostMapping("/apps/{appId}/envs/{env}/clusters/{clusterName}/ns_roles/{roleType}") public ResponseEntity assignClusterNamespaceRoleToUser(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName, @PathVariable String roleType, @RequestBody String user) { @@ -258,7 +258,7 @@ public ResponseEntity assignClusterNamespaceRoleToUser(@PathVariable Strin return ResponseEntity.ok().build(); } - @PreAuthorize(value = "@userPermissionValidator.hasAssignRolePermission(#appId)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasAssignRolePermission(#appId)") @DeleteMapping("/apps/{appId}/envs/{env}/clusters/{clusterName}/ns_roles/{roleType}") public ResponseEntity removeClusterNamespaceRoleFromUser(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName, @PathVariable String roleType, @RequestParam String user) { @@ -294,7 +294,7 @@ public NamespaceRolesAssignedUsers getNamespaceRoles(@PathVariable String appId, return assignedUsers; } - @PreAuthorize(value = "@userPermissionValidator.hasAssignRolePermission(#appId)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasAssignRolePermission(#appId)") @PostMapping("/apps/{appId}/namespaces/{namespaceName}/roles/{roleType}") @ApolloAuditLog(type = OpType.CREATE, name = "Auth.assignNamespaceRoleToUser") public ResponseEntity assignNamespaceRoleToUser(@PathVariable String appId, @PathVariable String namespaceName, @@ -314,7 +314,7 @@ public ResponseEntity assignNamespaceRoleToUser(@PathVariable String appId return ResponseEntity.ok().build(); } - @PreAuthorize(value = "@userPermissionValidator.hasAssignRolePermission(#appId)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasAssignRolePermission(#appId)") @DeleteMapping("/apps/{appId}/namespaces/{namespaceName}/roles/{roleType}") @ApolloAuditLog(type = OpType.DELETE, name = "Auth.removeNamespaceRoleFromUser") public ResponseEntity removeNamespaceRoleFromUser(@PathVariable String appId, @PathVariable String namespaceName, @@ -340,7 +340,7 @@ public AppRolesAssignedUsers getAppRoles(@PathVariable String appId) { return users; } - @PreAuthorize(value = "@userPermissionValidator.hasManageAppMasterPermission(#appId)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasManageAppMasterPermission(#appId)") @PostMapping("/apps/{appId}/roles/{roleType}") @ApolloAuditLog(type = OpType.CREATE, name = "Auth.assignAppRoleToUser") public ResponseEntity assignAppRoleToUser(@PathVariable String appId, @PathVariable String roleType, @@ -360,7 +360,7 @@ public ResponseEntity assignAppRoleToUser(@PathVariable String appId, @Pat return ResponseEntity.ok().build(); } - @PreAuthorize(value = "@userPermissionValidator.hasManageAppMasterPermission(#appId)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasManageAppMasterPermission(#appId)") @DeleteMapping("/apps/{appId}/roles/{roleType}") @ApolloAuditLog(type = OpType.DELETE, name = "Auth.removeAppRoleFromUser") public ResponseEntity removeAppRoleFromUser(@PathVariable String appId, @PathVariable String roleType, @@ -381,7 +381,7 @@ private void checkUserExists(String userId) { } } - @PreAuthorize(value = "@userPermissionValidator.isSuperAdmin()") + @PreAuthorize(value = "@unifiedPermissionValidator.isSuperAdmin()") @PostMapping("/system/role/createApplication") @ApolloAuditLog(type = OpType.CREATE, name = "Auth.addCreateApplicationRoleToUser") public ResponseEntity addCreateApplicationRoleToUser(@RequestBody List userIds) { @@ -393,7 +393,7 @@ public ResponseEntity addCreateApplicationRoleToUser(@RequestBody List deleteCreateApplicationRoleFromUser(@PathVariable("userId") String userId) { @@ -405,7 +405,7 @@ public ResponseEntity deleteCreateApplicationRoleFromUser(@PathVariable("u return ResponseEntity.ok().build(); } - @PreAuthorize(value = "@userPermissionValidator.isSuperAdmin()") + @PreAuthorize(value = "@unifiedPermissionValidator.isSuperAdmin()") @GetMapping("/system/role/createApplication") public List getCreateApplicationRoleUsers() { return rolePermissionService.queryUsersWithRole(SystemRoleManagerService.CREATE_APPLICATION_ROLE_NAME) @@ -415,11 +415,11 @@ public List getCreateApplicationRoleUsers() { @GetMapping("/system/role/createApplication/{userId}") public JsonObject hasCreateApplicationPermission(@PathVariable String userId) { JsonObject rs = new JsonObject(); - rs.addProperty("hasCreateApplicationPermission", userPermissionValidator.hasCreateApplicationPermission(userId)); + rs.addProperty("hasCreateApplicationPermission", unifiedPermissionValidator.hasCreateApplicationPermission(userId)); return rs; } - @PreAuthorize(value = "@userPermissionValidator.isSuperAdmin()") + @PreAuthorize(value = "@unifiedPermissionValidator.isSuperAdmin()") @PostMapping("/apps/{appId}/system/master/{userId}") @ApolloAuditLog(type = OpType.CREATE, name = "Auth.addManageAppMasterRoleToUser") public ResponseEntity addManageAppMasterRoleToUser(@PathVariable String appId, @PathVariable String userId) { @@ -432,7 +432,7 @@ public ResponseEntity addManageAppMasterRoleToUser(@PathVariable String ap return ResponseEntity.ok().build(); } - @PreAuthorize(value = "@userPermissionValidator.isSuperAdmin()") + @PreAuthorize(value = "@unifiedPermissionValidator.isSuperAdmin()") @DeleteMapping("/apps/{appId}/system/master/{userId}") @ApolloAuditLog(type = OpType.DELETE, name = "Auth.forbidManageAppMaster") public ResponseEntity forbidManageAppMaster(@PathVariable String appId, @PathVariable String userId) { diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ReleaseController.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ReleaseController.java index cf382438aee..ca185a4f75b 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ReleaseController.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ReleaseController.java @@ -19,8 +19,8 @@ import com.ctrip.framework.apollo.common.dto.ReleaseDTO; import com.ctrip.framework.apollo.common.exception.BadRequestException; import com.ctrip.framework.apollo.common.exception.NotFoundException; +import com.ctrip.framework.apollo.portal.component.UnifiedPermissionValidator; import com.ctrip.framework.apollo.portal.environment.Env; -import com.ctrip.framework.apollo.portal.component.UserPermissionValidator; import com.ctrip.framework.apollo.portal.component.config.PortalConfig; import com.ctrip.framework.apollo.portal.entity.bo.ReleaseBO; import com.ctrip.framework.apollo.portal.entity.model.NamespaceReleaseModel; @@ -53,23 +53,23 @@ public class ReleaseController { private final ReleaseService releaseService; private final ApplicationEventPublisher publisher; private final PortalConfig portalConfig; - private final UserPermissionValidator userPermissionValidator; + private final UnifiedPermissionValidator unifiedPermissionValidator; private final UserInfoHolder userInfoHolder; public ReleaseController( final ReleaseService releaseService, final ApplicationEventPublisher publisher, final PortalConfig portalConfig, - final UserPermissionValidator userPermissionValidator, + final UnifiedPermissionValidator unifiedPermissionValidator, final UserInfoHolder userInfoHolder) { this.releaseService = releaseService; this.publisher = publisher; this.portalConfig = portalConfig; - this.userPermissionValidator = userPermissionValidator; + this.unifiedPermissionValidator = unifiedPermissionValidator; this.userInfoHolder = userInfoHolder; } - @PreAuthorize(value = "@userPermissionValidator.hasReleaseNamespacePermission(#appId, #env, #clusterName, #namespaceName)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasReleaseNamespacePermission(#appId, #env, #clusterName, #namespaceName)") @PostMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/releases") public ReleaseDTO createRelease(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName, @@ -98,7 +98,7 @@ public ReleaseDTO createRelease(@PathVariable String appId, return createdRelease; } - @PreAuthorize(value = "@userPermissionValidator.hasReleaseNamespacePermission(#appId, #env, #clusterName, #namespaceName)") + @PreAuthorize(value = "@unifiedPermissionValidator.hasReleaseNamespacePermission(#appId, #env, #clusterName, #namespaceName)") @PostMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/branches/{branchName}/releases") public ReleaseDTO createGrayRelease(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName, @@ -136,7 +136,7 @@ public ReleaseDTO get(@PathVariable String env, if (release == null) { throw NotFoundException.releaseNotFound(releaseId); } - if (userPermissionValidator.shouldHideConfigToCurrentUser(release.getAppId(), env, + if (unifiedPermissionValidator.shouldHideConfigToCurrentUser(release.getAppId(), env, release.getClusterName(), release.getNamespaceName())) { throw new AccessDeniedException("Access is denied"); } @@ -150,7 +150,7 @@ public List findAllReleases(@PathVariable String appId, @PathVariable String namespaceName, @Valid @PositiveOrZero(message = "page should be positive or 0") @RequestParam(defaultValue = "0") int page, @Valid @Positive(message = "size should be positive number") @RequestParam(defaultValue = "5") int size) { - if (userPermissionValidator.shouldHideConfigToCurrentUser(appId, env, clusterName, namespaceName)) { + if (unifiedPermissionValidator.shouldHideConfigToCurrentUser(appId, env, clusterName, namespaceName)) { return Collections.emptyList(); } @@ -165,7 +165,7 @@ public List findActiveReleases(@PathVariable String appId, @Valid @PositiveOrZero(message = "page should be positive or 0") @RequestParam(defaultValue = "0") int page, @Valid @Positive(message = "size should be positive number") @RequestParam(defaultValue = "5") int size) { - if (userPermissionValidator.shouldHideConfigToCurrentUser(appId, env, clusterName, namespaceName)) { + if (unifiedPermissionValidator.shouldHideConfigToCurrentUser(appId, env, clusterName, namespaceName)) { return Collections.emptyList(); } @@ -191,7 +191,7 @@ public void rollback(@PathVariable String env, throw NotFoundException.releaseNotFound(releaseId); } - if (!userPermissionValidator.hasReleaseNamespacePermission(release.getAppId(), env, release.getClusterName(), release.getNamespaceName())) { + if (!unifiedPermissionValidator.hasReleaseNamespacePermission(release.getAppId(), env, release.getClusterName(), release.getNamespaceName())) { throw new AccessDeniedException("Access is denied"); } diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ReleaseHistoryController.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ReleaseHistoryController.java index 2102c08a0fa..90cc57f0fc1 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ReleaseHistoryController.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ReleaseHistoryController.java @@ -17,8 +17,8 @@ package com.ctrip.framework.apollo.portal.controller; +import com.ctrip.framework.apollo.portal.component.UnifiedPermissionValidator; import com.ctrip.framework.apollo.portal.environment.Env; -import com.ctrip.framework.apollo.portal.component.UserPermissionValidator; import com.ctrip.framework.apollo.portal.entity.bo.ReleaseHistoryBO; import com.ctrip.framework.apollo.portal.service.ReleaseHistoryService; import org.springframework.web.bind.annotation.GetMapping; @@ -33,11 +33,11 @@ public class ReleaseHistoryController { private final ReleaseHistoryService releaseHistoryService; - private final UserPermissionValidator userPermissionValidator; + private final UnifiedPermissionValidator unifiedPermissionValidator; - public ReleaseHistoryController(final ReleaseHistoryService releaseHistoryService, final UserPermissionValidator userPermissionValidator) { + public ReleaseHistoryController(final ReleaseHistoryService releaseHistoryService, final UnifiedPermissionValidator unifiedPermissionValidator) { this.releaseHistoryService = releaseHistoryService; - this.userPermissionValidator = userPermissionValidator; + this.unifiedPermissionValidator = unifiedPermissionValidator; } @GetMapping("/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/releases/histories") @@ -48,7 +48,7 @@ public List findReleaseHistoriesByNamespace(@PathVariable Stri @RequestParam(value = "page", defaultValue = "0") int page, @RequestParam(value = "size", defaultValue = "10") int size) { - if (userPermissionValidator.shouldHideConfigToCurrentUser(appId, env, clusterName, namespaceName)) { + if (unifiedPermissionValidator.shouldHideConfigToCurrentUser(appId, env, clusterName, namespaceName)) { return Collections.emptyList(); } diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ServerConfigController.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ServerConfigController.java index ec32e9f3cdd..00d5796ba14 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ServerConfigController.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ServerConfigController.java @@ -42,27 +42,27 @@ public ServerConfigController(final ServerConfigService serverConfigService) { this.serverConfigService = serverConfigService; } - @PreAuthorize(value = "@userPermissionValidator.isSuperAdmin()") + @PreAuthorize(value = "@unifiedPermissionValidator.isSuperAdmin()") @PostMapping("/server/portal-db/config") @ApolloAuditLog(type = OpType.CREATE, name = "ServerConfig.createOrUpdatePortalDBConfig") public ServerConfig createOrUpdatePortalDBConfig(@Valid @RequestBody ServerConfig serverConfig) { return serverConfigService.createOrUpdatePortalDBConfig(serverConfig); } - @PreAuthorize(value = "@userPermissionValidator.isSuperAdmin()") + @PreAuthorize(value = "@unifiedPermissionValidator.isSuperAdmin()") @PostMapping("/server/envs/{env}/config-db/config") @ApolloAuditLog(type = OpType.CREATE, name = "ServerConfig.createOrUpdateConfigDBConfig") public ServerConfig createOrUpdateConfigDBConfig(@Valid @RequestBody ServerConfig serverConfig, @PathVariable String env) { return serverConfigService.createOrUpdateConfigDBConfig(Env.transformEnv(env), serverConfig); } - @PreAuthorize(value = "@userPermissionValidator.isSuperAdmin()") + @PreAuthorize(value = "@unifiedPermissionValidator.isSuperAdmin()") @GetMapping("/server/portal-db/config/find-all-config") public List findAllPortalDBServerConfig() { return serverConfigService.findAllPortalDBConfig(); } - @PreAuthorize(value = "@userPermissionValidator.isSuperAdmin()") + @PreAuthorize(value = "@unifiedPermissionValidator.isSuperAdmin()") @GetMapping("/server/envs/{env}/config-db/config/find-all-config") public List findAllConfigDBServerConfig(@PathVariable String env) { return serverConfigService.findAllConfigDBConfig(Env.transformEnv(env)); diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/SystemInfoController.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/SystemInfoController.java index 72e33b4304d..9ecd57d5a3e 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/SystemInfoController.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/SystemInfoController.java @@ -65,7 +65,7 @@ private void init() { restTemplate = restTemplateFactory.getObject(); } - @PreAuthorize(value = "@userPermissionValidator.isSuperAdmin()") + @PreAuthorize(value = "@unifiedPermissionValidator.isSuperAdmin()") @GetMapping public SystemInfo getSystemInfo() { SystemInfo systemInfo = new SystemInfo(); @@ -86,7 +86,7 @@ public SystemInfo getSystemInfo() { return systemInfo; } - @PreAuthorize(value = "@userPermissionValidator.isSuperAdmin()") + @PreAuthorize(value = "@unifiedPermissionValidator.isSuperAdmin()") @GetMapping(value = "/health") public Health checkHealth(@RequestParam String instanceId) { List allEnvs = portalSettings.getAllEnvs(); diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/UserInfoController.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/UserInfoController.java index 95ca76952b2..7636938301b 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/UserInfoController.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/UserInfoController.java @@ -58,7 +58,7 @@ public UserInfoController( this.passwordChecker = passwordChecker; } - @PreAuthorize(value = "@userPermissionValidator.isSuperAdmin()") + @PreAuthorize(value = "@unifiedPermissionValidator.isSuperAdmin()") @PostMapping("/users") public void createOrUpdateUser( @RequestParam(value = "isCreate", defaultValue = "false") boolean isCreate, @@ -83,7 +83,7 @@ public void createOrUpdateUser( } } - @PreAuthorize(value = "@userPermissionValidator.isSuperAdmin()") + @PreAuthorize(value = "@unifiedPermissionValidator.isSuperAdmin()") @PutMapping("/users/enabled") public void changeUserEnabled(@RequestBody UserPO user) { if (userService instanceof SpringSecurityUserService) { diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/entity/po/Permission.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/entity/po/Permission.java index 10f1e86518b..d225dfb984a 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/entity/po/Permission.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/entity/po/Permission.java @@ -17,13 +17,12 @@ package com.ctrip.framework.apollo.portal.entity.po; import com.ctrip.framework.apollo.common.entity.BaseEntity; - -import org.hibernate.annotations.SQLDelete; -import org.hibernate.annotations.Where; - +import java.util.Objects; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.Where; /** * @author Jason Song(song_s@ctrip.com) @@ -33,12 +32,21 @@ @SQLDelete(sql = "Update `Permission` set IsDeleted = true, DeletedAt = ROUND(UNIX_TIMESTAMP(NOW(4))*1000) where Id = ?") @Where(clause = "`IsDeleted` = false") public class Permission extends BaseEntity { + @Column(name = "`PermissionType`", nullable = false) private String permissionType; @Column(name = "`TargetId`", nullable = false) private String targetId; + public Permission() { + } + + public Permission(String permissionType, String targetId) { + this.permissionType = permissionType; + this.targetId = targetId; + } + public String getPermissionType() { return permissionType; } @@ -54,4 +62,23 @@ public String getTargetId() { public void setTargetId(String targetId) { this.targetId = targetId; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Permission)) { + return false; + } + Permission that = (Permission) o; + return Objects.equals(permissionType, that.permissionType) && Objects.equals(targetId, + that.targetId); + } + + @Override + public int hashCode() { + return Objects.hash(permissionType, targetId); + } + } diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/filter/UserTypeResolverFilter.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/filter/UserTypeResolverFilter.java new file mode 100644 index 00000000000..811af0dbd14 --- /dev/null +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/filter/UserTypeResolverFilter.java @@ -0,0 +1,61 @@ +/* + * Copyright 2025 Apollo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package com.ctrip.framework.apollo.portal.filter; + +import com.ctrip.framework.apollo.portal.component.UserIdentityContextHolder; +import com.ctrip.framework.apollo.portal.constant.UserIdentityConstants; + +import java.io.IOException; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.security.authentication.AnonymousAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.filter.OncePerRequestFilter; + +import static com.ctrip.framework.apollo.openapi.util.ConsumerAuthUtil.CONSUMER_ID; + +public class UserTypeResolverFilter extends OncePerRequestFilter { + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { + + String authType = resolve(request); + UserIdentityContextHolder.setAuthType(authType); + try { + filterChain.doFilter(request, response); + } finally { + UserIdentityContextHolder.clear(); + } + } + + private String resolve(HttpServletRequest req) { + if (req.getAttribute(CONSUMER_ID) != null) { + return UserIdentityConstants.CONSUMER; + } + + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + if (auth != null && auth.isAuthenticated() && !(auth instanceof AnonymousAuthenticationToken)) { + return UserIdentityConstants.USER; + } + + return UserIdentityConstants.ANONYMOUS; + } +} diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/repository/PermissionRepository.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/repository/PermissionRepository.java index 89e53ca0dea..e52547980c3 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/repository/PermissionRepository.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/repository/PermissionRepository.java @@ -17,18 +17,18 @@ package com.ctrip.framework.apollo.portal.repository; import com.ctrip.framework.apollo.portal.entity.po.Permission; - +import java.util.Collection; +import java.util.List; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.PagingAndSortingRepository; - -import java.util.Collection; -import java.util.List; +import org.springframework.data.repository.query.Param; /** * @author Jason Song(song_s@ctrip.com) */ public interface PermissionRepository extends PagingAndSortingRepository { + /** * find permission by permission type and targetId */ @@ -38,7 +38,7 @@ public interface PermissionRepository extends PagingAndSortingRepository findByPermissionTypeInAndTargetId(Collection permissionTypes, - String targetId); + String targetId); @Query("SELECT p.id from Permission p where p.targetId like ?1 or p.targetId like CONCAT(?1, '+%')") List findPermissionIdsByAppId(String appId); @@ -56,6 +56,18 @@ List findByPermissionTypeInAndTargetId(Collection permission Integer batchDelete(List permissionIds, String operator); @Query("SELECT p.id from Permission p where p.targetId = CONCAT(?1, '+', ?2, '+', ?3)" - + " AND ( p.permissionType = 'ModifyNamespacesInCluster' OR p.permissionType = 'ReleaseNamespacesInCluster')") + + " AND ( p.permissionType = 'ModifyNamespacesInCluster' OR p.permissionType = 'ReleaseNamespacesInCluster')") List findPermissionIdsByAppIdAndEnvAndCluster(String appId, String env, String clusterName); -} + + @Query("SELECT DISTINCT p " + "FROM UserRole ur " + + "JOIN RolePermission rp ON ur.roleId = rp.roleId " + + "JOIN Permission p ON rp.permissionId = p.id " + "WHERE ur.userId = :userId " + + "AND ur.isDeleted = false " + "AND rp.isDeleted = false " + "AND p.isDeleted = false") + List findUserPermissions(@Param("userId") String userId); + + @Query("SELECT DISTINCT p " + "FROM ConsumerRole cr " + + "JOIN RolePermission rp ON cr.roleId = rp.roleId " + + "JOIN Permission p ON rp.permissionId = p.id " + "WHERE cr.consumerId = :consumerId " + + "AND cr.isDeleted = false " + "AND rp.isDeleted = false " + "AND p.isDeleted = false") + List findConsumerPermissions(@Param("consumerId") long consumerId); +} \ No newline at end of file diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/ConfigsExportService.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/ConfigsExportService.java index 9db7e653136..8ab153ff005 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/ConfigsExportService.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/ConfigsExportService.java @@ -16,6 +16,7 @@ */ package com.ctrip.framework.apollo.portal.service; +import com.ctrip.framework.apollo.portal.component.UnifiedPermissionValidator; import com.google.gson.Gson; import com.ctrip.framework.apollo.common.dto.ClusterDTO; @@ -24,7 +25,6 @@ import com.ctrip.framework.apollo.common.exception.BadRequestException; import com.ctrip.framework.apollo.common.exception.ServiceException; import com.ctrip.framework.apollo.core.enums.ConfigFileFormat; -import com.ctrip.framework.apollo.portal.component.UserPermissionValidator; import com.ctrip.framework.apollo.portal.component.PortalSettings; import com.ctrip.framework.apollo.portal.entity.bo.ConfigBO; import com.ctrip.framework.apollo.portal.entity.bo.NamespaceBO; @@ -66,21 +66,20 @@ public class ConfigsExportService { private final PortalSettings portalSettings; - private final UserPermissionValidator userPermissionValidator; + private final UnifiedPermissionValidator unifiedPermissionValidator; public ConfigsExportService( - AppService appService, - ClusterService clusterService, - final @Lazy NamespaceService namespaceService, - final AppNamespaceService appNamespaceService, - PortalSettings portalSettings, - UserPermissionValidator userPermissionValidator) { + AppService appService, + ClusterService clusterService, + final @Lazy NamespaceService namespaceService, + final AppNamespaceService appNamespaceService, + PortalSettings portalSettings, UnifiedPermissionValidator unifiedPermissionValidator) { this.appService = appService; this.clusterService = clusterService; this.namespaceService = namespaceService; this.appNamespaceService = appNamespaceService; this.portalSettings = portalSettings; - this.userPermissionValidator = userPermissionValidator; + this.unifiedPermissionValidator = unifiedPermissionValidator; } /** @@ -144,7 +143,7 @@ private List findHasPermissionApps() { final Predicate isAppAdmin = app -> { try { - return userPermissionValidator.isAppAdmin(app.getAppId()); + return unifiedPermissionValidator.isAppAdmin(app.getAppId()); } catch (Exception e) { logger.error("permission check failed. app = {}", app); return false; diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/RolePermissionService.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/RolePermissionService.java index d003484db9d..25fd3ce75bd 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/RolePermissionService.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/RolePermissionService.java @@ -92,4 +92,9 @@ Set assignRoleToUsers(String roleName, Set userIds, * delete permissions when delete cluster. */ void deleteRolePermissionsByCluster(String appId, String env, String clusterName, String operator); + + /** + * Check if user has any of the given permissions + */ + boolean hasAnyPermission(String userId, List permissions); } diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/spi/configuration/AuthFilterConfiguration.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/spi/configuration/AuthFilterConfiguration.java index 38a662c6dae..105af8f68aa 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/spi/configuration/AuthFilterConfiguration.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/spi/configuration/AuthFilterConfiguration.java @@ -19,6 +19,7 @@ import com.ctrip.framework.apollo.openapi.filter.ConsumerAuthenticationFilter; import com.ctrip.framework.apollo.openapi.util.ConsumerAuditUtil; import com.ctrip.framework.apollo.openapi.util.ConsumerAuthUtil; +import com.ctrip.framework.apollo.portal.filter.UserTypeResolverFilter; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -26,6 +27,7 @@ @Configuration public class AuthFilterConfiguration { + private static final int OPEN_API_AUTH_ORDER = -99; @Bean public FilterRegistrationBean openApiAuthenticationFilter( ConsumerAuthUtil consumerAuthUtil, @@ -35,8 +37,18 @@ public FilterRegistrationBean openApiAuthenticatio openApiFilter.setFilter(new ConsumerAuthenticationFilter(consumerAuthUtil, consumerAuditUtil)); openApiFilter.addUrlPatterns("/openapi/*"); + openApiFilter.setOrder(OPEN_API_AUTH_ORDER); return openApiFilter; } + @Bean + public FilterRegistrationBean authTypeResolverFilter() { + FilterRegistrationBean authTypeResolverFilter = new FilterRegistrationBean<>(); + authTypeResolverFilter.setFilter(new UserTypeResolverFilter()); + authTypeResolverFilter.addUrlPatterns("/*"); + authTypeResolverFilter.setOrder(OPEN_API_AUTH_ORDER + 1); + return authTypeResolverFilter; + } + } diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/spi/defaultimpl/DefaultRolePermissionService.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/spi/defaultimpl/DefaultRolePermissionService.java index dfcf51eeb2c..0fc00cbd465 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/spi/defaultimpl/DefaultRolePermissionService.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/spi/defaultimpl/DefaultRolePermissionService.java @@ -375,5 +375,16 @@ public void deleteRolePermissionsByCluster(String appId, String env, String clus } } + public boolean hasAnyPermission(String userId, List permissions) { + List userPermissions = permissionRepository.findUserPermissions(userId); + + if (CollectionUtils.isEmpty(userPermissions)) { + return false; + } + + Set userPermissionSet = Sets.newHashSet(userPermissions); + + return permissions.stream().anyMatch(userPermissionSet::contains); + } } diff --git a/apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/auth/ConsumerPermissionValidatorTest.java b/apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/auth/ConsumerPermissionValidatorTest.java new file mode 100644 index 00000000000..4387ab8b95e --- /dev/null +++ b/apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/auth/ConsumerPermissionValidatorTest.java @@ -0,0 +1,277 @@ +/* + * Copyright 2024 Apollo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package com.ctrip.framework.apollo.openapi.auth; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.anyList; +import static org.mockito.Mockito.anyLong; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.ctrip.framework.apollo.common.entity.AppNamespace; +import com.ctrip.framework.apollo.openapi.service.ConsumerRolePermissionService; +import com.ctrip.framework.apollo.openapi.util.ConsumerAuthUtil; +import com.ctrip.framework.apollo.portal.constant.PermissionType; +import com.ctrip.framework.apollo.portal.entity.po.Permission; +import com.ctrip.framework.apollo.portal.service.SystemRoleManagerService; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +/** + * Unit tests for the ConsumerPermissionValidator#hasPermissions method + */ +@ExtendWith(MockitoExtension.class) +public class ConsumerPermissionValidatorTest { + + private static final long CONSUMER_ID = 123L; + private static final String TARGET_ID = "targetId"; + private static final String PERMISSION_TYPE = "permissionType"; + private static final String APP_ID = "testAppId"; + @Mock + private ConsumerRolePermissionService permissionService; + @Mock + private ConsumerAuthUtil consumerAuthUtil; + private ConsumerPermissionValidator validator; + + @BeforeEach + public void setUp() { + validator = new ConsumerPermissionValidator(permissionService, consumerAuthUtil); + // Default mock behavior + lenient().when(consumerAuthUtil.retrieveConsumerIdFromCtx()).thenReturn(CONSUMER_ID); + } + + /** + * Test that hasCreateAppNamespacePermission method throws UnsupportedOperationException + */ + @Test + public void testHasCreateAppNamespacePermission_ThrowsUnsupportedOperationException() { + // Arrange + String appId = "testAppId"; + AppNamespace appNamespace = new AppNamespace(); + + // Act & Assert + assertThrows(UnsupportedOperationException.class, () -> { + validator.hasCreateAppNamespacePermission(appId, appNamespace); + }); + } + + /** + * Test that isSuperAdmin method always returns false + */ + @Test + public void testIsSuperAdmin_ReturnsFalse() { + // Act + boolean result = validator.isSuperAdmin(); + + // Assert + assertFalse(result); + } + + /** + * Test that shouldHideConfigToCurrentUser method throws UnsupportedOperationException + */ + @Test + public void testShouldHideConfigToCurrentUser_ThrowsUnsupportedOperationException() { + // Arrange + String appId = "testAppId"; + String env = "DEV"; + String clusterName = "default"; + String namespaceName = "application"; + + // Act & Assert + assertThrows(UnsupportedOperationException.class, () -> { + validator.shouldHideConfigToCurrentUser(appId, env, clusterName, namespaceName); + }); + } + + /** + * TC005: Consumer has create application permission, should return true + */ + @Test + public void testHasCreateApplicationPermission_UserHasPermission_ReturnsTrue() { + // Arrange + when(consumerAuthUtil.retrieveConsumerIdFromCtx()).thenReturn(CONSUMER_ID); + when(permissionService.consumerHasPermission( + CONSUMER_ID, + PermissionType.CREATE_APPLICATION, + SystemRoleManagerService.SYSTEM_PERMISSION_TARGET_ID)) + .thenReturn(true); + + // Act + boolean result = validator.hasCreateApplicationPermission(); + + // Assert + assertTrue(result); + verify(consumerAuthUtil, times(1)).retrieveConsumerIdFromCtx(); + verify(permissionService, times(1)) + .consumerHasPermission(CONSUMER_ID, PermissionType.CREATE_APPLICATION, + SystemRoleManagerService.SYSTEM_PERMISSION_TARGET_ID); + } + + /** + * TC006: Consumer does not have create application permission, should return false + */ + @Test + public void testHasCreateApplicationPermission_UserHasNoPermission_ReturnsFalse() { + // Arrange + when(consumerAuthUtil.retrieveConsumerIdFromCtx()).thenReturn(CONSUMER_ID); + when(permissionService.consumerHasPermission( + CONSUMER_ID, + PermissionType.CREATE_APPLICATION, + SystemRoleManagerService.SYSTEM_PERMISSION_TARGET_ID)) + .thenReturn(false); + + // Act + boolean result = validator.hasCreateApplicationPermission(); + + // Assert + assertFalse(result); + verify(consumerAuthUtil, times(1)).retrieveConsumerIdFromCtx(); + verify(permissionService, times(1)) + .consumerHasPermission(CONSUMER_ID, PermissionType.CREATE_APPLICATION, + SystemRoleManagerService.SYSTEM_PERMISSION_TARGET_ID); + } + + /** + * TC007: retrieveConsumerIdFromCtx throws exception, should throw exception + */ + @Test + public void testHasCreateApplicationPermission_RetrieveConsumerIdThrowsException_ThrowsException() { + // Arrange + when(consumerAuthUtil.retrieveConsumerIdFromCtx()).thenThrow( + new RuntimeException("Consumer ID not found")); + + // Act & Assert + assertThrows(RuntimeException.class, () -> { + validator.hasCreateApplicationPermission(); + }); + + verify(consumerAuthUtil, times(1)).retrieveConsumerIdFromCtx(); + verify(permissionService, never()) + .consumerHasPermission(anyLong(), anyString(), anyString()); + } + + /** + * TC008: consumerHasPermission throws exception, should throw exception + */ + @Test + public void testHasCreateApplicationPermission_ConsumerHasPermissionThrowsException_ThrowsException() { + // Arrange + when(consumerAuthUtil.retrieveConsumerIdFromCtx()).thenReturn(CONSUMER_ID); + when(permissionService.consumerHasPermission( + CONSUMER_ID, + PermissionType.CREATE_APPLICATION, + SystemRoleManagerService.SYSTEM_PERMISSION_TARGET_ID)) + .thenThrow(new RuntimeException("Permission check failed")); + + // Act & Assert + assertThrows(RuntimeException.class, () -> { + validator.hasCreateApplicationPermission(); + }); + + verify(consumerAuthUtil, times(1)).retrieveConsumerIdFromCtx(); + verify(permissionService, times(1)) + .consumerHasPermission(CONSUMER_ID, PermissionType.CREATE_APPLICATION, + SystemRoleManagerService.SYSTEM_PERMISSION_TARGET_ID); + } + + @Test + public void testHasManageAppMasterPermission_NotSupported_ThrowsException() { + // Act & Assert + assertThrows(UnsupportedOperationException.class, () -> { + validator.hasManageAppMasterPermission(APP_ID); + }); + } + + + /** + * TC001: Consumer has at least one of the required permissions, should return true + */ + @Test + public void testHasPermissions_UserHasPermission_ReturnsTrue() { + // 创建 Permission 对象列表 + List requiredPerms = Arrays.asList( + new Permission("a", "b"), + new Permission("c", "d") + ); + + // 模拟 permissionService.hasAnyPermission 返回 true + when(consumerAuthUtil.retrieveConsumerIdFromCtx()).thenReturn(CONSUMER_ID); + when(permissionService.hasAnyPermission(CONSUMER_ID, requiredPerms)).thenReturn(true); + + boolean result = validator.hasPermissions(requiredPerms); + + assertTrue(result); + verify(consumerAuthUtil, times(1)).retrieveConsumerIdFromCtx(); + verify(permissionService, times(1)).hasAnyPermission(CONSUMER_ID, requiredPerms); + } + + /** + * TC002: Consumer does not have any of the required permissions, should return false + */ + @Test + public void testHasPermissions_UserHasNoPermission_ReturnsFalse() { + List requiredPerms = Arrays.asList( + new Permission("a", "b"), + new Permission("c", "d") + ); + + when(consumerAuthUtil.retrieveConsumerIdFromCtx()).thenReturn(CONSUMER_ID); + when(permissionService.hasAnyPermission(CONSUMER_ID, requiredPerms)).thenReturn(false); + + boolean result = validator.hasPermissions(requiredPerms); + + assertFalse(result); + } + + /** + * TC003: requiredPerms is an empty list, should return false + */ + @Test + public void testHasPermissions_RequiredPermsIsEmpty_ReturnsFalse() { + List requiredPerms = Collections.emptyList(); + + boolean result = validator.hasPermissions(requiredPerms); + + assertFalse(result); + verify(permissionService, never()).hasAnyPermission(anyLong(), anyList()); + } + + /** + * TC004: requiredPerms is null, should return false + */ + @Test + public void testHasPermissions_RequiredPermsIsNull_ReturnsFalse() { + boolean result = validator.hasPermissions(null); + + assertFalse(result); + verify(permissionService, never()).hasAnyPermission(anyLong(), anyList()); + } + +} \ No newline at end of file diff --git a/apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/v1/controller/AppControllerParamBindLowLevelTest.java b/apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/v1/controller/AppControllerParamBindLowLevelTest.java index 5b6eceb61b0..3895ac5b06b 100644 --- a/apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/v1/controller/AppControllerParamBindLowLevelTest.java +++ b/apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/v1/controller/AppControllerParamBindLowLevelTest.java @@ -25,7 +25,9 @@ import com.ctrip.framework.apollo.openapi.service.ConsumerService; import com.ctrip.framework.apollo.openapi.util.ConsumerAuthUtil; import com.ctrip.framework.apollo.portal.component.PortalSettings; -import com.ctrip.framework.apollo.openapi.auth.ConsumerPermissionValidator; +import com.ctrip.framework.apollo.portal.component.UnifiedPermissionValidator; +import com.ctrip.framework.apollo.portal.component.UserIdentityContextHolder; +import com.ctrip.framework.apollo.portal.constant.UserIdentityConstants; import com.ctrip.framework.apollo.portal.entity.bo.UserInfo; import com.ctrip.framework.apollo.portal.repository.PermissionRepository; import com.ctrip.framework.apollo.portal.repository.RolePermissionRepository; @@ -68,8 +70,8 @@ public class AppControllerParamBindLowLevelTest { @Autowired private MockMvc mockMvc; // Keep the same mocks as your working test to satisfy context wiring - @MockBean(name = "consumerPermissionValidator") - private ConsumerPermissionValidator consumerPermissionValidator; + @MockBean(name = "unifiedPermissionValidator") + private UnifiedPermissionValidator unifiedPermissionValidator; @MockBean private PortalSettings portalSettings; @MockBean private AppService appService; @MockBean private ClusterService clusterService; @@ -93,12 +95,14 @@ public class AppControllerParamBindLowLevelTest { @Before public void setUp() { - when(consumerPermissionValidator.hasCreateApplicationPermission()).thenReturn(true); - when(consumerPermissionValidator.isAppAdmin(anyString())).thenReturn(true); + when(unifiedPermissionValidator.hasCreateApplicationPermission()).thenReturn(true); + when(unifiedPermissionValidator.isAppAdmin(anyString())).thenReturn(true); UserInfo user = new UserInfo(); user.setUserId("tester"); when(userService.findByUserId(anyString())).thenReturn(user); + + UserIdentityContextHolder.setAuthType(UserIdentityConstants.CONSUMER); } @Before public void setAuthentication() { @@ -112,6 +116,7 @@ public void setAuthentication() { @After public void clearAuthentication() { SecurityContextHolder.clearContext(); + UserIdentityContextHolder.clear(); } @Test public void createAppInEnv_shouldBind_env_query_body() throws Exception { diff --git a/apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/v1/controller/AppControllerTest.java b/apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/v1/controller/AppControllerTest.java index 9f2de8263d9..219263709ee 100644 --- a/apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/v1/controller/AppControllerTest.java +++ b/apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/v1/controller/AppControllerTest.java @@ -27,7 +27,9 @@ import com.ctrip.framework.apollo.openapi.service.ConsumerService; import com.ctrip.framework.apollo.openapi.util.ConsumerAuthUtil; import com.ctrip.framework.apollo.portal.component.PortalSettings; -import com.ctrip.framework.apollo.openapi.auth.ConsumerPermissionValidator; +import com.ctrip.framework.apollo.portal.component.UnifiedPermissionValidator; +import com.ctrip.framework.apollo.portal.component.UserIdentityContextHolder; +import com.ctrip.framework.apollo.portal.constant.UserIdentityConstants; import com.ctrip.framework.apollo.portal.entity.bo.UserInfo; import com.ctrip.framework.apollo.portal.repository.PermissionRepository; import com.ctrip.framework.apollo.portal.repository.RolePermissionRepository; @@ -41,6 +43,7 @@ import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.gson.Gson; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -89,9 +92,8 @@ public class AppControllerTest { @Autowired private MockMvc mockMvc; - - @MockBean(name = "consumerPermissionValidator") - private ConsumerPermissionValidator consumerPermissionValidator; + @MockBean(name = "unifiedPermissionValidator") + private UnifiedPermissionValidator unifiedPermissionValidator; @MockBean private PortalSettings portalSettings; @@ -142,14 +144,21 @@ public class AppControllerTest { @Before public void setUpSecurityMocks() { - when(consumerPermissionValidator.hasCreateApplicationPermission()).thenReturn(true); - when(consumerPermissionValidator.hasCreateNamespacePermission(Mockito.any())) + when(unifiedPermissionValidator.hasCreateApplicationPermission()).thenReturn(true); + when(unifiedPermissionValidator.hasCreateNamespacePermission(Mockito.any())) .thenReturn(true); - when(consumerPermissionValidator.isAppAdmin(Mockito.anyString())).thenReturn(true); + when(unifiedPermissionValidator.isAppAdmin(Mockito.anyString())).thenReturn(true); UserInfo userInfo = new UserInfo(); userInfo.setUserId("test"); when(userService.findByUserId(Mockito.anyString())).thenReturn(userInfo); + + UserIdentityContextHolder.setAuthType(UserIdentityConstants.CONSUMER); + } + + @After + public void tearDown() { + UserIdentityContextHolder.clear(); } @Test @@ -332,7 +341,7 @@ public void testUpdateApp() throws Exception { SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(userInfo, null, Collections.emptyList())); Mockito.doNothing().when(appOpenApiService).updateApp(Mockito.any(OpenAppDTO.class)); - when(consumerPermissionValidator.isAppAdmin(appId)).thenReturn(true); + when(unifiedPermissionValidator.isAppAdmin(appId)).thenReturn(true); mockMvc.perform(MockMvcRequestBuilders.put("/openapi/v1/apps/" + appId) .param("operator", operator) @@ -355,7 +364,7 @@ public void testUpdateAppWithMismatchedAppId() throws Exception { userInfo.setUserId("test"); SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(userInfo, null, Collections.emptyList())); - when(consumerPermissionValidator.isAppAdmin(pathAppId)).thenReturn(true); + when(unifiedPermissionValidator.isAppAdmin(pathAppId)).thenReturn(true); mockMvc.perform(MockMvcRequestBuilders.put("/openapi/v1/apps/" + pathAppId) .param("operator", operator) @@ -376,7 +385,7 @@ public void testDeleteApp() throws Exception { SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(userInfo, null, Collections.emptyList())); when(appOpenApiService.deleteApp(appId)).thenReturn(new OpenAppDTO()); - when(consumerPermissionValidator.isAppAdmin(appId)).thenReturn(true); + when(unifiedPermissionValidator.isAppAdmin(appId)).thenReturn(true); mockMvc.perform(delete("/openapi/v1/apps/" + appId) .param("operator", operator)) diff --git a/apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/v1/controller/ClusterControllerParamBindLowLevelTest.java b/apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/v1/controller/ClusterControllerParamBindLowLevelTest.java index 9f48e4a19e1..9e5f92c8acc 100644 --- a/apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/v1/controller/ClusterControllerParamBindLowLevelTest.java +++ b/apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/v1/controller/ClusterControllerParamBindLowLevelTest.java @@ -16,9 +16,11 @@ */ package com.ctrip.framework.apollo.openapi.v1.controller; -import com.ctrip.framework.apollo.openapi.auth.ConsumerPermissionValidator; import com.ctrip.framework.apollo.openapi.model.OpenClusterDTO; import com.ctrip.framework.apollo.openapi.server.service.ClusterOpenApiService; +import com.ctrip.framework.apollo.portal.component.UnifiedPermissionValidator; +import com.ctrip.framework.apollo.portal.component.UserIdentityContextHolder; +import com.ctrip.framework.apollo.portal.constant.UserIdentityConstants; import com.ctrip.framework.apollo.portal.entity.bo.UserInfo; import com.ctrip.framework.apollo.portal.spi.UserService; import com.google.gson.Gson; @@ -58,8 +60,8 @@ public class ClusterControllerParamBindLowLevelTest { @Autowired private MockMvc mockMvc; - @MockBean(name = "consumerPermissionValidator") - private ConsumerPermissionValidator consumerPermissionValidator; + @MockBean(name = "unifiedPermissionValidator") + private UnifiedPermissionValidator unifiedPermissionValidator; @MockBean private UserService userService; @@ -71,12 +73,13 @@ public class ClusterControllerParamBindLowLevelTest { @Before public void setUp() { - when(consumerPermissionValidator.hasCreateClusterPermission(anyString())).thenReturn(true); - when(consumerPermissionValidator.isAppAdmin(anyString())).thenReturn(true); + when(unifiedPermissionValidator.hasCreateClusterPermission(anyString())).thenReturn(true); + when(unifiedPermissionValidator.isAppAdmin(anyString())).thenReturn(true); UserInfo user = new UserInfo(); user.setUserId("tester"); when(userService.findByUserId(anyString())).thenReturn(user); + UserIdentityContextHolder.setAuthType(UserIdentityConstants.CONSUMER); } @Before @@ -91,6 +94,7 @@ public void setAuthentication() { @After public void clearAuthentication() { SecurityContextHolder.clearContext(); + UserIdentityContextHolder.clear(); } @Test diff --git a/apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/v1/controller/ClusterControllerTest.java b/apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/v1/controller/ClusterControllerTest.java index 0a524a9c816..d451c062b4c 100644 --- a/apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/v1/controller/ClusterControllerTest.java +++ b/apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/v1/controller/ClusterControllerTest.java @@ -16,13 +16,17 @@ */ package com.ctrip.framework.apollo.openapi.v1.controller; -import com.ctrip.framework.apollo.openapi.auth.ConsumerPermissionValidator; import com.ctrip.framework.apollo.openapi.model.OpenClusterDTO; import com.ctrip.framework.apollo.openapi.server.service.ClusterOpenApiService; +import com.ctrip.framework.apollo.portal.component.UnifiedPermissionValidator; +import com.ctrip.framework.apollo.portal.component.UserIdentityContextHolder; +import com.ctrip.framework.apollo.portal.constant.UserIdentityConstants; import com.ctrip.framework.apollo.portal.entity.bo.UserInfo; import com.ctrip.framework.apollo.portal.spi.UserService; import com.google.gson.Gson; import java.util.Collections; + +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -65,8 +69,8 @@ public class ClusterControllerTest { @Autowired private MockMvc mockMvc; - @MockBean(name = "consumerPermissionValidator") - private ConsumerPermissionValidator consumerPermissionValidator; + @MockBean(name = "unifiedPermissionValidator") + private UnifiedPermissionValidator unifiedPermissionValidator; @MockBean private UserService userService; @@ -79,14 +83,21 @@ public class ClusterControllerTest { @Before public void setUpSecurityMocks() { - when(consumerPermissionValidator.hasCreateClusterPermission(Mockito.anyString())).thenReturn(true); - when(consumerPermissionValidator.isAppAdmin(Mockito.anyString())).thenReturn(true); + when(unifiedPermissionValidator.hasCreateClusterPermission(Mockito.anyString())).thenReturn(true); + when(unifiedPermissionValidator.isAppAdmin(Mockito.anyString())).thenReturn(true); authenticatedUser = new UserInfo(); authenticatedUser.setUserId("test-operator"); when(userService.findByUserId(Mockito.anyString())).thenReturn(authenticatedUser); SecurityContextHolder.clearContext(); + UserIdentityContextHolder.setAuthType(UserIdentityConstants.CONSUMER); + } + + @After + public void clearAuthentication() { + SecurityContextHolder.clearContext(); + UserIdentityContextHolder.clear(); } private void authenticate() { diff --git a/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/component/AbstractPermissionValidatorTest.java b/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/component/AbstractPermissionValidatorTest.java new file mode 100644 index 00000000000..521a095c431 --- /dev/null +++ b/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/component/AbstractPermissionValidatorTest.java @@ -0,0 +1,273 @@ +/* + * Copyright 2025 Apollo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package com.ctrip.framework.apollo.portal.component; + +import com.ctrip.framework.apollo.common.entity.AppNamespace; +import com.ctrip.framework.apollo.portal.constant.PermissionType; +import com.ctrip.framework.apollo.portal.entity.po.Permission; +import com.ctrip.framework.apollo.portal.util.RoleUtils; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.*; + +@RunWith(MockitoJUnitRunner.class) +public class AbstractPermissionValidatorTest { + + @Mock + private AppNamespace appNamespace; + + private AbstractPermissionValidator permissionValidator; + + @Before + public void setUp() { + permissionValidator = new AbstractPermissionValidatorImpl(); + } + + @Test + public void testHasModifyNamespacePermission_WhenNoPermission() { + String appId = "testApp"; + String env = "DEV"; + String clusterName = "default"; + String namespaceName = "application"; + assertFalse(permissionValidator.hasModifyNamespacePermission(appId, env, clusterName, namespaceName)); + } + + @Test + public void testHasReleaseNamespacePermission_WhenNoPermission() { + String appId = "testApp"; + String env = "DEV"; + String clusterName = "default"; + String namespaceName = "application"; + assertFalse(permissionValidator.hasReleaseNamespacePermission(appId, env, clusterName, namespaceName)); + } + + @Test + public void testHasAssignRolePermission_WhenNoPermission() { + assertFalse(permissionValidator.hasAssignRolePermission("testApp")); + } + + @Test + public void testHasCreateNamespacePermission_WhenNoPermission() { + assertFalse(permissionValidator.hasCreateNamespacePermission("testApp")); + } + + @Test + public void testHasCreateAppNamespacePermission_WhenNoPermission() { + assertFalse(permissionValidator.hasCreateAppNamespacePermission("testApp", appNamespace)); + } + + @Test + public void testHasCreateClusterPermission_WhenNoPermission() { + assertFalse(permissionValidator.hasCreateClusterPermission("testApp")); + } + + @Test + public void testIsSuperAdmin_WhenNoPermission() { + assertFalse(permissionValidator.isSuperAdmin()); + } + + @Test + public void testShouldHideConfigToCurrentUser_WhenNoPermission() { + assertFalse(permissionValidator.shouldHideConfigToCurrentUser("testApp", "DEV", "default", "application")); + } + + @Test + public void testHasCreateApplicationPermission_WhenNoPermission() { + assertFalse(permissionValidator.hasCreateApplicationPermission()); + } + + @Test + public void testHasManageAppMasterPermission_WhenNoPermission() { + assertFalse(permissionValidator.hasManageAppMasterPermission("testApp")); + } + + @Test + public void testHasModifyNamespacePermission_WhenWithPermission() { + String appId = "testApp"; + String env = "DEV"; + String clusterName = "default"; + String namespaceName = "application"; + + List granted = Arrays.asList( + new Permission(PermissionType.MODIFY_NAMESPACE, + RoleUtils.buildNamespaceTargetId(appId, namespaceName)), + new Permission(PermissionType.MODIFY_NAMESPACE, + RoleUtils.buildNamespaceTargetId(appId, namespaceName, env)), + new Permission(PermissionType.MODIFY_NAMESPACES_IN_CLUSTER, + RoleUtils.buildClusterTargetId(appId, env, clusterName)) + ); + + AbstractPermissionValidator validator = new AbstractPermissionValidatorWithPermissionsImpl(granted); + assertTrue(validator.hasModifyNamespacePermission(appId, env, clusterName, namespaceName)); + } + + @Test + public void testHasReleaseNamespacePermission_WhenWithPermission() { + String appId = "testApp"; + String env = "DEV"; + String clusterName = "default"; + String namespaceName = "application"; + + List granted = Arrays.asList( + new Permission(PermissionType.RELEASE_NAMESPACE, + RoleUtils.buildNamespaceTargetId(appId, namespaceName)), + new Permission(PermissionType.RELEASE_NAMESPACE, + RoleUtils.buildNamespaceTargetId(appId, namespaceName, env)), + new Permission(PermissionType.RELEASE_NAMESPACES_IN_CLUSTER, + RoleUtils.buildClusterTargetId(appId, env, clusterName)) + ); + + AbstractPermissionValidator validator = new AbstractPermissionValidatorWithPermissionsImpl(granted); + assertTrue(validator.hasReleaseNamespacePermission(appId, env, clusterName, namespaceName)); + } + + @Test + public void testHasAssignRolePermission_WhenWithPermission() { + String appId = "testApp"; + List granted = Collections.singletonList( + new Permission(PermissionType.ASSIGN_ROLE, appId) + ); + AbstractPermissionValidator validator = new AbstractPermissionValidatorWithPermissionsImpl(granted); + assertTrue(validator.hasAssignRolePermission(appId)); + } + + @Test + public void testHasCreateNamespacePermission_WhenWithPermission() { + String appId = "testApp"; + List granted = Collections.singletonList( + new Permission(PermissionType.CREATE_NAMESPACE, appId) + ); + AbstractPermissionValidator validator = new AbstractPermissionValidatorWithPermissionsImpl(granted); + assertTrue(validator.hasCreateNamespacePermission(appId)); + } + + @Test + public void testHasCreateClusterPermission_WhenWithPermission() { + String appId = "testApp"; + List granted = Collections.singletonList( + new Permission(PermissionType.CREATE_CLUSTER, appId) + ); + AbstractPermissionValidator validator = new AbstractPermissionValidatorWithPermissionsImpl(granted); + assertTrue(validator.hasCreateClusterPermission(appId)); + } + + @Test + public void testShouldHideConfigToCurrentUser_WhenWithPermission() { + String appId = "testApp"; + String env = "DEV"; + String clusterName = "default"; + String namespaceName = "application"; + + List granted = Arrays.asList( + new Permission(PermissionType.MODIFY_NAMESPACE, + RoleUtils.buildNamespaceTargetId(appId, namespaceName)), + new Permission(PermissionType.RELEASE_NAMESPACE, + RoleUtils.buildNamespaceTargetId(appId, namespaceName)) + ); + + AbstractPermissionValidator validator = new AbstractPermissionValidatorWithPermissionsImpl(granted); + assertFalse(validator.shouldHideConfigToCurrentUser(appId, env, clusterName, namespaceName)); + } + + @Test + public void testHasManageAppMasterPermission_WhenWithPermission() { + String appId = "testApp"; + List granted = Collections.singletonList( + new Permission(PermissionType.MANAGE_APP_MASTER, appId) + ); + AbstractPermissionValidator validator = new AbstractPermissionValidatorWithPermissionsImpl(granted); + assertTrue(validator.hasManageAppMasterPermission(appId)); + } + + private static class AbstractPermissionValidatorImpl extends AbstractPermissionValidator { + @Override + public boolean hasCreateAppNamespacePermission(String appId, AppNamespace appNamespace) { + return false; + } + + @Override + public boolean isSuperAdmin() { + return false; + } + + @Override + public boolean hasCreateApplicationPermission() { + return false; + } + + @Override + public boolean hasManageAppMasterPermission(String appId) { + return false; + } + + @Override + protected boolean hasPermissions(List requiredPerms) { + return false; + } + + @Override + public boolean hasCreateApplicationPermission(String userId) { + return false; + } + } + + private static class AbstractPermissionValidatorWithPermissionsImpl extends AbstractPermissionValidator { + private final List allowed; + + AbstractPermissionValidatorWithPermissionsImpl(List allowed) { + this.allowed = allowed; + } + + @Override + public boolean hasCreateAppNamespacePermission(String appId, AppNamespace appNamespace) { + return true; + } + + @Override + public boolean isSuperAdmin() { + return true; + } + + @Override + public boolean hasCreateApplicationPermission() { + return true; + } + + @Override + public boolean hasManageAppMasterPermission(String appId) { + return true; + } + + @Override + protected boolean hasPermissions(List requiredPerms) { + return requiredPerms.stream().anyMatch(allowed::contains); + } + + @Override + public boolean hasCreateApplicationPermission(String userId) { + return true; + } + } +} \ No newline at end of file diff --git a/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/component/UnifiedPermissionValidatorTest.java b/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/component/UnifiedPermissionValidatorTest.java new file mode 100644 index 00000000000..1c38d359a70 --- /dev/null +++ b/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/component/UnifiedPermissionValidatorTest.java @@ -0,0 +1,141 @@ +/* + * Copyright 2025 Apollo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.ctrip.framework.apollo.portal.component; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.when; + +import com.ctrip.framework.apollo.openapi.auth.ConsumerPermissionValidator; +import com.ctrip.framework.apollo.portal.constant.UserIdentityConstants; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class UnifiedPermissionValidatorTest { + + @Mock + private UserPermissionValidator userPermissionValidator; + + @Mock + private ConsumerPermissionValidator consumerPermissionValidator; + + @InjectMocks + private UnifiedPermissionValidator unifiedPermissionValidator; + + // No additional initialization required before each test method (keep as is) + @BeforeEach + public void setUp() { + // No operation needed, UserIdentityContextHolder state will be set separately in each test + } + + // Clean up UserIdentityContextHolder state after each test method (critical! avoid pollution between tests) + @AfterEach + public void tearDown() { + UserIdentityContextHolder.clear(); + } + + @Test + public void hasManageAppMasterPermission_UserAuthType_DelegatesToUserValidator() { + final String appId = "testAppId"; + final boolean expectedPermission = true; + + // Set authentication type to USER + UserIdentityContextHolder.setAuthType(UserIdentityConstants.USER); + when(userPermissionValidator.hasManageAppMasterPermission(appId)).thenReturn( + expectedPermission); + + boolean result = unifiedPermissionValidator.hasManageAppMasterPermission(appId); + + assertTrue(result); + } + + @Test + public void hasManageAppMasterPermission_ConsumerAuthType_DelegatesToConsumerValidator() { + final String appId = "testAppId"; + final boolean expectedPermission = false; + + // Set authentication type to CONSUMER + UserIdentityContextHolder.setAuthType(UserIdentityConstants.CONSUMER); + when(consumerPermissionValidator.hasManageAppMasterPermission(appId)).thenReturn( + expectedPermission); + + boolean result = unifiedPermissionValidator.hasManageAppMasterPermission(appId); + + assertFalse(result); + } + + @Test + public void hasManageAppMasterPermission_UnknownAuthType_ThrowsException() { + final String appId = "testAppId"; + + // Set authentication type to UNKNOWN + UserIdentityContextHolder.setAuthType("UNKNOWN"); + + assertThrows(IllegalStateException.class, () -> { + unifiedPermissionValidator.hasManageAppMasterPermission(appId); + }); + } + + @Test + public void hasCreateNamespacePermission_UserAuthType_UsesUserPermissionValidator() { + final String appId = "testAppId"; + final boolean expectedPermission = true; + + // Set authentication type to USER + UserIdentityContextHolder.setAuthType(UserIdentityConstants.USER); + when(userPermissionValidator.hasCreateNamespacePermission(appId)).thenReturn( + expectedPermission); + + boolean result = unifiedPermissionValidator.hasCreateNamespacePermission(appId); + + assertTrue(result); + } + + @Test + public void hasCreateNamespacePermission_ConsumerAuthType_UsesConsumerPermissionValidator() { + final String appId = "testAppId"; + final boolean expectedPermission = true; + + // Set authentication type to CONSUMER + UserIdentityContextHolder.setAuthType(UserIdentityConstants.CONSUMER); + when(consumerPermissionValidator.hasCreateNamespacePermission(appId)).thenReturn( + expectedPermission); + + boolean result = unifiedPermissionValidator.hasCreateNamespacePermission(appId); + + assertTrue(result); + } + + @Test + public void hasCreateNamespacePermission_UnknownAuthType_ThrowsIllegalStateException() { + final String appId = "testAppId"; + + // Set authentication type to UNKNOWN + UserIdentityContextHolder.setAuthType("UNKNOWN"); + + assertThrows(IllegalStateException.class, + () -> unifiedPermissionValidator.hasCreateNamespacePermission(appId)); + } +} \ No newline at end of file diff --git a/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/component/UserIdentityContextHolderTest.java b/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/component/UserIdentityContextHolderTest.java new file mode 100644 index 00000000000..0edfd2d20d0 --- /dev/null +++ b/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/component/UserIdentityContextHolderTest.java @@ -0,0 +1,76 @@ +/* + * Copyright 2025 Apollo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package com.ctrip.framework.apollo.portal.component; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class UserIdentityContextHolderTest { + + @BeforeEach + public void setUp() { + // Clear ThreadLocal before each test + UserIdentityContextHolder.clear(); + } + + @Test + public void setAuthType_NonNullAuthType_ShouldSetCorrectly() { + String authType = "testAuthType"; + UserIdentityContextHolder.setAuthType(authType); + assertEquals(authType, UserIdentityContextHolder.getAuthType()); + } + + @Test + public void setAuthType_NullAuthType_ShouldSetCorrectly() { + UserIdentityContextHolder.setAuthType(null); + assertNull(UserIdentityContextHolder.getAuthType()); + } + + @Test + public void getAuthType_WhenNotSet_ShouldReturnNull() { + // Test: getAuthType should return null when AUTH_TYPE_HOLDER is not set + assertNull(UserIdentityContextHolder.getAuthType(), + "Expected null when AUTH_TYPE_HOLDER is not set"); + } + + @Test + public void getAuthType_WhenSet_ShouldReturnCorrectValue() { + // Setup: Set a value in AUTH_TYPE_HOLDER + UserIdentityContextHolder.setAuthType("TestAuthType"); + + // Test: getAuthType should return the set value + assertEquals("TestAuthType", UserIdentityContextHolder.getAuthType(), + "Expected 'TestAuthType' when AUTH_TYPE_HOLDER is set"); + } + + @Test + public void clear_ShouldRemoveAuthTypeHolderValue() { + // Step 1: Set authentication type + UserIdentityContextHolder.setAuthType("testValue"); + assertEquals("testValue", UserIdentityContextHolder.getAuthType()); // Verify setup success + + // Step 2: Call clear() method + UserIdentityContextHolder.clear(); + + // Step 3: Verify result after clearing (get value via public method) + assertNull( + UserIdentityContextHolder.getAuthType()); // Directly verify getAuthType() returns null + } +} \ No newline at end of file diff --git a/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/component/UserPermissionValidatorTest.java b/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/component/UserPermissionValidatorTest.java new file mode 100644 index 00000000000..cf5c557a664 --- /dev/null +++ b/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/component/UserPermissionValidatorTest.java @@ -0,0 +1,223 @@ +/* + * Copyright 2024 Apollo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package com.ctrip.framework.apollo.portal.component; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.when; + +import com.ctrip.framework.apollo.common.entity.AppNamespace; +import com.ctrip.framework.apollo.portal.component.config.PortalConfig; +import com.ctrip.framework.apollo.portal.constant.PermissionType; +import com.ctrip.framework.apollo.portal.entity.bo.UserInfo; +import com.ctrip.framework.apollo.portal.entity.po.Permission; +import com.ctrip.framework.apollo.portal.service.AppNamespaceService; +import com.ctrip.framework.apollo.portal.service.RolePermissionService; +import com.ctrip.framework.apollo.portal.service.SystemRoleManagerService; +import com.ctrip.framework.apollo.portal.spi.UserInfoHolder; +import com.google.common.collect.Lists; +import java.util.Collections; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class UserPermissionValidatorTest { + + private static final String USER_ID = "test-user"; + private static final String APP_ID = "test-app"; + private static final String ENV = "DEV"; + private static final String CLUSTER = "default"; + private static final String NAMESPACE = "application"; + @Mock + private UserInfoHolder userInfoHolder; + @Mock + private RolePermissionService rolePermissionService; + @Mock + private PortalConfig portalConfig; + @Mock + private AppNamespaceService appNamespaceService; + @Mock + private SystemRoleManagerService systemRoleManagerService; + @InjectMocks + private UserPermissionValidator validator; + + @BeforeEach + void setUp() { + // Create a UserInfo instance + UserInfo stubUser = new UserInfo(); + stubUser.setUserId(USER_ID); + stubUser.setName("test"); + lenient().when(userInfoHolder.getUser()).thenReturn(stubUser); + } + + // 1. hasCreateAppNamespacePermission tests + + @Test + void hasCreateAppNamespacePermission_publicNamespace() { + AppNamespace publicNs = new AppNamespace(); + publicNs.setPublic(true); + List requiredPermissions = Collections.singletonList( + new Permission(PermissionType.CREATE_NAMESPACE, APP_ID)); + when(rolePermissionService.hasAnyPermission(USER_ID, requiredPermissions)).thenReturn(true); + assertThat(validator.hasCreateAppNamespacePermission(APP_ID, publicNs)).isTrue(); + } + + @Test + void hasCreateAppNamespacePermission_privateNamespace_adminCanCreate() { + AppNamespace privateNs = new AppNamespace(); + privateNs.setPublic(false); + + when(portalConfig.canAppAdminCreatePrivateNamespace()).thenReturn(true); + List requiredPermissions = Collections.singletonList( + new Permission(PermissionType.CREATE_NAMESPACE, APP_ID)); + when(rolePermissionService.hasAnyPermission(USER_ID, requiredPermissions)).thenReturn(true); + + assertThat(validator.hasCreateAppNamespacePermission(APP_ID, privateNs)).isTrue(); + } + + @Test + void hasCreateAppNamespacePermission_privateNamespace_adminCannotCreate_andUserIsSuperAdmin() { + AppNamespace privateNs = new AppNamespace(); + privateNs.setPublic(false); + + when(portalConfig.canAppAdminCreatePrivateNamespace()).thenReturn(false); + when(rolePermissionService.isSuperAdmin(USER_ID)).thenReturn(true); + + assertThat(validator.hasCreateAppNamespacePermission(APP_ID, privateNs)).isTrue(); + } + + @Test + void hasCreateAppNamespacePermission_privateNamespace_adminCannotCreate_andUserIsNotSuperAdmin() { + AppNamespace privateNs = new AppNamespace(); + privateNs.setPublic(false); + + when(portalConfig.canAppAdminCreatePrivateNamespace()).thenReturn(false); + when(rolePermissionService.isSuperAdmin(USER_ID)).thenReturn(false); + + assertThat(validator.hasCreateAppNamespacePermission(APP_ID, privateNs)).isFalse(); + } + + // 2. isSuperAdmin tests + + @Test + void isSuperAdmin_true() { + when(rolePermissionService.isSuperAdmin(USER_ID)).thenReturn(true); + assertThat(validator.isSuperAdmin()).isTrue(); + } + + @Test + void isSuperAdmin_false() { + when(rolePermissionService.isSuperAdmin(USER_ID)).thenReturn(false); + assertThat(validator.isSuperAdmin()).isFalse(); + } + @Test + void shouldHideConfigToCurrentUser_publicNamespace() { + when(portalConfig.isConfigViewMemberOnly(ENV)).thenReturn(true); + + AppNamespace publicNs = new AppNamespace(); + publicNs.setPublic(true); + when(appNamespaceService.findByAppIdAndName(APP_ID, NAMESPACE)).thenReturn(publicNs); + + assertThat(validator.shouldHideConfigToCurrentUser(APP_ID, ENV, CLUSTER, NAMESPACE)).isFalse(); + } + + @Test + void shouldHideConfigToCurrentUser_userIsNotAppAdmin() { + when(portalConfig.isConfigViewMemberOnly(ENV)).thenReturn(true); + when(appNamespaceService.findByAppIdAndName(APP_ID, NAMESPACE)).thenReturn(null); + assertThat(validator.shouldHideConfigToCurrentUser(APP_ID, ENV, CLUSTER, NAMESPACE)).isTrue(); + } + + @Test + void shouldHideConfigToCurrentUser_configViewNotMemberOnly() { + when(portalConfig.isConfigViewMemberOnly(ENV)).thenReturn(false); + assertThat(validator.shouldHideConfigToCurrentUser(APP_ID, ENV, CLUSTER, NAMESPACE)).isFalse(); + } + + // 4. hasCreateApplicationPermission tests + + @Test + void hasCreateApplicationPermission_true() { + when(systemRoleManagerService.hasCreateApplicationPermission(USER_ID)).thenReturn(true); + assertThat(validator.hasCreateApplicationPermission()).isTrue(); + } + + @Test + void hasCreateApplicationPermission_false() { + when(systemRoleManagerService.hasCreateApplicationPermission(USER_ID)).thenReturn(false); + assertThat(validator.hasCreateApplicationPermission()).isFalse(); + } + + // 5. hasManageAppMasterPermission tests + + @Test + void hasManageAppMasterPermission_superAdmin() { + when(rolePermissionService.isSuperAdmin(USER_ID)).thenReturn(true); + assertThat(validator.hasManageAppMasterPermission(APP_ID)).isTrue(); + } + + @Test + void hasManageAppMasterPermission_normalUser_withAssignRole_andManageAppMaster() { + when(rolePermissionService.isSuperAdmin(USER_ID)).thenReturn(false); + List requiredPermissions = Collections.singletonList( + new Permission(PermissionType.ASSIGN_ROLE, APP_ID)); + when(rolePermissionService.hasAnyPermission(USER_ID, requiredPermissions)).thenReturn(true); + + when(systemRoleManagerService.hasManageAppMasterPermission(USER_ID, APP_ID)).thenReturn(true); + + assertThat(validator.hasManageAppMasterPermission(APP_ID)).isTrue(); + } + + @Test + void hasManageAppMasterPermission_normalUser_withoutManageAppMaster() { + when(rolePermissionService.isSuperAdmin(USER_ID)).thenReturn(false); + List requiredPermissions = Collections.singletonList( + new Permission(PermissionType.ASSIGN_ROLE, APP_ID)); + when(rolePermissionService.hasAnyPermission(USER_ID, requiredPermissions)).thenReturn(true); + when(systemRoleManagerService.hasManageAppMasterPermission(USER_ID, APP_ID)).thenReturn(false); + + assertThat(validator.hasManageAppMasterPermission(APP_ID)).isFalse(); + } + + + @Test + void hasPermissions_match() { + List requiredPerms = Lists.newArrayList(new Permission(), new Permission()); + when(rolePermissionService.hasAnyPermission(USER_ID, requiredPerms)).thenReturn(true); + + assertThat(validator.hasPermissions(requiredPerms)).isTrue(); + } + + @Test + void hasPermissions_notMatch() { + List requiredPerms = Lists.newArrayList(new Permission(), new Permission()); + when(rolePermissionService.hasAnyPermission(USER_ID, requiredPerms)).thenReturn(false); + + assertThat(validator.hasPermissions(requiredPerms)).isFalse(); + } + + @Test + void hasPermissions_emptyList() { + assertThat(validator.hasPermissions(Collections.emptyList())).isFalse(); + } + +} \ No newline at end of file diff --git a/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/controller/ItemControllerAuthIntegrationTest.java b/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/controller/ItemControllerAuthIntegrationTest.java index 05cbbcb700b..236544c46d4 100644 --- a/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/controller/ItemControllerAuthIntegrationTest.java +++ b/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/controller/ItemControllerAuthIntegrationTest.java @@ -18,6 +18,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.Mockito.any; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.verify; @@ -29,9 +30,11 @@ import com.ctrip.framework.apollo.portal.entity.bo.UserInfo; import com.ctrip.framework.apollo.portal.environment.Env; import com.ctrip.framework.apollo.portal.service.ItemService; +import com.ctrip.framework.apollo.portal.service.RolePermissionService; import com.ctrip.framework.apollo.portal.spi.UserInfoHolder; import com.google.gson.Gson; import javax.annotation.PostConstruct; + import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -48,6 +51,8 @@ import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestTemplate; +import java.util.Collections; + @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = {PortalApplication.class, ControllableAuthorizationConfiguration.class}, webEnvironment = WebEnvironment.RANDOM_PORT) @@ -68,6 +73,9 @@ public class ItemControllerAuthIntegrationTest { @Autowired private ItemService itemService; + @Autowired + private RolePermissionService rolePermissionService; + @PostConstruct private void postConstruct() { System.setProperty("spring.profiles.active", "test"); @@ -108,6 +116,7 @@ public void testCreateItemPermissionAccessed() { headers.set("Content-Type", "application/json"); HttpEntity entity = new HttpEntity<>(GSON.toJson(itemDTO), headers); + when(rolePermissionService.hasAnyPermission(eq("luke"), anyList())).thenReturn(true); restTemplate.postForEntity( url("/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/item"), diff --git a/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/controller/ItemControllerTest.java b/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/controller/ItemControllerTest.java index 600866b627e..b6bf3ddcc0a 100644 --- a/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/controller/ItemControllerTest.java +++ b/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/controller/ItemControllerTest.java @@ -18,6 +18,7 @@ import com.ctrip.framework.apollo.common.exception.BadRequestException; import com.ctrip.framework.apollo.core.enums.ConfigFileFormat; +import com.ctrip.framework.apollo.portal.component.UnifiedPermissionValidator; import com.ctrip.framework.apollo.portal.component.UserPermissionValidator; import com.ctrip.framework.apollo.portal.entity.model.NamespaceTextModel; import com.ctrip.framework.apollo.portal.service.ItemService; @@ -44,15 +45,15 @@ public class ItemControllerTest { @Mock private UserInfoHolder userInfoHolder; @Mock - private UserPermissionValidator userPermissionValidator; + private UnifiedPermissionValidator unifiedPermissionValidator; @InjectMocks private ItemController itemController; @Before public void setUp() throws Exception { - itemController = new ItemController(configService, userInfoHolder, userPermissionValidator, - namespaceService); + itemController = new ItemController(configService, userInfoHolder , + namespaceService,unifiedPermissionValidator); } @Test diff --git a/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/controller/PermissionControllerTest.java b/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/controller/PermissionControllerTest.java index 3f0436f00f5..2f49cdb99fb 100644 --- a/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/controller/PermissionControllerTest.java +++ b/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/controller/PermissionControllerTest.java @@ -29,9 +29,15 @@ import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.jdbc.Sql; +import java.util.Collections; + @ActiveProfiles("skipAuthorization") public class PermissionControllerTest extends AbstractIntegrationTest { @@ -47,6 +53,10 @@ public class PermissionControllerTest extends AbstractIntegrationTest { @Before public void setUp() { roleInitializationService.initClusterNamespaceRoles(appId, env, clusterName, "apollo"); + Authentication auth = new UsernamePasswordAuthenticationToken( + "test-user", null, Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")) + ); + SecurityContextHolder.getContext().setAuthentication(auth); } @Test diff --git a/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/filter/UserTypeResolverFilter.java b/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/filter/UserTypeResolverFilter.java new file mode 100644 index 00000000000..319795c6341 --- /dev/null +++ b/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/filter/UserTypeResolverFilter.java @@ -0,0 +1,41 @@ +/* + * Copyright 2025 Apollo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package com.ctrip.framework.apollo.portal.filter; + +import com.ctrip.framework.apollo.portal.component.UserIdentityContextHolder; +import com.ctrip.framework.apollo.portal.constant.UserIdentityConstants; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +public class UserTypeResolverFilter extends OncePerRequestFilter { + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { + if (request.getRequestURI().contains("openapi")) { + UserIdentityContextHolder.setAuthType(UserIdentityConstants.CONSUMER); + } else { + UserIdentityContextHolder.setAuthType(UserIdentityConstants.USER); + } + filterChain.doFilter(request, response); + } +} \ No newline at end of file diff --git a/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/service/ConfigsExportServiceTest.java b/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/service/ConfigsExportServiceTest.java index 217ac2d40dd..7c829a064fa 100644 --- a/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/service/ConfigsExportServiceTest.java +++ b/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/service/ConfigsExportServiceTest.java @@ -23,6 +23,7 @@ import com.ctrip.framework.apollo.common.entity.AppNamespace; import com.ctrip.framework.apollo.core.enums.ConfigFileFormat; import com.ctrip.framework.apollo.portal.AbstractUnitTest; +import com.ctrip.framework.apollo.portal.component.UnifiedPermissionValidator; import com.ctrip.framework.apollo.portal.component.UserPermissionValidator; import com.ctrip.framework.apollo.portal.entity.bo.ItemBO; import com.ctrip.framework.apollo.portal.entity.bo.NamespaceBO; @@ -81,6 +82,9 @@ public class ConfigsExportServiceTest extends AbstractUnitTest { @InjectMocks private ConfigsImportService configsImportService; + @Mock + private UnifiedPermissionValidator unifiedPermissionValidator; + @Test public void testNamespaceExportImport() throws FileNotFoundException { // Test with fillItemDetail = true @@ -156,6 +160,9 @@ private void testExportImportScenario(boolean fillItemDetail) throws FileNotFoun when(appService.findAll()).thenReturn(exportApps); when(appNamespaceService.findAll()).thenReturn(appNamespaces); when(userPermissionValidator.isAppAdmin(any())).thenReturn(true); + when(unifiedPermissionValidator.isAppAdmin(any())).thenReturn( true); + when(unifiedPermissionValidator.hasAssignRolePermission(anyString())).thenReturn(true); + when(unifiedPermissionValidator.isSuperAdmin()).thenReturn(true); when(clusterService.findClusters(env, appId1)).thenReturn(app1Clusters); when(clusterService.findClusters(env, appId2)).thenReturn(app2Clusters); when(namespaceService.findNamespaceBOs(appId1, Env.DEV, clusterName1, fillItemDetail, false)).thenReturn(app1Cluster1Namespace); @@ -182,6 +189,7 @@ private void testExportImportScenario(boolean fillItemDetail) throws FileNotFoun HttpStatusCodeException itemNotFoundException = new HttpClientErrorException(HttpStatus.NOT_FOUND); when(itemService.loadItem(any(), any(), any(), any(), anyString())).thenThrow(itemNotFoundException); + FileInputStream fileInputStream = new FileInputStream(filePath); ZipInputStream zipInputStream = new ZipInputStream(fileInputStream); diff --git a/apollo-portal/src/test/resources/sql/permission/consumer_role_permission_service/test_get_user_permission_set_different_users.sql b/apollo-portal/src/test/resources/sql/permission/consumer_role_permission_service/test_get_user_permission_set_different_users.sql new file mode 100644 index 00000000000..fdadd58b5a6 --- /dev/null +++ b/apollo-portal/src/test/resources/sql/permission/consumer_role_permission_service/test_get_user_permission_set_different_users.sql @@ -0,0 +1,41 @@ +-- +-- Copyright 2025 Apollo Authors +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +-- User 7's permissions: app:app2 +INSERT INTO "ConsumerRole" ("Id", "ConsumerId", "RoleId", "DataChange_CreatedBy", "DataChange_LastModifiedBy") +VALUES (1003, 7, 400, 'test-operator', 'test-operator'); + +INSERT INTO "RolePermission" ("RoleId", "PermissionId") +VALUES (400, 401); + +INSERT INTO "Permission" ("Id", "PermissionType", "TargetId") +VALUES (401, 'app', 'app2'); + +-- User 8's permissions: namespace:ns2, cluster:cluster2 +INSERT INTO "ConsumerRole" ("Id", "ConsumerId", "RoleId", "DataChange_CreatedBy", "DataChange_LastModifiedBy") +VALUES + (1004, 8, 500, 'test-operator', 'test-operator'), + (1005, 8, 501, 'test-operator', 'test-operator'); + +INSERT INTO "RolePermission" ("RoleId", "PermissionId") +VALUES + (500, 502), + (501, 503); + +INSERT INTO "Permission" ("Id", "PermissionType", "TargetId") +VALUES + (502, 'namespace', 'ns2'), + (503, 'cluster', 'cluster2'); \ No newline at end of file diff --git a/apollo-portal/src/test/resources/sql/permission/consumer_role_permission_service/test_get_user_permission_set_no_roles.sql b/apollo-portal/src/test/resources/sql/permission/consumer_role_permission_service/test_get_user_permission_set_no_roles.sql new file mode 100644 index 00000000000..2dd1a7516b0 --- /dev/null +++ b/apollo-portal/src/test/resources/sql/permission/consumer_role_permission_service/test_get_user_permission_set_no_roles.sql @@ -0,0 +1,19 @@ +-- +-- Copyright 2025 Apollo Authors +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +-- Ensure that user 4 has no role association records in the ConsumerRole table +-- (no data needs to be inserted, or explicitly delete possible interfering data) +DELETE FROM "ConsumerRole" WHERE "ConsumerId" = 4; \ No newline at end of file diff --git a/apollo-portal/src/test/resources/sql/permission/consumer_role_permission_service/test_get_user_permission_set_roles_without_permissions.sql b/apollo-portal/src/test/resources/sql/permission/consumer_role_permission_service/test_get_user_permission_set_roles_without_permissions.sql new file mode 100644 index 00000000000..76c46fbd593 --- /dev/null +++ b/apollo-portal/src/test/resources/sql/permission/consumer_role_permission_service/test_get_user_permission_set_roles_without_permissions.sql @@ -0,0 +1,22 @@ +-- +-- Copyright 2025 Apollo Authors +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +-- Insert role association for user 5 (role 100), but role 100 has no permissions +INSERT INTO "ConsumerRole" ("Id", "ConsumerId", "RoleId", "DataChange_CreatedBy", "DataChange_LastModifiedBy") +VALUES + (1000, 5, 100, 'test-operator', 'test-operator'); -- Insert role association record for user 5 + +-- Role 100 has no associated permissions in RolePermission table (no need to insert RolePermission records) \ No newline at end of file diff --git a/apollo-portal/src/test/resources/sql/permission/consumer_role_permission_service/test_get_user_permission_set_with_permissions.sql b/apollo-portal/src/test/resources/sql/permission/consumer_role_permission_service/test_get_user_permission_set_with_permissions.sql new file mode 100644 index 00000000000..7cdeb1ab0be --- /dev/null +++ b/apollo-portal/src/test/resources/sql/permission/consumer_role_permission_service/test_get_user_permission_set_with_permissions.sql @@ -0,0 +1,35 @@ +-- +-- Copyright 2025 Apollo Authors +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +-- Insert role associations for user 6 (roles 200 and 201) +INSERT INTO "ConsumerRole" ("Id", "ConsumerId", "RoleId", "DataChange_CreatedBy", "DataChange_LastModifiedBy") +VALUES + (1001, 6, 200, 'test-operator', 'test-operator'), + (1002, 6, 201, 'test-operator', 'test-operator'); + +-- Insert role-permission associations (role 200 associated with permission 300, role 201 associated with permissions 301 and 302) +INSERT INTO "RolePermission" ("RoleId", "PermissionId") -- Assuming RolePermission table structure is (RoleId, PermissionId) +VALUES + (200, 300), + (201, 301), + (201, 302); + +-- Insert permission details (permission table) +INSERT INTO "Permission" ("Id", "PermissionType", "TargetId") -- Assuming Permission table structure is (Id, PermissionType, TargetId) +VALUES + (300, 'app', 'app1'), + (301, 'namespace', 'ns1'), + (302, 'cluster', 'cluster1'); \ No newline at end of file diff --git a/apollo-portal/src/test/resources/sql/permission/insert-test-getUserPermissionSet.sql b/apollo-portal/src/test/resources/sql/permission/insert-test-getUserPermissionSet.sql new file mode 100644 index 00000000000..f55d3bcbb63 --- /dev/null +++ b/apollo-portal/src/test/resources/sql/permission/insert-test-getUserPermissionSet.sql @@ -0,0 +1,45 @@ +-- +-- Copyright 2025 Apollo Authors +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +-- Clean up possible legacy data (optional, will be done in cleanup.sql, written again here for safety) +DELETE FROM `RolePermission` WHERE `RoleId` IN (2001, 2002); +DELETE FROM `UserRole` WHERE `RoleId` IN (2001, 2002); +DELETE FROM `Permission` WHERE `Id` IN (1001, 1002); +DELETE FROM `Role` WHERE `Id` IN (2001, 2002); + +-- Permissions +INSERT INTO `Permission` (`Id`, `PermissionType`, `TargetId`, `IsDeleted`, `DeletedAt`, `DataChange_CreatedBy`, `DataChange_CreatedTime`, `DataChange_LastModifiedBy`, `DataChange_LastTime`) +VALUES + (1001, 'ModifyNamespace', 'someApp+someNamespace', 0, 0, 'test', NOW(), 'test', NOW()), + (1002, 'ReleaseNamespace', 'someApp+someNamespace', 0, 0, 'test', NOW(), 'test', NOW()); + +-- Roles +INSERT INTO `Role` (`Id`, `RoleName`, `IsDeleted`, `DeletedAt`, `DataChange_CreatedBy`, `DataChange_CreatedTime`, `DataChange_LastModifiedBy`, `DataChange_LastTime`) +VALUES + (2001, 'role-with-modify', 0, 0, 'test', NOW(), 'test', NOW()), + (2002, 'role-with-release', 0, 0, 'test', NOW(), 'test', NOW()); + +-- Role-Permission associations +INSERT INTO `RolePermission` (`RoleId`, `PermissionId`, `IsDeleted`, `DeletedAt`, `DataChange_CreatedBy`, `DataChange_CreatedTime`, `DataChange_LastModifiedBy`, `DataChange_LastTime`) +VALUES + (2001, 1001, 0, 0, 'test', NOW(), 'test', NOW()), + (2002, 1002, 0, 0, 'test', NOW(), 'test', NOW()); + +-- User-Role associations (apollo has two permissions, nobody has no roles) +INSERT INTO `UserRole` (`UserId`, `RoleId`, `IsDeleted`, `DeletedAt`, `DataChange_CreatedBy`, `DataChange_CreatedTime`, `DataChange_LastModifiedBy`, `DataChange_LastTime`) +VALUES + ('apollo', 2001, 0, 0, 'test', NOW(), 'test', NOW()), + ('apollo', 2002, 0, 0, 'test', NOW(), 'test', NOW()); \ No newline at end of file