KYLIN-2703 supports managing access rights for project and cube through apache 
ranger

Signed-off-by: Li Yang <liy...@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/kylin/repo
Commit: http://git-wip-us.apache.org/repos/asf/kylin/commit/108e93c6
Tree: http://git-wip-us.apache.org/repos/asf/kylin/tree/108e93c6
Diff: http://git-wip-us.apache.org/repos/asf/kylin/diff/108e93c6

Branch: refs/heads/security_update
Commit: 108e93c604d89b83d40986ebf50d1024412ab8e4
Parents: e3e361d
Author: peng.jianhua <peng.jian...@zte.com.cn>
Authored: Wed Sep 6 17:50:51 2017 +0800
Committer: Li Yang <liy...@apache.org>
Committed: Fri Sep 15 22:41:24 2017 +0800

----------------------------------------------------------------------
 build/deploy/context.xml                        |   2 +-
 .../apache/kylin/common/KylinConfigBase.java    |   5 +
 .../kylin/metadata/project/ProjectManager.java  |   9 ++
 .../kylin/rest/controller/AccessController.java |  44 +++++++-
 .../kylin/rest/security/AclEntityType.java      |   1 -
 .../kylin/rest/security/AclPermission.java      |   2 -
 .../rest/security/ExternalAclProvider.java      | 104 +++++++++++++++++
 .../security/KylinAclPermissionEvaluator.java   | 111 +++++++++++++++++++
 .../kylin/rest/util/AclPermissionUtil.java      |  39 +++++++
 server/src/main/resources/kylinSecurity.xml     |   4 +-
 webapp/app/js/services/kylinProperties.js       |   8 ++
 webapp/app/partials/cubes/cube_detail.html      |   3 +-
 .../app/partials/projects/project_detail.html   |   3 +-
 13 files changed, 323 insertions(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/kylin/blob/108e93c6/build/deploy/context.xml
----------------------------------------------------------------------
diff --git a/build/deploy/context.xml b/build/deploy/context.xml
index 38c6ec8..4650d27 100644
--- a/build/deploy/context.xml
+++ b/build/deploy/context.xml
@@ -17,7 +17,7 @@
   ~ limitations under the License.
   -->
 <!-- The contents of this file will be loaded for each web application -->
-<Context>
+<Context allowLinking="true">
 
     <!-- Default set of monitored resources -->
     <WatchedResource>WEB-INF/web.xml</WatchedResource>

http://git-wip-us.apache.org/repos/asf/kylin/blob/108e93c6/core-common/src/main/java/org/apache/kylin/common/KylinConfigBase.java
----------------------------------------------------------------------
diff --git 
a/core-common/src/main/java/org/apache/kylin/common/KylinConfigBase.java 
b/core-common/src/main/java/org/apache/kylin/common/KylinConfigBase.java
index c120c4b..c74c093 100644
--- a/core-common/src/main/java/org/apache/kylin/common/KylinConfigBase.java
+++ b/core-common/src/main/java/org/apache/kylin/common/KylinConfigBase.java
@@ -1194,6 +1194,10 @@ abstract public class KylinConfigBase implements 
Serializable {
     public int getServerUserCacheMaxEntries() {
         return 
Integer.valueOf(this.getOptional("kylin.server.auth-user-cache.max-entries", 
"100"));
     }
+    
+    public String getExternalAclProvider() {
+        return getOptional("kylin.server.external-acl-provider", "");
+    }
 
     // 
============================================================================
     // WEB
@@ -1238,4 +1242,5 @@ abstract public class KylinConfigBase implements 
Serializable {
     public String getPerfLoggerClassName() {
         return getOptional("kylin.metrics.perflogger-class", 
"org.apache.kylin.common.metrics.perflog.PerfLogger");
     }
+
 }

http://git-wip-us.apache.org/repos/asf/kylin/blob/108e93c6/core-metadata/src/main/java/org/apache/kylin/metadata/project/ProjectManager.java
----------------------------------------------------------------------
diff --git 
a/core-metadata/src/main/java/org/apache/kylin/metadata/project/ProjectManager.java
 
b/core-metadata/src/main/java/org/apache/kylin/metadata/project/ProjectManager.java
index bbbca4f..ee51fa2 100644
--- 
a/core-metadata/src/main/java/org/apache/kylin/metadata/project/ProjectManager.java
+++ 
b/core-metadata/src/main/java/org/apache/kylin/metadata/project/ProjectManager.java
@@ -442,6 +442,15 @@ public class ProjectManager {
         return projects;
     }
 
+    public ProjectInstance getProjectByUuid(String uuid) {
+        Collection<ProjectInstance> copy = new 
ArrayList<ProjectInstance>(projectMap.values());
+        for (ProjectInstance project : copy) {
+            if (uuid.equals(project.getUuid()))
+                return project;
+        }
+        return null;
+    }
+
     public ExternalFilterDesc getExternalFilterDesc(String project, String 
extFilter) {
         return l2Cache.getExternalFilterDesc(project, extFilter);
     }

http://git-wip-us.apache.org/repos/asf/kylin/blob/108e93c6/server-base/src/main/java/org/apache/kylin/rest/controller/AccessController.java
----------------------------------------------------------------------
diff --git 
a/server-base/src/main/java/org/apache/kylin/rest/controller/AccessController.java
 
b/server-base/src/main/java/org/apache/kylin/rest/controller/AccessController.java
index a88c342..cd39cb1 100644
--- 
a/server-base/src/main/java/org/apache/kylin/rest/controller/AccessController.java
+++ 
b/server-base/src/main/java/org/apache/kylin/rest/controller/AccessController.java
@@ -19,18 +19,26 @@
 package org.apache.kylin.rest.controller;
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.kylin.common.persistence.AclEntity;
+import org.apache.kylin.common.util.Pair;
 import org.apache.kylin.rest.request.AccessRequest;
 import org.apache.kylin.rest.response.AccessEntryResponse;
+import org.apache.kylin.rest.security.AclPermission;
 import org.apache.kylin.rest.security.AclPermissionFactory;
+import org.apache.kylin.rest.security.ExternalAclProvider;
 import org.apache.kylin.rest.service.AccessService;
+import org.apache.kylin.rest.service.UserService;
+import org.apache.kylin.rest.util.AclPermissionUtil;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.security.acls.domain.PrincipalSid;
 import org.springframework.security.acls.model.Acl;
 import org.springframework.security.acls.model.Permission;
 import org.springframework.security.acls.model.Sid;
+import org.springframework.security.core.userdetails.UserDetails;
 import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestBody;
@@ -50,6 +58,10 @@ public class AccessController extends BasicController {
     @Qualifier("accessService")
     private AccessService accessService;
 
+    @Autowired
+    @Qualifier("userService")
+    private UserService userService;
+
     /**
      * Get access entry list of a domain object
      * 
@@ -59,11 +71,35 @@ public class AccessController extends BasicController {
      */
     @RequestMapping(value = "/{type}/{uuid}", method = { RequestMethod.GET }, 
produces = { "application/json" })
     @ResponseBody
-    public List<AccessEntryResponse> getAccessEntities(@PathVariable String 
type, @PathVariable String uuid) {
-        AclEntity ae = accessService.getAclEntity(type, uuid);
-        Acl acl = accessService.getAcl(ae);
+    public List<AccessEntryResponse> getAccessEntities(@PathVariable String 
type, @PathVariable String uuid) throws IOException {
+        ExternalAclProvider eap = ExternalAclProvider.getInstance();
 
-        return accessService.generateAceResponses(acl);
+        if (eap != null) {
+            List<AccessEntryResponse> ret = new ArrayList<>();
+            List<Pair<String, AclPermission>> acl = eap.getAcl(type, uuid);
+            if (acl != null) {
+                for (Pair<String, AclPermission> p : acl) {
+                    PrincipalSid sid = new PrincipalSid(p.getFirst());
+                    ret.add(new AccessEntryResponse(null, sid, p.getSecond(), 
true));
+                }
+            } else {
+                // in case getAcl() does not work, try checkPermission() as a 
fall back
+                for (UserDetails user : userService.listUsers()) {
+                    PrincipalSid sid = new PrincipalSid(user.getUsername());
+                    List<String> authorities = 
AclPermissionUtil.transformAuthorities(user.getAuthorities());
+                    for (Permission p : AclPermissionFactory.getPermissions()) 
{
+                        if (eap.checkPermission(user.getUsername(), 
authorities, type, uuid, p)) {
+                            ret.add(new AccessEntryResponse(null, sid, p, 
true));
+                        }
+                    }
+                }
+            }
+            return ret;
+        } else {
+            AclEntity ae = accessService.getAclEntity(type, uuid);
+            Acl acl = accessService.getAcl(ae);
+            return accessService.generateAceResponses(acl);
+        }
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/kylin/blob/108e93c6/server-base/src/main/java/org/apache/kylin/rest/security/AclEntityType.java
----------------------------------------------------------------------
diff --git 
a/server-base/src/main/java/org/apache/kylin/rest/security/AclEntityType.java 
b/server-base/src/main/java/org/apache/kylin/rest/security/AclEntityType.java
index 69965f8..62ba5da 100644
--- 
a/server-base/src/main/java/org/apache/kylin/rest/security/AclEntityType.java
+++ 
b/server-base/src/main/java/org/apache/kylin/rest/security/AclEntityType.java
@@ -18,7 +18,6 @@
 package org.apache.kylin.rest.security;
 
 /**
- * Created by xiefan on 17-4-14.
  */
 public interface AclEntityType {
     static final String CUBE_INSTANCE = "CubeInstance";

http://git-wip-us.apache.org/repos/asf/kylin/blob/108e93c6/server-base/src/main/java/org/apache/kylin/rest/security/AclPermission.java
----------------------------------------------------------------------
diff --git 
a/server-base/src/main/java/org/apache/kylin/rest/security/AclPermission.java 
b/server-base/src/main/java/org/apache/kylin/rest/security/AclPermission.java
index 4e2e182..7d493d1 100644
--- 
a/server-base/src/main/java/org/apache/kylin/rest/security/AclPermission.java
+++ 
b/server-base/src/main/java/org/apache/kylin/rest/security/AclPermission.java
@@ -22,8 +22,6 @@ import 
org.springframework.security.acls.domain.BasePermission;
 import org.springframework.security.acls.model.Permission;
 
 /**
- * @author xduo
- * 
  */
 public class AclPermission extends BasePermission {
 

http://git-wip-us.apache.org/repos/asf/kylin/blob/108e93c6/server-base/src/main/java/org/apache/kylin/rest/security/ExternalAclProvider.java
----------------------------------------------------------------------
diff --git 
a/server-base/src/main/java/org/apache/kylin/rest/security/ExternalAclProvider.java
 
b/server-base/src/main/java/org/apache/kylin/rest/security/ExternalAclProvider.java
new file mode 100644
index 0000000..395f44d
--- /dev/null
+++ 
b/server-base/src/main/java/org/apache/kylin/rest/security/ExternalAclProvider.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.kylin.rest.security;
+
+import java.util.List;
+
+import org.apache.kylin.common.KylinConfig;
+import org.apache.kylin.common.util.ClassUtil;
+import org.apache.kylin.common.util.Pair;
+import org.springframework.security.acls.model.Permission;
+
+/**
+ */
+abstract public class ExternalAclProvider {
+
+    private static boolean inited = false;
+    private static ExternalAclProvider singleton = null;
+
+    public static ExternalAclProvider getInstance() {
+        if (inited)
+            return singleton;
+
+        synchronized (ExternalAclProvider.class) {
+            if (inited)
+                return singleton;
+
+            String cls = 
KylinConfig.getInstanceFromEnv().getExternalAclProvider();
+            if (cls != null && cls.length() > 0) {
+                singleton = (ExternalAclProvider) ClassUtil.newInstance(cls);
+                singleton.init();
+            }
+
+            inited = true;
+            return singleton;
+        }
+    }
+
+    // 
============================================================================
+
+    public final static String CUBE_ADMIN = "CUBE ADMIN";
+    public final static String CUBE_EDIT = "CUBE EDIT";
+    public final static String CUBE_OPERATION = "CUBE OPERATION";
+    public final static String CUBE_QUERY = "CUBE QUERY";
+    
+    // used by ranger ExternalAclProvider
+    public static String transformPermission(Permission p) {
+        String permString = null;
+        if (AclPermission.ADMINISTRATION.equals(p)) {
+            permString = CUBE_ADMIN;
+        } else if (AclPermission.MANAGEMENT.equals(p)) {
+            permString = CUBE_EDIT;
+        } else if (AclPermission.OPERATION.equals(p)) {
+            permString = CUBE_OPERATION;
+        } else if (AclPermission.READ.equals(p)) {
+            permString = CUBE_QUERY;
+        } else {
+            permString = p.getPattern();
+        }
+        return permString;
+    }
+
+    // 
============================================================================
+    
+    abstract public void init();
+
+    /**
+     * Checks if a user has permission on an entity.
+     * 
+     * @param user
+     * @param userRoles
+     * @param entityType String constants defined in AclEntityType 
+     * @param entityUuid
+     * @param permission
+     * 
+     * @return true if has permission
+     */
+    abstract public boolean checkPermission(String user, List<String> 
userRoles, //
+            String entityType, String entityUuid, Permission permission);
+
+    /**
+     * Returns all granted permissions on specified entity.
+     * 
+     * @param entityType String constants defined in AclEntityType
+     * @param entityUuid
+     * @return a list of (user/role, permission)
+     */
+    abstract public List<Pair<String, AclPermission>> getAcl(String 
entityType, String entityUuid);
+
+}

http://git-wip-us.apache.org/repos/asf/kylin/blob/108e93c6/server-base/src/main/java/org/apache/kylin/rest/security/KylinAclPermissionEvaluator.java
----------------------------------------------------------------------
diff --git 
a/server-base/src/main/java/org/apache/kylin/rest/security/KylinAclPermissionEvaluator.java
 
b/server-base/src/main/java/org/apache/kylin/rest/security/KylinAclPermissionEvaluator.java
new file mode 100644
index 0000000..b677537
--- /dev/null
+++ 
b/server-base/src/main/java/org/apache/kylin/rest/security/KylinAclPermissionEvaluator.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.kylin.rest.security;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.kylin.common.persistence.AclEntity;
+import org.apache.kylin.rest.service.AclService;
+import org.apache.kylin.rest.util.AclPermissionUtil;
+import org.springframework.security.acls.AclPermissionEvaluator;
+import org.springframework.security.acls.domain.PermissionFactory;
+import org.springframework.security.acls.model.Permission;
+import org.springframework.security.core.Authentication;
+
+public class KylinAclPermissionEvaluator extends AclPermissionEvaluator {
+
+    private PermissionFactory kylinPermissionFactory;
+
+    public KylinAclPermissionEvaluator(AclService aclService, 
PermissionFactory permissionFactory) {
+        super(aclService);
+        super.setPermissionFactory(permissionFactory);
+        this.kylinPermissionFactory = permissionFactory;
+    }
+
+    @Override
+    public boolean hasPermission(Authentication authentication, Object 
targetDomainObject, Object permission) {
+        ExternalAclProvider eap = ExternalAclProvider.getInstance();
+        if (eap == null)
+            return super.hasPermission(authentication, targetDomainObject, 
permission);
+
+        if (targetDomainObject == null) {
+            return false;
+        }
+
+        AclEntity e = (AclEntity) targetDomainObject;
+        return checkExternalPermission(eap, authentication, 
e.getClass().getSimpleName(), e.getId(), permission);
+    }
+
+    private boolean checkExternalPermission(ExternalAclProvider eap, 
Authentication authentication, String entityType,
+            String entityUuid, Object permission) {
+
+        String currentUser = authentication.getName();
+        List<String> authorities = 
AclPermissionUtil.transformAuthorities(authentication.getAuthorities());
+        List<Permission> kylinPermissions = resolveKylinPermission(permission);
+
+        for (Permission p : kylinPermissions) {
+            if (eap.checkPermission(currentUser, authorities, entityType, 
entityUuid, p))
+                return true;
+        }
+        return false;
+    }
+
+    private List<Permission> resolveKylinPermission(Object permission) {
+        if (permission instanceof Integer) {
+            return 
Arrays.asList(kylinPermissionFactory.buildFromMask(((Integer) 
permission).intValue()));
+        }
+
+        if (permission instanceof Permission) {
+            return Arrays.asList((Permission) permission);
+        }
+
+        if (permission instanceof Permission[]) {
+            return Arrays.asList((Permission[]) permission);
+        }
+
+        if (permission instanceof String) {
+            String permString = (String) permission;
+            Permission p;
+
+            try {
+                p = kylinPermissionFactory.buildFromName(permString);
+            } catch (IllegalArgumentException notfound) {
+                p = 
kylinPermissionFactory.buildFromName(permString.toUpperCase());
+            }
+
+            if (p != null) {
+                return Arrays.asList(p);
+            }
+
+        }
+        throw new IllegalArgumentException("Unsupported permission: " + 
permission);
+    }
+
+    @Override
+    public boolean hasPermission(Authentication authentication, Serializable 
targetId, String targetType,
+            Object permission) {
+        ExternalAclProvider eap = ExternalAclProvider.getInstance();
+        if (eap == null)
+            return super.hasPermission(authentication, targetId, targetType, 
permission);
+        
+        return checkExternalPermission(eap, authentication, targetType, 
targetId.toString(), permission);
+    }
+}

http://git-wip-us.apache.org/repos/asf/kylin/blob/108e93c6/server-base/src/main/java/org/apache/kylin/rest/util/AclPermissionUtil.java
----------------------------------------------------------------------
diff --git 
a/server-base/src/main/java/org/apache/kylin/rest/util/AclPermissionUtil.java 
b/server-base/src/main/java/org/apache/kylin/rest/util/AclPermissionUtil.java
new file mode 100644
index 0000000..a687d4d
--- /dev/null
+++ 
b/server-base/src/main/java/org/apache/kylin/rest/util/AclPermissionUtil.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.kylin.rest.util;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.springframework.security.core.GrantedAuthority;
+
+public class AclPermissionUtil {
+
+    public static List<String> transformAuthorities(Collection<? extends 
GrantedAuthority> authorities) {
+        List<String> ret = new ArrayList<String>();
+        for (GrantedAuthority auth : authorities) {
+            if (!authorities.contains(auth.getAuthority())) {
+                ret.add(auth.getAuthority());
+            }
+        }
+        return ret;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/kylin/blob/108e93c6/server/src/main/resources/kylinSecurity.xml
----------------------------------------------------------------------
diff --git a/server/src/main/resources/kylinSecurity.xml 
b/server/src/main/resources/kylinSecurity.xml
index ce068d7..856fb5e 100644
--- a/server/src/main/resources/kylinSecurity.xml
+++ b/server/src/main/resources/kylinSecurity.xml
@@ -38,9 +38,9 @@
         <property name="permissionEvaluator" ref="permissionEvaluator"/>
     </bean>
 
-    <bean id="permissionEvaluator" 
class="org.springframework.security.acls.AclPermissionEvaluator">
+    <bean id="permissionEvaluator" 
class="org.apache.kylin.rest.security.KylinAclPermissionEvaluator">
         <constructor-arg ref="aclService"/>
-        <property name="permissionFactory" ref="aclPermissionFactory"/>
+        <constructor-arg ref="aclPermissionFactory"/>
     </bean>
 
     <bean id="aclAuthorizationStrategy"

http://git-wip-us.apache.org/repos/asf/kylin/blob/108e93c6/webapp/app/js/services/kylinProperties.js
----------------------------------------------------------------------
diff --git a/webapp/app/js/services/kylinProperties.js 
b/webapp/app/js/services/kylinProperties.js
index 645ed2f..a2af4ed 100644
--- a/webapp/app/js/services/kylinProperties.js
+++ b/webapp/app/js/services/kylinProperties.js
@@ -107,5 +107,13 @@ KylinApp.service('kylinConfig', function (AdminService, 
$log) {
     }
   }
 
+  this.isExternalAclEnabled = function() {
+    var status = this.getProperty("kylin.server.external-acl-provider").trim();
+    if (status == '') {
+      return false;
+    }
+    return true;
+  }
+
 });
 

http://git-wip-us.apache.org/repos/asf/kylin/blob/108e93c6/webapp/app/partials/cubes/cube_detail.html
----------------------------------------------------------------------
diff --git a/webapp/app/partials/cubes/cube_detail.html 
b/webapp/app/partials/cubes/cube_detail.html
index 0113c12..51f7dee 100755
--- a/webapp/app/partials/cubes/cube_detail.html
+++ b/webapp/app/partials/cubes/cube_detail.html
@@ -29,7 +29,8 @@
             ng-if="userService.hasRole('ROLE_ADMIN') || hasPermission(cube, 
16) && !newAccess">
             <a href="" ng-click="cube.visiblePage='json';">JSON(Cube)</a>
         </li>
-        <li class="{{cube.visiblePage=='access'? 'active':''}}">
+        <li class="{{cube.visiblePage=='access'? 'active':''}}"
+            ng-if="!kylinConfig.isExternalAclEnabled()">
             <a href="" ng-click="cube.visiblePage='access';listAccess(cube, 
'CubeInstance');">Access</a>
         </li>
         <li class="{{cube.visiblePage=='notification'? 'active':''}}"

http://git-wip-us.apache.org/repos/asf/kylin/blob/108e93c6/webapp/app/partials/projects/project_detail.html
----------------------------------------------------------------------
diff --git a/webapp/app/partials/projects/project_detail.html 
b/webapp/app/partials/projects/project_detail.html
index 5cfc091..7112da6 100644
--- a/webapp/app/partials/projects/project_detail.html
+++ b/webapp/app/partials/projects/project_detail.html
@@ -21,7 +21,8 @@
         <li class="{{project.visiblePage=='cubes'? 'active':''}}">
             <a href="" ng-click="project.visiblePage='cubes';">Cubes</a>
         </li>
-        <li class="{{project.visiblePage=='access'? 'active':''}}">
+        <li class="{{project.visiblePage=='access'? 'active':''}}"
+            ng-if="!kylinConfig.isExternalAclEnabled()">
             <a href="" 
ng-click="project.visiblePage='access';listAccess(project, 
'ProjectInstance');">Access</a>
         </li>
         <li class="{{project.visiblePage=='config'? 'active':''}}">

Reply via email to