package com.ctrip.framework.apollo.portal.controller; import com.google.common.collect.Sets; import com.ctrip.framework.apollo.common.dto.NamespaceDTO; import com.ctrip.framework.apollo.common.entity.AppNamespace; import com.ctrip.framework.apollo.common.exception.BadRequestException; import com.ctrip.framework.apollo.common.utils.InputValidator; import com.ctrip.framework.apollo.common.utils.RequestPrecondition; import com.ctrip.framework.apollo.core.enums.Env; import com.ctrip.framework.apollo.portal.component.config.PortalConfig; import com.ctrip.framework.apollo.portal.constant.RoleType; import com.ctrip.framework.apollo.portal.entity.bo.NamespaceBO; import com.ctrip.framework.apollo.portal.entity.model.NamespaceCreationModel; import com.ctrip.framework.apollo.portal.listener.AppNamespaceCreationEvent; import com.ctrip.framework.apollo.portal.service.AppNamespaceService; import com.ctrip.framework.apollo.portal.service.NamespaceService; import com.ctrip.framework.apollo.portal.service.RoleInitializationService; 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 com.ctrip.framework.apollo.tracer.Tracer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationEventPublisher; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.util.CollectionUtils; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.List; import java.util.Map; import static com.ctrip.framework.apollo.common.utils.RequestPrecondition.checkModel; @RestController public class NamespaceController { private static final Logger logger = LoggerFactory.getLogger(NamespaceController.class); @Autowired private ApplicationEventPublisher publisher; @Autowired private UserInfoHolder userInfoHolder; @Autowired private NamespaceService namespaceService; @Autowired private AppNamespaceService appNamespaceService; @Autowired private RoleInitializationService roleInitializationService; @Autowired private RolePermissionService rolePermissionService; @Autowired private PortalConfig portalConfig; @RequestMapping(value = "/appnamespaces/public", method = RequestMethod.GET) public List<AppNamespace> findPublicAppNamespaces() { return appNamespaceService.findPublicAppNamespaces(); } @RequestMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces", method = RequestMethod.GET) public List<NamespaceBO> findNamespaces(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName) { return namespaceService.findNamespaceBOs(appId, Env.valueOf(env), clusterName); } @RequestMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName:.+}", method = RequestMethod.GET) public NamespaceBO findNamespace(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName, @PathVariable String namespaceName) { return namespaceService.loadNamespaceBO(appId, Env.valueOf(env), clusterName, namespaceName); } @RequestMapping(value = "/envs/{env}/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/associated-public-namespace", method = RequestMethod.GET) public NamespaceBO findPublicNamespaceForAssociatedNamespace(@PathVariable String env, @PathVariable String appId, @PathVariable String namespaceName, @PathVariable String clusterName) { return namespaceService.findPublicNamespaceForAssociatedNamespace(Env.valueOf(env), appId, clusterName, namespaceName); } @PreAuthorize(value = "@permissionValidator.hasCreateNamespacePermission(#appId)") @RequestMapping(value = "/apps/{appId}/namespaces", method = RequestMethod.POST) public ResponseEntity<Void> createNamespace(@PathVariable String appId, @RequestBody List<NamespaceCreationModel> models) { checkModel(!CollectionUtils.isEmpty(models)); String namespaceName = models.get(0).getNamespace().getNamespaceName(); String operator = userInfoHolder.getUser().getUserId(); roleInitializationService.initNamespaceRoles(appId, namespaceName, operator); for (NamespaceCreationModel model : models) { NamespaceDTO namespace = model.getNamespace(); RequestPrecondition.checkArgumentsNotEmpty(model.getEnv(), namespace.getAppId(), namespace.getClusterName(), namespace.getNamespaceName()); try { namespaceService.createNamespace(Env.valueOf(model.getEnv()), namespace); } catch (Exception e) { logger.error("create namespace fail.", e); Tracer.logError( String.format("create namespace fail. (env=%s namespace=%s)", model.getEnv(), namespace.getNamespaceName()), e); } } assignNamespaceRoleToOperator(appId, namespaceName); return ResponseEntity.ok().build(); } @PreAuthorize(value = "@permissionValidator.hasDeleteNamespacePermission(#appId)") @RequestMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName:.+}", method = RequestMethod.DELETE) public ResponseEntity<Void> deleteNamespace(@PathVariable String appId, @PathVariable String env, @PathVariable String clusterName, @PathVariable String namespaceName) { namespaceService.deleteNamespace(appId, Env.valueOf(env), clusterName, namespaceName); return ResponseEntity.ok().build(); } @PreAuthorize(value = "@permissionValidator.hasCreateAppNamespacePermission(#appId, #appNamespace)") @RequestMapping(value = "/apps/{appId}/appnamespaces", method = RequestMethod.POST) public AppNamespace createAppNamespace(@PathVariable String appId, @RequestBody AppNamespace appNamespace) { RequestPrecondition.checkArgumentsNotEmpty(appNamespace.getAppId(), appNamespace.getName()); if (!InputValidator.isValidAppNamespace(appNamespace.getName())) { throw new BadRequestException(String.format("Namespace格式错误: %s", InputValidator.INVALID_CLUSTER_NAMESPACE_MESSAGE + " & " + InputValidator.INVALID_NAMESPACE_NAMESPACE_MESSAGE)); } AppNamespace createdAppNamespace = appNamespaceService.createAppNamespaceInLocal(appNamespace); if (portalConfig.canAppAdminCreatePrivateNamespace() || createdAppNamespace.isPublic()) { assignNamespaceRoleToOperator(appId, appNamespace.getName()); } publisher.publishEvent(new AppNamespaceCreationEvent(createdAppNamespace)); return createdAppNamespace; } /** * env -> cluster -> cluster has not published namespace? * Example: * dev -> * default -> true (default cluster has not published namespace) * customCluster -> false (customCluster cluster's all namespaces had published) */ @RequestMapping(value = "/apps/{appId}/namespaces/publish_info", method = RequestMethod.GET) public Map<String, Map<String, Boolean>> getNamespacesPublishInfo(@PathVariable String appId) { return namespaceService.getNamespacesPublishInfo(appId); } @RequestMapping(value = "/envs/{env}/appnamespaces/{publicNamespaceName}/namespaces", method = RequestMethod.GET) public List<NamespaceDTO> getPublicAppNamespaceAllNamespaces(@PathVariable String env, @PathVariable String publicNamespaceName, @RequestParam(name = "page", defaultValue = "0") int page, @RequestParam(name = "size", defaultValue = "10") int size) { return namespaceService.getPublicAppNamespaceAllNamespaces(Env.fromString(env), publicNamespaceName, page, size); } private void assignNamespaceRoleToOperator(String appId, String namespaceName) { //default assign modify、release namespace role to namespace creator String operator = userInfoHolder.getUser().getUserId(); rolePermissionService .assignRoleToUsers(RoleUtils.buildNamespaceRoleName(appId, namespaceName, RoleType.MODIFY_NAMESPACE), Sets.newHashSet(operator), operator); rolePermissionService .assignRoleToUsers(RoleUtils.buildNamespaceRoleName(appId, namespaceName, RoleType.RELEASE_NAMESPACE), Sets.newHashSet(operator), operator); } }