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 8630dcbd41b5ea4ec1be90485a9da46d45c22c30..8200964794eeeaa5ba987d807d99bb339254ee02 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 @@ -19,26 +19,25 @@ public class ConsumerPermissionValidator { private ConsumerAuthUtil consumerAuthUtil; - public boolean hasModifyNamespacePermission(HttpServletRequest request, String appId, String - namespaceName) { - + public boolean hasModifyNamespacePermission(HttpServletRequest request, String appId, String namespaceName, + String env) { if (hasCreateNamespacePermission(request, appId)) { return true; } return permissionService.consumerHasPermission(consumerAuthUtil.retrieveConsumerId(request), PermissionType.MODIFY_NAMESPACE, - RoleUtils.buildNamespaceTargetId(appId, namespaceName)); + RoleUtils.buildNamespaceTargetId(appId, namespaceName, env)); } - public boolean hasReleaseNamespacePermission(HttpServletRequest request, String appId, String - namespaceName) { + public boolean hasReleaseNamespacePermission(HttpServletRequest request, String appId, String namespaceName, + String env) { if (hasCreateNamespacePermission(request, appId)) { return true; } return permissionService.consumerHasPermission(consumerAuthUtil.retrieveConsumerId(request), PermissionType.RELEASE_NAMESPACE, - RoleUtils.buildNamespaceTargetId(appId, namespaceName)); + RoleUtils.buildNamespaceTargetId(appId, namespaceName, env)); } diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/service/ConsumerService.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/service/ConsumerService.java index a07d2c85d6829c742480fb94fe37714f46fe50a7..c52f5f453e49f670fb3692b37bf8ca3719f5b88c 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/service/ConsumerService.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/service/ConsumerService.java @@ -112,17 +112,21 @@ public class ConsumerService { return consumerRepository.findOne(consumerId); } - @Transactional public List assignNamespaceRoleToConsumer(String token, String appId, String namespaceName) { + return assignNamespaceRoleToConsumer(token, appId, namespaceName, null); + } + + @Transactional + public List assignNamespaceRoleToConsumer(String token, String appId, String namespaceName, String env) { Long consumerId = getConsumerIdByToken(token); if (consumerId == null) { throw new BadRequestException("Token is Illegal"); } Role namespaceModifyRole = - rolePermissionService.findRoleByRoleName(RoleUtils.buildModifyNamespaceRoleName(appId, namespaceName)); + rolePermissionService.findRoleByRoleName(RoleUtils.buildModifyNamespaceRoleName(appId, namespaceName, env)); Role namespaceReleaseRole = - rolePermissionService.findRoleByRoleName(RoleUtils.buildReleaseNamespaceRoleName(appId, namespaceName)); + rolePermissionService.findRoleByRoleName(RoleUtils.buildReleaseNamespaceRoleName(appId, namespaceName, env)); if (namespaceModifyRole == null || namespaceReleaseRole == null) { throw new BadRequestException("Namespace's role does not exist. Please check whether namespace has created."); 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 3c26be16692931a94e6db2477a28df086d426a31..61a2445f94e0de0ba08ddad1e83e86d1b119c3f8 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 @@ -32,7 +32,7 @@ public class ItemController { private UserService userService; - @PreAuthorize(value = "@consumerPermissionValidator.hasModifyNamespacePermission(#request, #appId, #namespaceName)") + @PreAuthorize(value = "@consumerPermissionValidator.hasModifyNamespacePermission(#request, #appId, #namespaceName, #env)") @RequestMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/items", method = RequestMethod.POST) public OpenItemDTO createItem(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName, @PathVariable String namespaceName, @@ -60,7 +60,7 @@ public class ItemController { return OpenApiBeanUtils.transformFromItemDTO(createdItem); } - @PreAuthorize(value = "@consumerPermissionValidator.hasModifyNamespacePermission(#request, #appId, #namespaceName)") + @PreAuthorize(value = "@consumerPermissionValidator.hasModifyNamespacePermission(#request, #appId, #namespaceName, #env)") @RequestMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/items/{key:.+}", method = RequestMethod.PUT) public void updateItem(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName, @PathVariable String namespaceName, @@ -91,7 +91,7 @@ public class ItemController { } - @PreAuthorize(value = "@consumerPermissionValidator.hasModifyNamespacePermission(#request, #appId, #namespaceName)") + @PreAuthorize(value = "@consumerPermissionValidator.hasModifyNamespacePermission(#request, #appId, #namespaceName, #env)") @RequestMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/items/{key:.+}", method = RequestMethod.DELETE) public void deleteItem(@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/ReleaseController.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/ReleaseController.java index de3d85ca83ace759900ceeb0b0e39ed60213f3de..2a5911dcf79825e0e5c38d2200e1924c426cf9bf 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 @@ -33,7 +33,7 @@ public class ReleaseController { @Autowired private UserService userService; - @PreAuthorize(value = "@consumerPermissionValidator.hasReleaseNamespacePermission(#request, #appId, #namespaceName)") + @PreAuthorize(value = "@consumerPermissionValidator.hasReleaseNamespacePermission(#request, #appId, #namespaceName, #env)") @RequestMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases", method = RequestMethod.POST) public OpenReleaseDTO createRelease(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName, 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 6a2fa26a68f7ac91542fd8293b3521dcd2205073..960799ed08183cb61b0bbc130a140027973cacec 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 @@ -6,7 +6,6 @@ import com.ctrip.framework.apollo.portal.constant.PermissionType; import com.ctrip.framework.apollo.portal.service.RolePermissionService; import com.ctrip.framework.apollo.portal.spi.UserInfoHolder; import com.ctrip.framework.apollo.portal.util.RoleUtils; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -22,14 +21,26 @@ public class PermissionValidator { public boolean hasModifyNamespacePermission(String appId, String namespaceName) { return rolePermissionService.userHasPermission(userInfoHolder.getUser().getUserId(), - PermissionType.MODIFY_NAMESPACE, - RoleUtils.buildNamespaceTargetId(appId, namespaceName)); + PermissionType.MODIFY_NAMESPACE, + RoleUtils.buildNamespaceTargetId(appId, namespaceName)); + } + + public boolean hasModifyNamespacePermission(String appId, String namespaceName, String env) { + return hasModifyNamespacePermission(appId, namespaceName) || + rolePermissionService.userHasPermission(userInfoHolder.getUser().getUserId(), + PermissionType.MODIFY_NAMESPACE, RoleUtils.buildNamespaceTargetId(appId, namespaceName, env)); } public boolean hasReleaseNamespacePermission(String appId, String namespaceName) { return rolePermissionService.userHasPermission(userInfoHolder.getUser().getUserId(), - PermissionType.RELEASE_NAMESPACE, - RoleUtils.buildNamespaceTargetId(appId, namespaceName)); + PermissionType.RELEASE_NAMESPACE, + RoleUtils.buildNamespaceTargetId(appId, namespaceName)); + } + + public boolean hasReleaseNamespacePermission(String appId, String namespaceName, String env) { + return hasReleaseNamespacePermission(appId, namespaceName) || + rolePermissionService.userHasPermission(userInfoHolder.getUser().getUserId(), + PermissionType.RELEASE_NAMESPACE, RoleUtils.buildNamespaceTargetId(appId, namespaceName, env)); } public boolean hasDeleteNamespacePermission(String appId) { @@ -40,17 +51,23 @@ public class PermissionValidator { return hasModifyNamespacePermission(appId, namespaceName) || hasReleaseNamespacePermission(appId, namespaceName); } + public boolean hasOperateNamespacePermission(String appId, String namespaceName, String env) { + return hasOperateNamespacePermission(appId, namespaceName) || + hasModifyNamespacePermission(appId, namespaceName, env) || + hasReleaseNamespacePermission(appId, namespaceName, env); + } + public boolean hasAssignRolePermission(String appId) { return rolePermissionService.userHasPermission(userInfoHolder.getUser().getUserId(), - PermissionType.ASSIGN_ROLE, - appId); + PermissionType.ASSIGN_ROLE, + appId); } public boolean hasCreateNamespacePermission(String appId) { return rolePermissionService.userHasPermission(userInfoHolder.getUser().getUserId(), - PermissionType.CREATE_NAMESPACE, - appId); + PermissionType.CREATE_NAMESPACE, + appId); } public boolean hasCreateAppNamespacePermission(String appId, AppNamespace appNamespace) { @@ -66,8 +83,8 @@ public class PermissionValidator { public boolean hasCreateClusterPermission(String appId) { return rolePermissionService.userHasPermission(userInfoHolder.getUser().getUserId(), - PermissionType.CREATE_CLUSTER, - appId); + PermissionType.CREATE_CLUSTER, + appId); } public boolean isAppAdmin(String appId) { diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/component/emailbuilder/ConfigPublishEmailBuilder.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/component/emailbuilder/ConfigPublishEmailBuilder.java index 2d3df536a6d26d76b2e02e57366ef8b593d40b0b..82cab8305f37bf79d562f2a9232f0e08330b0a3e 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/component/emailbuilder/ConfigPublishEmailBuilder.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/component/emailbuilder/ConfigPublishEmailBuilder.java @@ -104,7 +104,7 @@ public abstract class ConfigPublishEmailBuilder { email.setSubject(subject()); email.setSenderEmailAddress(portalConfig.emailSender()); - email.setRecipients(recipients(releaseHistory.getAppId(), releaseHistory.getNamespaceName())); + email.setRecipients(recipients(releaseHistory.getAppId(), releaseHistory.getNamespaceName(), env.toString())); String emailBody = emailContent(env, releaseHistory); //clear not used module @@ -208,13 +208,19 @@ public abstract class ConfigPublishEmailBuilder { return releaseService.compare(env, releaseHistory.getPreviousReleaseId(), releaseHistory.getReleaseId()); } - private List recipients(String appId, String namespaceName) { + private List recipients(String appId, String namespaceName, String env) { Set modifyRoleUsers = rolePermissionService .queryUsersWithRole(RoleUtils.buildNamespaceRoleName(appId, namespaceName, RoleType.MODIFY_NAMESPACE)); + Set envModifyRoleUsers = + rolePermissionService + .queryUsersWithRole(RoleUtils.buildNamespaceRoleName(appId, namespaceName, RoleType.MODIFY_NAMESPACE, env)); Set releaseRoleUsers = rolePermissionService .queryUsersWithRole(RoleUtils.buildNamespaceRoleName(appId, namespaceName, RoleType.RELEASE_NAMESPACE)); + Set envReleaseRoleUsers = + rolePermissionService + .queryUsersWithRole(RoleUtils.buildNamespaceRoleName(appId, namespaceName, RoleType.RELEASE_NAMESPACE, env)); Set owners = rolePermissionService.queryUsersWithRole(RoleUtils.buildAppMasterRoleName(appId)); Set userIds = new HashSet<>(modifyRoleUsers.size() + releaseRoleUsers.size() + owners.size()); @@ -223,10 +229,18 @@ public abstract class ConfigPublishEmailBuilder { userIds.add(userInfo.getUserId()); } + for (UserInfo userInfo : envModifyRoleUsers) { + userIds.add(userInfo.getUserId()); + } + for (UserInfo userInfo : releaseRoleUsers) { userIds.add(userInfo.getUserId()); } + for (UserInfo userInfo : envReleaseRoleUsers) { + userIds.add(userInfo.getUserId()); + } + for (UserInfo userInfo : owners) { userIds.add(userInfo.getUserId()); } 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 a9f725ae4b14c958dd83c1645acf3015dc458b18..6bdd80f6b98672fadbef973c9ede958bbf45a020 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 @@ -1,6 +1,9 @@ package com.ctrip.framework.apollo.portal.controller; +import com.ctrip.framework.apollo.core.ConfigConsts; +import com.ctrip.framework.apollo.portal.service.RoleInitializationService; + import com.ctrip.framework.apollo.common.entity.App; import com.ctrip.framework.apollo.common.exception.BadRequestException; import com.ctrip.framework.apollo.common.http.MultiResponseEntity; @@ -53,6 +56,8 @@ public class AppController { private ApplicationEventPublisher publisher; @Autowired private RolePermissionService rolePermissionService; + @Autowired + private RoleInitializationService roleInitializationService; @RequestMapping(value = "", method = RequestMethod.GET) public List findApps(@RequestParam(value = "appIds", required = false) String appIds) { @@ -132,6 +137,8 @@ public class AppController { appService.createAppInRemote(Env.valueOf(env), app); + roleInitializationService.initNamespaceSpecificEnvRoles(app.getAppId(), ConfigConsts.NAMESPACE_APPLICATION, env, userInfoHolder.getUser().getUserId()); + return ResponseEntity.ok().build(); } 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 767c8bdf3631712e55971fe618fe91f78a330389..84ba65d5dd8bbcd8aa283dc8a597c2e62586d1e8 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 @@ -2,12 +2,15 @@ package com.ctrip.framework.apollo.portal.controller; import com.ctrip.framework.apollo.common.dto.NamespaceDTO; import com.ctrip.framework.apollo.common.exception.BadRequestException; +import com.ctrip.framework.apollo.core.enums.EnvUtils; import com.ctrip.framework.apollo.core.utils.StringUtils; import com.ctrip.framework.apollo.openapi.entity.Consumer; import com.ctrip.framework.apollo.openapi.entity.ConsumerRole; import com.ctrip.framework.apollo.openapi.entity.ConsumerToken; import com.ctrip.framework.apollo.openapi.service.ConsumerService; +import com.google.common.base.Strings; +import com.google.common.collect.Lists; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.security.access.prepost.PreAuthorize; @@ -19,12 +22,7 @@ import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import java.util.Calendar; -import java.util.Collections; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.List; -import java.util.Objects; +import java.util.*; /** * @author Jason Song(song_s@ctrip.com) @@ -69,6 +67,7 @@ public class ConsumerController { @RequestMapping(value = "/consumers/{token}/assign-role", method = RequestMethod.POST) public List assignNamespaceRoleToConsumer(@PathVariable String token, @RequestParam String type, + @RequestParam(required = false) String envs, @RequestBody NamespaceDTO namespace) { String appId = namespace.getAppId(); @@ -77,13 +76,33 @@ public class ConsumerController { if (StringUtils.isEmpty(appId)) { throw new BadRequestException("Params(AppId) can not be empty."); } - if (Objects.equals("AppRole", type)) { return Collections.singletonList(consumerService.assignAppRoleToConsumer(token, appId)); } else { if (StringUtils.isEmpty(namespaceName)) { throw new BadRequestException("Params(NamespaceName) can not be empty."); } + if (null != envs){ + String[] envArray = envs.split(","); + List envList = Lists.newArrayList(); + // validate env parameter + for (String env : envArray) { + if (Strings.isNullOrEmpty(env)) { + continue; + } + if (null == EnvUtils.transformEnv(env)) { + throw new BadRequestException(String.format("env: %s is illegal", env)); + } + envList.add(env); + } + + List consumeRoles = new ArrayList<>(); + for (String env : envList) { + consumeRoles.addAll(consumerService.assignNamespaceRoleToConsumer(token, appId, namespaceName, env)); + } + return consumeRoles; + } + return consumerService.assignNamespaceRoleToConsumer(token, appId, namespaceName); } } 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 0939eb19cda38d0ef3218ee31b9e6a1bc6758a1d..514102326d3cf3f75eeba9e6440a471e3120865a 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 @@ -4,15 +4,18 @@ import com.ctrip.framework.apollo.common.dto.ItemDTO; import com.ctrip.framework.apollo.common.exception.BadRequestException; import com.ctrip.framework.apollo.core.enums.Env; import com.ctrip.framework.apollo.core.utils.StringUtils; +import com.ctrip.framework.apollo.portal.component.PermissionValidator; 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; +import com.ctrip.framework.apollo.portal.entity.vo.NamespaceIdentifier; import com.ctrip.framework.apollo.portal.service.ItemService; import com.ctrip.framework.apollo.portal.spi.UserInfoHolder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; @@ -34,8 +37,10 @@ public class ItemController { private ItemService configService; @Autowired private UserInfoHolder userInfoHolder; + @Autowired + private PermissionValidator permissionValidator; - @PreAuthorize(value = "@permissionValidator.hasModifyNamespacePermission(#appId, #namespaceName)") + @PreAuthorize(value = "@permissionValidator.hasModifyNamespacePermission(#appId, #namespaceName, #env)") @RequestMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/items", method = RequestMethod.PUT, consumes = { "application/json"}) public void modifyItemsByText(@PathVariable String appId, @PathVariable String env, @@ -52,7 +57,7 @@ public class ItemController { configService.updateConfigItemByText(model); } - @PreAuthorize(value = "@permissionValidator.hasModifyNamespacePermission(#appId, #namespaceName)") + @PreAuthorize(value = "@permissionValidator.hasModifyNamespacePermission(#appId, #namespaceName, #env)") @RequestMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/item", method = RequestMethod.POST) public ItemDTO createItem(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName, @PathVariable String namespaceName, @@ -71,7 +76,7 @@ public class ItemController { return configService.createItem(appId, Env.valueOf(env), clusterName, namespaceName, item); } - @PreAuthorize(value = "@permissionValidator.hasModifyNamespacePermission(#appId, #namespaceName)") + @PreAuthorize(value = "@permissionValidator.hasModifyNamespacePermission(#appId, #namespaceName, #env)") @RequestMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/item", method = RequestMethod.PUT) public void updateItem(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName, @PathVariable String namespaceName, @@ -85,7 +90,7 @@ public class ItemController { } - @PreAuthorize(value = "@permissionValidator.hasModifyNamespacePermission(#appId, #namespaceName)") + @PreAuthorize(value = "@permissionValidator.hasModifyNamespacePermission(#appId, #namespaceName, #env) ") @RequestMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/items/{itemId}", method = RequestMethod.DELETE) public void deleteItem(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName, @PathVariable String namespaceName, @@ -134,15 +139,32 @@ public class ItemController { return configService.compare(model.getSyncToNamespaces(), model.getSyncItems()); } - @PreAuthorize(value = "@permissionValidator.hasModifyNamespacePermission(#appId, #namespaceName)") @RequestMapping(value = "/apps/{appId}/namespaces/{namespaceName}/items", method = RequestMethod.PUT, consumes = { "application/json"}) public ResponseEntity update(@PathVariable String appId, @PathVariable String namespaceName, @RequestBody NamespaceSyncModel model) { checkModel(Objects.nonNull(model) && !model.isInvalid()); - - configService.syncItems(model.getSyncToNamespaces(), model.getSyncItems()); - return ResponseEntity.status(HttpStatus.OK).build(); + boolean hasPermission = permissionValidator.hasModifyNamespacePermission(appId, namespaceName); + Env envNoPermission = null; + // if uses has ModifyNamespace permission then he has permission + if (!hasPermission) { + // else check if user has every env's ModifyNamespace permission + hasPermission = true; + for (NamespaceIdentifier namespaceIdentifier : model.getSyncToNamespaces()) { + // once user has not one of the env's ModifyNamespace permission, then break the loop + hasPermission &= permissionValidator.hasModifyNamespacePermission(namespaceIdentifier.getAppId(), namespaceIdentifier.getNamespaceName(), namespaceIdentifier.getEnv().toString()); + if (!hasPermission) { + envNoPermission = namespaceIdentifier.getEnv(); + break; + } + } + } + if (hasPermission) { + configService.syncItems(model.getSyncToNamespaces(), model.getSyncItems()); + return ResponseEntity.status(HttpStatus.OK).build(); + } + else + throw new AccessDeniedException(String.format("您没有修改环境%s的权限", envNoPermission)); } private boolean isValidItem(ItemDTO item) { 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 ea003d19281b2a0e88d35412c504e17b3dfb137e..454255e847c52bb10a17d96fd9f6b2cfe56c6107 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 @@ -46,7 +46,7 @@ public class NamespaceBranchController { return namespaceBranchService.findBranch(appId, Env.valueOf(env), clusterName, namespaceName); } - @PreAuthorize(value = "@permissionValidator.hasModifyNamespacePermission(#appId, #namespaceName)") + @PreAuthorize(value = "@permissionValidator.hasModifyNamespacePermission(#appId, #namespaceName, #env)") @RequestMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/branches", method = RequestMethod.POST) public NamespaceDTO createBranch(@PathVariable String appId, @PathVariable String env, @@ -63,9 +63,9 @@ public class NamespaceBranchController { @PathVariable String namespaceName, @PathVariable String branchName) { - boolean canDelete = permissionValidator.hasReleaseNamespacePermission(appId, namespaceName) || - (permissionValidator.hasModifyNamespacePermission(appId, namespaceName) && - releaseService.loadLatestRelease(appId, Env.valueOf(env), branchName, namespaceName) == null); + boolean canDelete = permissionValidator.hasReleaseNamespacePermission(appId, namespaceName, env) || + (permissionValidator.hasModifyNamespacePermission(appId, namespaceName, env) && + releaseService.loadLatestRelease(appId, Env.valueOf(env), branchName, namespaceName) == null); if (!canDelete) { @@ -81,7 +81,7 @@ public class NamespaceBranchController { - @PreAuthorize(value = "@permissionValidator.hasReleaseNamespacePermission(#appId, #namespaceName)") + @PreAuthorize(value = "@permissionValidator.hasReleaseNamespacePermission(#appId, #namespaceName, #env)") @RequestMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/branches/{branchName}/merge", method = RequestMethod.POST) public ReleaseDTO merge(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName, @PathVariable String namespaceName, @@ -120,7 +120,7 @@ public class NamespaceBranchController { } - @PreAuthorize(value = "@permissionValidator.hasOperateNamespacePermission(#appId, #namespaceName)") + @PreAuthorize(value = "@permissionValidator.hasOperateNamespacePermission(#appId, #namespaceName, #env)") @RequestMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/branches/{branchName}/rules", method = RequestMethod.PUT) 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/portal/controller/NamespaceController.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/NamespaceController.java index d21ef02c207245cf498f34f19e0c5c6006890ba1..a86e09d086d8ac7ae05d0fda95a4b0da9730b739 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 @@ -104,6 +104,7 @@ public class NamespaceController { String operator = userInfoHolder.getUser().getUserId(); roleInitializationService.initNamespaceRoles(appId, namespaceName, operator); + roleInitializationService.initNamespaceEnvRoles(appId, namespaceName, operator); for (NamespaceCreationModel model : models) { NamespaceDTO namespace = model.getNamespace(); 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 50958b8ff28ba025e88df6eda944dabacc416dad..73ddce86285ab191b215ab939f98b6cf2019d1c8 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 @@ -1,5 +1,9 @@ package com.ctrip.framework.apollo.portal.controller; +import com.ctrip.framework.apollo.core.enums.Env; +import com.ctrip.framework.apollo.core.enums.EnvUtils; +import com.ctrip.framework.apollo.portal.entity.vo.NamespaceEnvRolesAssignedUsers; +import com.ctrip.framework.apollo.portal.service.RoleInitializationService; import com.google.common.collect.Sets; import com.ctrip.framework.apollo.common.exception.BadRequestException; @@ -37,6 +41,14 @@ public class PermissionController { private RolePermissionService rolePermissionService; @Autowired private UserService userService; + @Autowired + private RoleInitializationService roleInitializationService; + + @RequestMapping(value = "/apps/{appId}/initPermission", method = RequestMethod.POST) + public ResponseEntity initAppPermission(@PathVariable String appId, @RequestBody String namespaceName) { + roleInitializationService.initNamespaceEnvRoles(appId, namespaceName, userInfoHolder.getUser().getUserId()); + return ResponseEntity.ok().build(); + } @RequestMapping(value = "/apps/{appId}/permissions/{permissionType}", method = RequestMethod.GET) public ResponseEntity hasPermission(@PathVariable String appId, @PathVariable String permissionType) { @@ -60,6 +72,18 @@ public class PermissionController { return ResponseEntity.ok().body(permissionCondition); } + @RequestMapping(value = "/apps/{appId}/envs/{env}/namespaces/{namespaceName}/permissions/{permissionType}", method = RequestMethod.GET) + public ResponseEntity hasPermission(@PathVariable String appId, @PathVariable String env, @PathVariable String namespaceName, + @PathVariable String permissionType) { + PermissionCondition permissionCondition = new PermissionCondition(); + + permissionCondition.setHasPermission( + rolePermissionService.userHasPermission(userInfoHolder.getUser().getUserId(), permissionType, + RoleUtils.buildNamespaceTargetId(appId, namespaceName, env))); + + return ResponseEntity.ok().body(permissionCondition); + } + @RequestMapping(value = "/permissions/root", method = RequestMethod.GET) public ResponseEntity hasRootPermission() { PermissionCondition permissionCondition = new PermissionCondition(); @@ -70,6 +94,72 @@ public class PermissionController { } + @RequestMapping(value = "/apps/{appId}/envs/{env}/namespaces/{namespaceName}/role_users", method = RequestMethod.GET) + public NamespaceEnvRolesAssignedUsers getNamespaceEnvRoles(@PathVariable String appId, @PathVariable String env, @PathVariable String namespaceName) { + + // validate env parameter + if (null == EnvUtils.transformEnv(env)) { + throw new BadRequestException("env is illegal"); + } + + NamespaceEnvRolesAssignedUsers assignedUsers = new NamespaceEnvRolesAssignedUsers(); + assignedUsers.setNamespaceName(namespaceName); + assignedUsers.setAppId(appId); + assignedUsers.setEnv(Env.fromString(env)); + + Set releaseNamespaceUsers = + rolePermissionService.queryUsersWithRole(RoleUtils.buildReleaseNamespaceRoleName(appId, namespaceName, env)); + assignedUsers.setReleaseRoleUsers(releaseNamespaceUsers); + + Set modifyNamespaceUsers = + rolePermissionService.queryUsersWithRole(RoleUtils.buildModifyNamespaceRoleName(appId, namespaceName, env)); + assignedUsers.setModifyRoleUsers(modifyNamespaceUsers); + + return assignedUsers; + } + + @PreAuthorize(value = "@permissionValidator.hasAssignRolePermission(#appId)") + @RequestMapping(value = "/apps/{appId}/envs/{env}/namespaces/{namespaceName}/roles/{roleType}", method = RequestMethod.POST) + public ResponseEntity assignNamespaceEnvRoleToUser(@PathVariable String appId, @PathVariable String env, @PathVariable String namespaceName, + @PathVariable String roleType, @RequestBody String user) { + checkUserExists(user); + RequestPrecondition.checkArgumentsNotEmpty(user); + + if (!RoleType.isValidRoleType(roleType)) { + throw new BadRequestException("role type is illegal"); + } + + // validate env parameter + if (null == EnvUtils.transformEnv(env)) { + throw new BadRequestException("env is illegal"); + } + Set assignedUser = rolePermissionService.assignRoleToUsers(RoleUtils.buildNamespaceRoleName(appId, namespaceName, roleType, env), + Sets.newHashSet(user), userInfoHolder.getUser().getUserId()); + if (CollectionUtils.isEmpty(assignedUser)) { + throw new BadRequestException(user + "已授权"); + } + + return ResponseEntity.ok().build(); + } + + @PreAuthorize(value = "@permissionValidator.hasAssignRolePermission(#appId)") + @RequestMapping(value = "/apps/{appId}/envs/{env}/namespaces/{namespaceName}/roles/{roleType}", method = RequestMethod.DELETE) + public ResponseEntity removeNamespaceEnvRoleFromUser(@PathVariable String appId, @PathVariable String env, @PathVariable String namespaceName, + @PathVariable String roleType, @RequestParam String user) { + RequestPrecondition.checkArgumentsNotEmpty(user); + + if (!RoleType.isValidRoleType(roleType)) { + throw new BadRequestException("role type is illegal"); + } + // validate env parameter + if (null == EnvUtils.transformEnv(env)) { + throw new BadRequestException("env is illegal"); + } + rolePermissionService.removeRoleFromUsers(RoleUtils.buildNamespaceRoleName(appId, namespaceName, roleType, env), + Sets.newHashSet(user), userInfoHolder.getUser().getUserId()); + return ResponseEntity.ok().build(); + } + @RequestMapping(value = "/apps/{appId}/namespaces/{namespaceName}/role_users", method = RequestMethod.GET) public NamespaceRolesAssignedUsers getNamespaceRoles(@PathVariable String appId, @PathVariable String namespaceName) { 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 15ba981864f510e063959b874f984dd5aa9cb350..6570efca3ca533296bad1424b53e38c4a2ed199d 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 @@ -36,7 +36,7 @@ public class ReleaseController { @Autowired private PortalConfig portalConfig; - @PreAuthorize(value = "@permissionValidator.hasReleaseNamespacePermission(#appId, #namespaceName)") + @PreAuthorize(value = "@permissionValidator.hasReleaseNamespacePermission(#appId, #namespaceName, #env)") @RequestMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/releases", method = RequestMethod.POST) public ReleaseDTO createRelease(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName, @@ -67,7 +67,7 @@ public class ReleaseController { return createdRelease; } - @PreAuthorize(value = "@permissionValidator.hasReleaseNamespacePermission(#appId, #namespaceName)") + @PreAuthorize(value = "@permissionValidator.hasReleaseNamespacePermission(#appId, #namespaceName, #env)") @RequestMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/branches/{branchName}/releases", method = RequestMethod.POST) public ReleaseDTO createGrayRelease(@PathVariable String appId, @@ -138,6 +138,7 @@ public class ReleaseController { } + @PreAuthorize(value = "@permissionValidator.hasReleaseNamespacePermission(#appId, #namespaceName, #env)") @RequestMapping(path = "/envs/{env}/releases/{releaseId}/rollback", method = RequestMethod.PUT) public void rollback(@PathVariable String env, @PathVariable long releaseId) { diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/entity/vo/NamespaceEnvRolesAssignedUsers.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/entity/vo/NamespaceEnvRolesAssignedUsers.java new file mode 100644 index 0000000000000000000000000000000000000000..99ed2e3b7771a008883006965212e549961b26c5 --- /dev/null +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/entity/vo/NamespaceEnvRolesAssignedUsers.java @@ -0,0 +1,15 @@ +package com.ctrip.framework.apollo.portal.entity.vo; + +import com.ctrip.framework.apollo.core.enums.Env; + +public class NamespaceEnvRolesAssignedUsers extends NamespaceRolesAssignedUsers { + private Env env; + + public Env getEnv() { + return env; + } + + public void setEnv(Env env) { + this.env = env; + } +} diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/AppNamespaceService.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/AppNamespaceService.java index 03005e1838dd09d5f7b01aefdd7541b1ebab3d54..a224d9d782b339c43d19f5d1a1c9b8254a51d323 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/AppNamespaceService.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/AppNamespaceService.java @@ -116,6 +116,7 @@ public class AppNamespaceService { AppNamespace createdAppNamespace = appNamespaceRepository.save(appNamespace); roleInitializationService.initNamespaceRoles(appNamespace.getAppId(), appNamespace.getName(), operator); + roleInitializationService.initNamespaceEnvRoles(appNamespace.getAppId(), appNamespace.getName(), operator); return createdAppNamespace; } diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/RoleInitializationService.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/RoleInitializationService.java index 14697210880952db9a50c68173af3eff71de2843..e43be4fe866ebc904a1a7b13a79c32d209de18fa 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/RoleInitializationService.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/RoleInitializationService.java @@ -8,4 +8,8 @@ public interface RoleInitializationService { public void initNamespaceRoles(String appId, String namespaceName, String operator); + public void initNamespaceEnvRoles(String appId, String namespaceName, String operator); + + public void initNamespaceSpecificEnvRoles(String appId, String namespaceName, String env, String operator); + } diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/spi/defaultimpl/DefaultRoleInitializationService.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/spi/defaultimpl/DefaultRoleInitializationService.java index f7ef7a18faf8a0a197ac243e7343b53d422424ee..71d911dab912070e2c609cffc083e7b1d8adbe80 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/spi/defaultimpl/DefaultRoleInitializationService.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/spi/defaultimpl/DefaultRoleInitializationService.java @@ -1,5 +1,7 @@ package com.ctrip.framework.apollo.portal.spi.defaultimpl; +import com.ctrip.framework.apollo.core.enums.Env; +import com.ctrip.framework.apollo.portal.component.config.PortalConfig; import com.google.common.collect.FluentIterable; import com.google.common.collect.Lists; import com.google.common.collect.Sets; @@ -18,7 +20,7 @@ import com.ctrip.framework.apollo.portal.util.RoleUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; -import java.util.Set; +import java.util.*; /** * Created by timothy on 2017/4/26. @@ -29,6 +31,8 @@ public class DefaultRoleInitializationService implements RoleInitializationServi private UserInfoHolder userInfoHolder; @Autowired private RolePermissionService rolePermissionService; + @Autowired + private PortalConfig portalConfig; @Transactional public void initAppRoles(App app) { @@ -47,9 +51,10 @@ public class DefaultRoleInitializationService implements RoleInitializationServi //assign master role to user rolePermissionService .assignRoleToUsers(RoleUtils.buildAppMasterRoleName(appId), Sets.newHashSet(app.getOwnerName()), - operator); + operator); initNamespaceRoles(appId, ConfigConsts.NAMESPACE_APPLICATION, operator); + initNamespaceEnvRoles(appId, ConfigConsts.NAMESPACE_APPLICATION, operator); //assign modify、release namespace role to user rolePermissionService.assignRoleToUsers( @@ -67,13 +72,37 @@ public class DefaultRoleInitializationService implements RoleInitializationServi String modifyNamespaceRoleName = RoleUtils.buildModifyNamespaceRoleName(appId, namespaceName); if (rolePermissionService.findRoleByRoleName(modifyNamespaceRoleName) == null) { createNamespaceRole(appId, namespaceName, PermissionType.MODIFY_NAMESPACE, - RoleUtils.buildModifyNamespaceRoleName(appId, namespaceName), operator); + modifyNamespaceRoleName, operator); } String releaseNamespaceRoleName = RoleUtils.buildReleaseNamespaceRoleName(appId, namespaceName); if (rolePermissionService.findRoleByRoleName(releaseNamespaceRoleName) == null) { createNamespaceRole(appId, namespaceName, PermissionType.RELEASE_NAMESPACE, - RoleUtils.buildReleaseNamespaceRoleName(appId, namespaceName), operator); + releaseNamespaceRoleName, operator); + } + } + + @Transactional + public void initNamespaceEnvRoles(String appId, String namespaceName, String operator) { + List portalEnvs = portalConfig.portalSupportedEnvs(); + + for (Env env : portalEnvs) { + initNamespaceSpecificEnvRoles(appId, namespaceName, env.toString(), operator); + } + } + + @Transactional + public void initNamespaceSpecificEnvRoles(String appId, String namespaceName, String env, String operator) { + String modifyNamespaceEnvRoleName = RoleUtils.buildModifyNamespaceRoleName(appId, namespaceName, env); + if (rolePermissionService.findRoleByRoleName(modifyNamespaceEnvRoleName) == null) { + createNamespaceEnvRole(appId, namespaceName, PermissionType.MODIFY_NAMESPACE, env, + modifyNamespaceEnvRoleName, operator); + } + + String releaseNamespaceEnvRoleName = RoleUtils.buildReleaseNamespaceRoleName(appId, namespaceName, env); + if (rolePermissionService.findRoleByRoleName(releaseNamespaceEnvRoleName) == null) { + createNamespaceEnvRole(appId, namespaceName, PermissionType.RELEASE_NAMESPACE, env, + releaseNamespaceEnvRoleName, operator); } } @@ -121,4 +150,15 @@ public class DefaultRoleInitializationService implements RoleInitializationServi rolePermissionService .createRoleWithPermissions(role, Sets.newHashSet(createdPermission.getId())); } + + private void createNamespaceEnvRole(String appId, String namespaceName, String permissionType, String env, + String roleName, String operator) { + Permission permission = + createPermission(RoleUtils.buildNamespaceTargetId(appId, namespaceName, env), permissionType, operator); + Permission createdPermission = rolePermissionService.createPermission(permission); + + Role role = createRole(roleName, operator); + rolePermissionService + .createRoleWithPermissions(role, Sets.newHashSet(createdPermission.getId())); + } } diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/util/RoleUtils.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/util/RoleUtils.java index b43842324546214eaeb69103c1a06baad126e4d7..f1ab7f457d084fdd856017f9d75d3836afa77a51 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/util/RoleUtils.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/util/RoleUtils.java @@ -7,7 +7,7 @@ import com.ctrip.framework.apollo.portal.constant.RoleType; public class RoleUtils { - private static final Joiner STRING_JOINER = Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR); + private static final Joiner STRING_JOINER = Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR).skipNulls(); public static String buildAppMasterRoleName(String appId) { return STRING_JOINER.join(RoleType.MASTER, appId); @@ -18,7 +18,11 @@ public class RoleUtils { } public static String buildModifyNamespaceRoleName(String appId, String namespaceName) { - return STRING_JOINER.join(RoleType.MODIFY_NAMESPACE, appId, namespaceName); + return buildModifyNamespaceRoleName(appId, namespaceName, null); + } + + public static String buildModifyNamespaceRoleName(String appId, String namespaceName, String env) { + return STRING_JOINER.join(RoleType.MODIFY_NAMESPACE, appId, namespaceName, env); } public static String buildModifyDefaultNamespaceRoleName(String appId) { @@ -26,11 +30,19 @@ public class RoleUtils { } public static String buildReleaseNamespaceRoleName(String appId, String namespaceName) { - return STRING_JOINER.join(RoleType.RELEASE_NAMESPACE, appId, namespaceName); + return buildReleaseNamespaceRoleName(appId, namespaceName, null); + } + + public static String buildReleaseNamespaceRoleName(String appId, String namespaceName, String env) { + return STRING_JOINER.join(RoleType.RELEASE_NAMESPACE, appId, namespaceName, env); } public static String buildNamespaceRoleName(String appId, String namespaceName, String roleType) { - return STRING_JOINER.join(roleType, appId, namespaceName); + return buildNamespaceRoleName(appId, namespaceName, roleType, null); + } + + public static String buildNamespaceRoleName(String appId, String namespaceName, String roleType, String env) { + return STRING_JOINER.join(roleType, appId, namespaceName, env); } public static String buildReleaseDefaultNamespaceRoleName(String appId) { @@ -38,7 +50,11 @@ public class RoleUtils { } public static String buildNamespaceTargetId(String appId, String namespaceName) { - return STRING_JOINER.join(appId, namespaceName); + return buildNamespaceTargetId(appId, namespaceName, null); + } + + public static String buildNamespaceTargetId(String appId, String namespaceName, String env) { + return STRING_JOINER.join(appId, namespaceName, env); } public static String buildDefaultNamespaceTargetId(String appId) { diff --git a/apollo-portal/src/main/resources/static/namespace/role.html b/apollo-portal/src/main/resources/static/namespace/role.html index 59d40ddb9e5938c291682cf105698c9b338133ed..15c6b8d003a3afd83661a8099928c99be48c0490 100644 --- a/apollo-portal/src/main/resources/static/namespace/role.html +++ b/apollo-portal/src/main/resources/static/namespace/role.html @@ -40,16 +40,30 @@
+
- +
所有环境
+
+
+
+
{{env}}
+
+ +
@@ -69,21 +83,35 @@
- +
+
所有环境
+
+
{{env}}
+
+ + +
+
diff --git a/apollo-portal/src/main/resources/static/open/manage.html b/apollo-portal/src/main/resources/static/open/manage.html index 9fd0f52e7d3b025a7b235a9ca6c8174524c09577..bec50baebe6c2a9de0858056db03b97bb84f1658 100644 --- a/apollo-portal/src/main/resources/static/open/manage.html +++ b/apollo-portal/src/main/resources/static/open/manage.html @@ -135,6 +135,20 @@ +
+ +
+
+ +
+ (不选择则所有环境都有权限,如果提示Namespace's role does not exist,请先打开该Namespace的授权页面触发一下权限的初始化动作) +
+