瀏覽代碼

apollo-portal to support displaying configs only to team members, users could control this behavior by configuring configView.memberOnly.envs in ApolloPortalDB.ServerConfig.

nobodyiam 6 年之前
父節點
當前提交
1794d3d2d3
共有 15 個文件被更改,包括 145 次插入14 次删除
  1. 19 0
      apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/component/PermissionValidator.java
  2. 12 0
      apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/component/config/PortalConfig.java
  3. 8 0
      apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/CommitController.java
  4. 21 1
      apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ItemController.java
  5. 7 1
      apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/NamespaceBranchController.java
  6. 19 2
      apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/NamespaceController.java
  7. 8 0
      apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ReleaseController.java
  8. 8 0
      apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ReleaseHistoryController.java
  9. 15 1
      apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/entity/bo/NamespaceBO.java
  10. 4 3
      apollo-portal/src/main/resources/static/config/history.html
  11. 3 0
      apollo-portal/src/main/resources/static/scripts/controller/config/ReleaseHistoryController.js
  12. 7 2
      apollo-portal/src/main/resources/static/views/component/namespace-panel-branch-tab.html
  13. 9 2
      apollo-portal/src/main/resources/static/views/component/namespace-panel-master-tab.html
  14. 2 1
      scripts/apollo-on-kubernetes/db/portal-db/apolloportaldb.sql
  15. 3 1
      scripts/sql/apolloportaldb.sql

+ 19 - 0
apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/component/PermissionValidator.java

@@ -3,6 +3,7 @@ package com.ctrip.framework.apollo.portal.component;
 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.service.AppNamespaceService;
 import com.ctrip.framework.apollo.portal.service.RolePermissionService;
 import com.ctrip.framework.apollo.portal.spi.UserInfoHolder;
 import com.ctrip.framework.apollo.portal.util.RoleUtils;
@@ -18,6 +19,8 @@ public class PermissionValidator {
   private RolePermissionService rolePermissionService;
   @Autowired
   private PortalConfig portalConfig;
+  @Autowired
+  private AppNamespaceService appNamespaceService;
 
   public boolean hasModifyNamespacePermission(String appId, String namespaceName) {
     return rolePermissionService.userHasPermission(userInfoHolder.getUser().getUserId(),
@@ -94,4 +97,20 @@ public class PermissionValidator {
   public boolean isSuperAdmin() {
     return rolePermissionService.isSuperAdmin(userInfoHolder.getUser().getUserId());
   }
+
+  public boolean shouldHideConfigToCurrentUser(String appId, String env, String namespaceName) {
+    // 1. check whether the current environment enables member only function
+    if (!portalConfig.isConfigViewMemberOnly(env)) {
+      return false;
+    }
+
+    // 2. public namespace is open to every one
+    AppNamespace appNamespace = appNamespaceService.findByAppIdAndName(appId, namespaceName);
+    if (appNamespace != null && appNamespace.isPublic()) {
+      return false;
+    }
+
+    // 3. check app admin and operate permissions
+    return !isAppAdmin(appId) && !hasOperateNamespacePermission(appId, namespaceName, env);
+  }
 }

+ 12 - 0
apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/component/config/PortalConfig.java

@@ -74,6 +74,18 @@ public class PortalConfig extends RefreshableConfig {
     return result;
   }
 
+  public boolean isConfigViewMemberOnly(String env) {
+    String[] configViewMemberOnlyEnvs = getArrayProperty("configView.memberOnly.envs", new String[0]);
+
+    for (String memberOnlyEnv : configViewMemberOnlyEnvs) {
+      if (memberOnlyEnv.equalsIgnoreCase(env)) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
   /***
    * Level: normal
    **/

+ 8 - 0
apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/CommitController.java

@@ -3,8 +3,10 @@ package com.ctrip.framework.apollo.portal.controller;
 import com.ctrip.framework.apollo.common.dto.CommitDTO;
 import com.ctrip.framework.apollo.common.utils.RequestPrecondition;
 import com.ctrip.framework.apollo.core.enums.Env;
+import com.ctrip.framework.apollo.portal.component.PermissionValidator;
 import com.ctrip.framework.apollo.portal.service.CommitService;
 
+import java.util.Collections;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -21,10 +23,16 @@ public class CommitController {
   @Autowired
   private CommitService commitService;
 
+  @Autowired
+  private PermissionValidator permissionValidator;
+
   @RequestMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/commits", method = RequestMethod.GET)
   public List<CommitDTO> find(@PathVariable String appId, @PathVariable String env,
                               @PathVariable String clusterName, @PathVariable String namespaceName,
                               @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size) {
+    if (permissionValidator.shouldHideConfigToCurrentUser(appId, env, namespaceName)) {
+      return Collections.emptyList();
+    }
 
     RequestPrecondition.checkNumberPositive(size);
     RequestPrecondition.checkNumberNotNegative(page);

+ 21 - 1
apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ItemController.java

@@ -1,5 +1,6 @@
 package com.ctrip.framework.apollo.portal.controller;
 
+import com.ctrip.framework.apollo.common.dto.ItemChangeSets;
 import com.ctrip.framework.apollo.common.dto.ItemDTO;
 import com.ctrip.framework.apollo.common.exception.BadRequestException;
 import com.ctrip.framework.apollo.core.enums.Env;
@@ -107,6 +108,10 @@ public class ItemController {
                                  @PathVariable String clusterName, @PathVariable String namespaceName,
                                  @RequestParam(defaultValue = "lineNum") String orderBy) {
 
+    if (permissionValidator.shouldHideConfigToCurrentUser(appId, env, namespaceName)) {
+      return Collections.emptyList();
+    }
+
     List<ItemDTO> items = configService.findItems(appId, Env.valueOf(env), clusterName, namespaceName);
     if ("lastModifiedTime".equals(orderBy)) {
       Collections.sort(items, (o1, o2) -> {
@@ -136,7 +141,22 @@ public class ItemController {
   public List<ItemDiffs> diff(@RequestBody NamespaceSyncModel model) {
     checkModel(Objects.nonNull(model) && !model.isInvalid());
 
-    return configService.compare(model.getSyncToNamespaces(), model.getSyncItems());
+    List<ItemDiffs> itemDiffs = configService.compare(model.getSyncToNamespaces(), model.getSyncItems());
+
+    for (ItemDiffs diff : itemDiffs) {
+      NamespaceIdentifier namespace = diff.getNamespace();
+      if (namespace == null) {
+        continue;
+      }
+
+      if (permissionValidator
+          .shouldHideConfigToCurrentUser(namespace.getAppId(), namespace.getEnv().name(), namespace.getNamespaceName())) {
+        diff.setDiffs(new ItemChangeSets());
+        diff.setExtInfo("您不是该项目的管理员,也没有该Namespace在 " + namespace.getEnv() +  " 环境的编辑或发布权限");
+      }
+    }
+
+    return itemDiffs;
   }
 
   @RequestMapping(value = "/apps/{appId}/namespaces/{namespaceName}/items", method = RequestMethod.PUT, consumes = {

+ 7 - 1
apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/NamespaceBranchController.java

@@ -43,7 +43,13 @@ public class NamespaceBranchController {
                                 @PathVariable String env,
                                 @PathVariable String clusterName,
                                 @PathVariable String namespaceName) {
-    return namespaceBranchService.findBranch(appId, Env.valueOf(env), clusterName, namespaceName);
+    NamespaceBO namespaceBO = namespaceBranchService.findBranch(appId, Env.valueOf(env), clusterName, namespaceName);
+
+    if (namespaceBO != null && permissionValidator.shouldHideConfigToCurrentUser(appId, env, namespaceName)) {
+      namespaceBO.hideItems();
+    }
+
+    return namespaceBO;
   }
 
   @PreAuthorize(value = "@permissionValidator.hasModifyNamespacePermission(#appId, #namespaceName, #env)")

+ 19 - 2
apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/NamespaceController.java

@@ -2,6 +2,7 @@ package com.ctrip.framework.apollo.portal.controller;
 
 import com.ctrip.framework.apollo.common.dto.AppNamespaceDTO;
 import com.ctrip.framework.apollo.common.utils.BeanUtils;
+import com.ctrip.framework.apollo.portal.component.PermissionValidator;
 import com.ctrip.framework.apollo.portal.listener.AppNamespaceDeletionEvent;
 import com.google.common.collect.Sets;
 
@@ -62,6 +63,8 @@ public class NamespaceController {
   private RolePermissionService rolePermissionService;
   @Autowired
   private PortalConfig portalConfig;
+  @Autowired
+  private PermissionValidator permissionValidator;
 
 
   @RequestMapping(value = "/appnamespaces/public", method = RequestMethod.GET)
@@ -73,14 +76,28 @@ public class NamespaceController {
   public List<NamespaceBO> findNamespaces(@PathVariable String appId, @PathVariable String env,
                                           @PathVariable String clusterName) {
 
-    return namespaceService.findNamespaceBOs(appId, Env.valueOf(env), clusterName);
+    List<NamespaceBO> namespaceBOs = namespaceService.findNamespaceBOs(appId, Env.valueOf(env), clusterName);
+
+    for (NamespaceBO namespaceBO : namespaceBOs) {
+      if (permissionValidator.shouldHideConfigToCurrentUser(appId, env, namespaceBO.getBaseInfo().getNamespaceName())) {
+        namespaceBO.hideItems();
+      }
+    }
+
+    return namespaceBOs;
   }
 
   @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);
+    NamespaceBO namespaceBO = namespaceService.loadNamespaceBO(appId, Env.valueOf(env), clusterName, namespaceName);
+
+    if (namespaceBO != null && permissionValidator.shouldHideConfigToCurrentUser(appId, env, namespaceName)) {
+      namespaceBO.hideItems();
+    }
+
+    return namespaceBO;
   }
 
   @RequestMapping(value = "/envs/{env}/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/associated-public-namespace",

+ 8 - 0
apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ReleaseController.java

@@ -13,6 +13,7 @@ import com.ctrip.framework.apollo.portal.entity.bo.ReleaseBO;
 import com.ctrip.framework.apollo.portal.listener.ConfigPublishEvent;
 import com.ctrip.framework.apollo.portal.service.ReleaseService;
 
+import java.util.Collections;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.security.access.AccessDeniedException;
@@ -113,6 +114,9 @@ public class ReleaseController {
                                          @PathVariable String namespaceName,
                                          @RequestParam(defaultValue = "0") int page,
                                          @RequestParam(defaultValue = "5") int size) {
+    if (permissionValidator.shouldHideConfigToCurrentUser(appId, env, namespaceName)) {
+      return Collections.emptyList();
+    }
 
     RequestPrecondition.checkNumberPositive(size);
     RequestPrecondition.checkNumberNotNegative(page);
@@ -128,6 +132,10 @@ public class ReleaseController {
                                              @RequestParam(defaultValue = "0") int page,
                                              @RequestParam(defaultValue = "5") int size) {
 
+    if (permissionValidator.shouldHideConfigToCurrentUser(appId, env, namespaceName)) {
+      return Collections.emptyList();
+    }
+
     RequestPrecondition.checkNumberPositive(size);
     RequestPrecondition.checkNumberNotNegative(page);
 

+ 8 - 0
apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ReleaseHistoryController.java

@@ -2,9 +2,11 @@ package com.ctrip.framework.apollo.portal.controller;
 
 
 import com.ctrip.framework.apollo.core.enums.Env;
+import com.ctrip.framework.apollo.portal.component.PermissionValidator;
 import com.ctrip.framework.apollo.portal.entity.bo.ReleaseHistoryBO;
 import com.ctrip.framework.apollo.portal.service.ReleaseHistoryService;
 
+import java.util.Collections;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -19,6 +21,8 @@ public class ReleaseHistoryController {
 
   @Autowired
   private ReleaseHistoryService releaseHistoryService;
+  @Autowired
+  private PermissionValidator permissionValidator;
 
   @RequestMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/releases/histories",
       method = RequestMethod.GET)
@@ -29,6 +33,10 @@ public class ReleaseHistoryController {
                                                                 @RequestParam(value = "page", defaultValue = "0") int page,
                                                                 @RequestParam(value = "size", defaultValue = "10") int size) {
 
+    if (permissionValidator.shouldHideConfigToCurrentUser(appId, env, namespaceName)) {
+      return Collections.emptyList();
+    }
+
    return releaseHistoryService.findNamespaceReleaseHistory(appId, Env.valueOf(env), clusterName ,namespaceName, page, size);
   }
 

+ 15 - 1
apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/entity/bo/NamespaceBO.java

@@ -12,7 +12,8 @@ public class NamespaceBO {
   private boolean isPublic;
   private String parentAppId;
   private String comment;
-
+  // is the configs hidden to current user?
+  private boolean isConfigHidden;
 
   public NamespaceDTO getBaseInfo() {
     return baseInfo;
@@ -70,4 +71,17 @@ public class NamespaceBO {
     this.comment = comment;
   }
 
+  public boolean isConfigHidden() {
+    return isConfigHidden;
+  }
+
+  public void setConfigHidden(boolean hidden) {
+    isConfigHidden = hidden;
+  }
+
+  public void hideItems() {
+    setConfigHidden(true);
+    items.clear();
+    setItemModifiedCnt(0);
+  }
 }

+ 4 - 3
apollo-portal/src/main/resources/static/config/history.html

@@ -48,7 +48,7 @@
 
         </div>
 
-        <div class="release-history-container panel-body row" ng-show="releaseHistories && releaseHistories.length > 0">
+        <div class="release-history-container panel-body row" ng-show="!isConfigHidden && releaseHistories && releaseHistories.length > 0">
             <div class="release-history-list col-md-3">
 
                 <div class="media hover" ng-class="{'active': releaseHistory.id == selectedReleaseHistory}"
@@ -232,8 +232,9 @@
 
         </div>
 
-        <div class="panel-body" ng-show="!releaseHistories || releaseHistories.length == 0">
-            <h4 class="text-center empty-container">无发布历史信息</h4>
+        <div class="panel-body" ng-show="isConfigHidden || !releaseHistories || releaseHistories.length == 0">
+            <h4 class="text-center empty-container" ng-show="isConfigHidden">您不是该项目的管理员,也没有该Namespace的编辑或发布权限,无法查看发布历史</h4>
+            <h4 class="text-center empty-container" ng-show="!isConfigHidden">无发布历史信息</h4>
         </div>
     </section>
 

+ 3 - 0
apollo-portal/src/main/resources/static/scripts/controller/config/ReleaseHistoryController.js

@@ -26,6 +26,8 @@ function releaseHistoryController($scope, $location, AppUtil,
     $scope.hasLoadAll = false;
     $scope.selectedReleaseHistory = 0;
     $scope.isTextNamespace = false;
+    // whether current user can view config
+    $scope.isConfigHidden = false;
 
     $scope.showReleaseHistoryDetail = showReleaseHistoryDetail;
     $scope.switchConfigViewType = switchConfigViewType;
@@ -99,6 +101,7 @@ function releaseHistoryController($scope, $location, AppUtil,
                 if ($scope.isTextNamespace) {
                   fixTextNamespaceViewType();
                 }
+                $scope.isConfigHidden = result.isConfigHidden;
             })
     }
 

+ 7 - 2
apollo-portal/src/main/resources/static/views/component/namespace-panel-branch-tab.html

@@ -53,8 +53,13 @@
     </header>
 
     <div id="BODY{{namespace.branch.id}}" class="collapse in">
+        <div class="J_namespace-release-tip well well-sm no-radius text-center"
+             ng-show="namespace.isConfigHidden">
+            <span style="color: red">您不是该项目的管理员,也没有该Namespace的编辑或发布权限,无法查看配置信息。</span>
+        </div>
+
         <!--second header-->
-        <header class="panel-heading second-panel-heading">
+        <header class="panel-heading second-panel-heading" ng-show="!namespace.isConfigHidden">
             <div class="row">
                 <div class="col-md-12 pull-left">
                     <ul class="nav nav-tabs">
@@ -92,7 +97,7 @@
             </div>
         </header>
         <!--namespace body-->
-        <section>
+        <section ng-show="!namespace.isConfigHidden">
             <!--items-->
             <div class="namespace-view-table" ng-show="namespace.branch.viewType == 'table'">
 

+ 9 - 2
apollo-portal/src/main/resources/static/views/component/namespace-panel-master-tab.html

@@ -92,9 +92,16 @@
         </div>
     </header>
 
+
+
     <div id="BODY{{namespace.id}}" class="collapse in">
+        <div class="J_namespace-release-tip well well-sm no-radius text-center"
+             ng-show="namespace.isConfigHidden">
+            <span style="color: red">您不是该项目的管理员,也没有该Namespace的编辑或发布权限,无法查看配置信息。</span>
+        </div>
+
         <!--second header-->
-        <header class="panel-heading second-panel-heading">
+        <header class="panel-heading second-panel-heading" ng-show="!namespace.isConfigHidden">
             <div class="row">
                 <div class="col-md-6 col-sm-6 pull-left">
                     <!--master branch nav tabs-->
@@ -182,7 +189,7 @@
         </header>
 
         <!--namespace body-->
-        <section>
+        <section ng-show="!namespace.isConfigHidden">
             <!--table view-->
             <div class="namespace-view-table" ng-show="namespace.viewType == 'table'">
 

+ 2 - 1
scripts/apollo-on-kubernetes/db/portal-db/apolloportaldb.sql

@@ -312,7 +312,8 @@ VALUES
     ('superAdmin', 'apollo', 'Portal超级管理员'),
     ('api.readTimeout', '10000', 'http接口read timeout'),
     ('consumer.token.salt', 'someSalt', 'consumer token salt'),
-    ('admin.createPrivateNamespace.switch', 'true', '是否允许项目管理员创建私有namespace');
+    ('admin.createPrivateNamespace.switch', 'true', '是否允许项目管理员创建私有namespace'),
+    ('configView.memberOnly.envs', 'pro', '只对项目成员显示配置信息的环境列表,多个env以英文逗号分隔');
 
 INSERT INTO `Users` (`Username`, `Password`, `Email`, `Enabled`)
 VALUES

+ 3 - 1
scripts/sql/apolloportaldb.sql

@@ -312,7 +312,9 @@ VALUES
     ('superAdmin', 'apollo', 'Portal超级管理员'),
     ('api.readTimeout', '10000', 'http接口read timeout'),
     ('consumer.token.salt', 'someSalt', 'consumer token salt'),
-    ('admin.createPrivateNamespace.switch', 'true', '是否允许项目管理员创建私有namespace');
+    ('admin.createPrivateNamespace.switch', 'true', '是否允许项目管理员创建私有namespace'),
+    ('configView.memberOnly.envs', 'pro', '只对项目成员显示配置信息的环境列表,多个env以英文逗号分隔');
+
 
 INSERT INTO `Users` (`Username`, `Password`, `Email`, `Enabled`)
 VALUES