This is an automated email from the ASF dual-hosted git repository.

xxyu pushed a commit to branch kylin5
in repository https://gitbox.apache.org/repos/asf/kylin.git

commit 9ee8b057cb38ef5a62c533452cf5949b03eee8b0
Author: Hang Jia <754332...@qq.com>
AuthorDate: Wed Mar 15 18:54:55 2023 +0800

    KYLIN-5563 Enable OPERATION role to manage INDEX
---
 .../kylin/rest/controller/NAdminController.java    |  1 +
 .../kylin/rest/controller/AdminControllerTest.java | 19 +++++++++++++++-
 .../org/apache/kylin/rest/util/AclEvaluate.java    |  6 ++++-
 .../kylin/rest/service/AccessServiceTest.java      | 24 ++++++++++++++++++++
 .../org/apache/kylin/common/KylinConfigBase.java   |  4 ++++
 .../org/apache/kylin/rest/constant/Constant.java   | 24 +++++++++-----------
 .../java/org/apache/kylin/rest/util/AclUtil.java   |  4 ++++
 .../kylin/rest/service/IndexPlanService.java       | 26 +++++++++++-----------
 8 files changed, 79 insertions(+), 29 deletions(-)

diff --git 
a/src/common-server/src/main/java/org/apache/kylin/rest/controller/NAdminController.java
 
b/src/common-server/src/main/java/org/apache/kylin/rest/controller/NAdminController.java
index 421fe1b009..291433ee26 100644
--- 
a/src/common-server/src/main/java/org/apache/kylin/rest/controller/NAdminController.java
+++ 
b/src/common-server/src/main/java/org/apache/kylin/rest/controller/NAdminController.java
@@ -79,6 +79,7 @@ public class NAdminController extends NBasicController {
         propertyKeys.add("kylin.source.ddl.logical-view.database");
         propertyKeys.add("kylin.storage.check-quota-enabled");
         propertyKeys.add("kylin.table.load-threshold-enabled");
+        propertyKeys.add("kylin.index.enable-operator-design");
 
         // add second storage
         if 
(StringUtils.isNotEmpty(KylinConfig.getInstanceFromEnv().getSecondStorage())) {
diff --git 
a/src/common-server/src/test/java/org/apache/kylin/rest/controller/AdminControllerTest.java
 
b/src/common-server/src/test/java/org/apache/kylin/rest/controller/AdminControllerTest.java
index 62d0b0bd77..80347d8d09 100644
--- 
a/src/common-server/src/test/java/org/apache/kylin/rest/controller/AdminControllerTest.java
+++ 
b/src/common-server/src/test/java/org/apache/kylin/rest/controller/AdminControllerTest.java
@@ -20,9 +20,12 @@ package org.apache.kylin.rest.controller;
 
 import static 
org.apache.kylin.common.constant.HttpConstant.HTTP_VND_APACHE_KYLIN_V4_PUBLIC_JSON;
 
-import org.apache.kylin.rest.constant.Constant;
+import org.apache.commons.lang.StringUtils;
+import org.apache.kylin.common.util.JsonUtil;
 import org.apache.kylin.common.util.NLocalFileMetadataTestCase;
+import org.apache.kylin.rest.constant.Constant;
 import org.junit.After;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.InjectMocks;
@@ -33,10 +36,13 @@ import 
org.springframework.security.authentication.TestingAuthenticationToken;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.MvcResult;
 import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
 import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
 import org.springframework.test.web.servlet.setup.MockMvcBuilders;
 
+import com.fasterxml.jackson.databind.JsonNode;
+
 public class AdminControllerTest extends NLocalFileMetadataTestCase {
     private MockMvc mockMvc;
 
@@ -89,4 +95,15 @@ public class AdminControllerTest extends 
NLocalFileMetadataTestCase {
         Mockito.verify(adminController).getPublicConfig();
     }
 
+    @Test
+    public void testGetPublicConfigKylinIndexEnableOperatorDesign() throws 
Exception {
+        MvcResult mvcResult = mockMvc
+                
.perform(MockMvcRequestBuilders.get("/api/admin/public_config").contentType(MediaType.APPLICATION_JSON)
+                        
.accept(MediaType.parseMediaType(HTTP_VND_APACHE_KYLIN_V4_PUBLIC_JSON)))
+                .andExpect(MockMvcResultMatchers.status().isOk()).andReturn();
+        Mockito.verify(adminController).getPublicConfig();
+        final JsonNode jsonNode = 
JsonUtil.readValueAsTree(mvcResult.getResponse().getContentAsString());
+        
Assert.assertTrue(StringUtils.contains(jsonNode.get("data").textValue(), 
"kylin.index.enable-operator-design"));
+    }
+
 }
diff --git 
a/src/common-service/src/main/java/org/apache/kylin/rest/util/AclEvaluate.java 
b/src/common-service/src/main/java/org/apache/kylin/rest/util/AclEvaluate.java
index c556998bed..ff7608a612 100644
--- 
a/src/common-service/src/main/java/org/apache/kylin/rest/util/AclEvaluate.java
+++ 
b/src/common-service/src/main/java/org/apache/kylin/rest/util/AclEvaluate.java
@@ -135,4 +135,8 @@ public class AclEvaluate {
         aclUtil.checkIsGlobalAdmin();
     }
 
-}
\ No newline at end of file
+    public void checkProjectOperationDesignPermission(String projectName) {
+        boolean indexEnableOperatorDesign = 
KylinConfig.getInstanceFromEnv().isIndexEnableOperatorDesign();
+        
aclUtil.hasProjectOperationDesignPermission(getProjectInstance(projectName), 
indexEnableOperatorDesign);
+    }
+}
diff --git 
a/src/common-service/src/test/java/org/apache/kylin/rest/service/AccessServiceTest.java
 
b/src/common-service/src/test/java/org/apache/kylin/rest/service/AccessServiceTest.java
index 7fc2665b6f..9272ff7493 100644
--- 
a/src/common-service/src/test/java/org/apache/kylin/rest/service/AccessServiceTest.java
+++ 
b/src/common-service/src/test/java/org/apache/kylin/rest/service/AccessServiceTest.java
@@ -901,4 +901,28 @@ public class AccessServiceTest extends 
NLocalFileMetadataTestCase {
         List<String> existed = Arrays.asList("group1", "group2");
         accessService.batchCheckSid("nogroup", false, existed);
     }
+
+    @Test
+    public void testCheckProjectOperationDesignPermission() {
+        getTestConfig().setProperty("kylin.index.enable-operator-design", 
"true");
+        String username = "OPERATION1";
+        ManagedUser managedUser = new ManagedUser(username, username, false, 
Arrays.asList(//
+                new SimpleGrantedAuthority(Constant.GROUP_ALL_USERS)));
+        if (!userService.userExists(username)) {
+            userService.createUser(managedUser);
+        }
+
+        ProjectInstance projectInstance = 
NProjectManager.getInstance(getTestConfig()).getProject("default");
+        AclEntity ae = 
accessService.getAclEntity(AclEntityType.PROJECT_INSTANCE, 
projectInstance.getUuid());
+        Sid sid = accessService.getSid(username, false);
+        accessService.grant(ae, AclPermission.OPERATION, sid);
+
+        SecurityContextHolder.getContext()
+                .setAuthentication(new TestingAuthenticationToken(username, 
username, Constant.GROUP_ALL_USERS));
+
+        aclEvaluate.checkProjectOperationDesignPermission("default");
+
+        SecurityContextHolder.getContext()
+                .setAuthentication(new TestingAuthenticationToken("ADMIN", 
"ADMIN", Constant.ROLE_ADMIN));
+    }
 }
diff --git 
a/src/core-common/src/main/java/org/apache/kylin/common/KylinConfigBase.java 
b/src/core-common/src/main/java/org/apache/kylin/common/KylinConfigBase.java
index 0c5465d87b..6d1e60608b 100644
--- a/src/core-common/src/main/java/org/apache/kylin/common/KylinConfigBase.java
+++ b/src/core-common/src/main/java/org/apache/kylin/common/KylinConfigBase.java
@@ -3898,4 +3898,8 @@ public abstract class KylinConfigBase implements 
Serializable {
     public boolean isTableLoadThresholdEnabled() {
         return 
Boolean.parseBoolean(getOptional("kylin.table.load-threshold-enabled", TRUE));
     }
+
+    public boolean isIndexEnableOperatorDesign() {
+        return 
Boolean.parseBoolean(getOptional("kylin.index.enable-operator-design", FALSE));
+    }
 }
diff --git 
a/src/core-metadata/src/main/java/org/apache/kylin/rest/constant/Constant.java 
b/src/core-metadata/src/main/java/org/apache/kylin/rest/constant/Constant.java
index 927ff939ac..1c2fd590f9 100644
--- 
a/src/core-metadata/src/main/java/org/apache/kylin/rest/constant/Constant.java
+++ 
b/src/core-metadata/src/main/java/org/apache/kylin/rest/constant/Constant.java
@@ -43,24 +43,20 @@ public class Constant {
 
     public final static String ACCESS_HAS_ROLE_ADMIN = "hasRole('ROLE_ADMIN')";
 
-    public static final String ACCESS_POST_FILTER_READ = 
"hasRole('ROLE_ADMIN') " + //
-            " or hasPermission(#project, 'ADMINISTRATION')" + //
-            " or hasPermission(#project, 'MANAGEMENT')" + //
-            " or hasPermission(#project, 'OPERATION')" + //
-            " or hasPermission(#project, 'READ')";
+    public static final String ACCESS_CAN_PROJECT_ADMIN = 
"hasRole('ROLE_ADMIN') " + //
+            " or hasPermission(#project, 'ADMINISTRATION')";
 
-    public static final String 
ACCESS_POST_FILTER_READ_FOR_DATA_PERMISSION_SEPARATE = "hasPermission(#project, 
'DATA_QUERY')";
+    public static final String ACCESS_CAN_PROJECT_WRITE = 
ACCESS_CAN_PROJECT_ADMIN + //
+            " or hasPermission(#project, 'MANAGEMENT')";
 
-    public static final String ACCESS_CAN_PROJECT_OPERATION = 
"hasRole('ROLE_ADMIN') " + //
-            " or hasPermission(#project, 'ADMINISTRATION')" + //
-            " or hasPermission(#project, 'MANAGEMENT')" + //
+    public static final String ACCESS_CAN_PROJECT_OPERATION = 
ACCESS_CAN_PROJECT_WRITE + //
             " or hasPermission(#project, 'OPERATION')";
 
-    public static final String ACCESS_CAN_PROJECT_WRITE = 
"hasRole('ROLE_ADMIN') " + //
-            " or hasPermission(#project, 'ADMINISTRATION')" + //
-            " or hasPermission(#project, 'MANAGEMENT')";
+    public static final String ACCESS_POST_FILTER_READ = 
ACCESS_CAN_PROJECT_OPERATION + //
+            " or hasPermission(#project, 'READ')";
 
-    public static final String ACCESS_CAN_PROJECT_ADMIN = 
"hasRole('ROLE_ADMIN') " + //
-            " or hasPermission(#project, 'ADMINISTRATION')";
+    public static final String 
ACCESS_POST_FILTER_READ_FOR_DATA_PERMISSION_SEPARATE = "hasPermission(#project, 
'DATA_QUERY')";
 
+    public static final String ACCESS_CAN_PROJECT_OPERATION_DESIGN = 
ACCESS_CAN_PROJECT_WRITE + //
+            " or (hasPermission(#project, 'OPERATION') and 
#isIndexEnableOperatorDesign)";
 }
diff --git 
a/src/core-metadata/src/main/java/org/apache/kylin/rest/util/AclUtil.java 
b/src/core-metadata/src/main/java/org/apache/kylin/rest/util/AclUtil.java
index 2c48c6e60b..70ced83f3d 100644
--- a/src/core-metadata/src/main/java/org/apache/kylin/rest/util/AclUtil.java
+++ b/src/core-metadata/src/main/java/org/apache/kylin/rest/util/AclUtil.java
@@ -64,4 +64,8 @@ public class AclUtil {
         return true;
     }
 
+    @PreAuthorize(Constant.ACCESS_CAN_PROJECT_OPERATION_DESIGN)
+    public boolean hasProjectOperationDesignPermission(ProjectInstance 
project, boolean isIndexEnableOperatorDesign) {
+        return true;
+    }
 }
diff --git 
a/src/modeling-service/src/main/java/org/apache/kylin/rest/service/IndexPlanService.java
 
b/src/modeling-service/src/main/java/org/apache/kylin/rest/service/IndexPlanService.java
index 4795883c5c..bc9b03a0d5 100644
--- 
a/src/modeling-service/src/main/java/org/apache/kylin/rest/service/IndexPlanService.java
+++ 
b/src/modeling-service/src/main/java/org/apache/kylin/rest/service/IndexPlanService.java
@@ -170,7 +170,7 @@ public class IndexPlanService extends BasicService 
implements TableIndexPlanSupp
     @Transaction(project = 0)
     public Pair<IndexPlan, BuildIndexResponse> updateRuleBasedCuboid(String 
project,
             final UpdateRuleBasedCuboidRequest request) {
-        aclEvaluate.checkProjectWritePermission(project);
+        aclEvaluate.checkProjectOperationDesignPermission(project);
         try {
             val kylinConfig = KylinConfig.getInstanceFromEnv();
             val indexPlanManager = getManager(NIndexPlanManager.class, 
project);
@@ -201,7 +201,7 @@ public class IndexPlanService extends BasicService 
implements TableIndexPlanSupp
 
     @Transaction(project = 0)
     public BuildIndexResponse updateTableIndex(String project, 
CreateTableIndexRequest request) {
-        aclEvaluate.checkProjectWritePermission(project);
+        aclEvaluate.checkProjectOperationDesignPermission(project);
         try {
             val indexPlan = getIndexPlan(request.getProject(), 
request.getModelId());
             val layout = parseToLayout(project, request, 
indexPlan.getNextTableIndexId() + 1);
@@ -254,14 +254,14 @@ public class IndexPlanService extends BasicService 
implements TableIndexPlanSupp
 
     @Transaction(project = 0)
     public BuildIndexResponse createTableIndex(String project, 
CreateTableIndexRequest request) {
-        aclEvaluate.checkProjectWritePermission(project);
+        aclEvaluate.checkProjectOperationDesignPermission(project);
         val indexPlan = getIndexPlan(request.getProject(), 
request.getModelId());
         return createTableIndex(project, request, 
indexPlan.getNextTableIndexId() + 1);
     }
 
     @Transaction(project = 0)
     public BuildIndexResponse createTableIndex(String project, 
CreateTableIndexRequest request, long layoutId) {
-        aclEvaluate.checkProjectWritePermission(project);
+        aclEvaluate.checkProjectOperationDesignPermission(project);
         val newLayout = parseToLayout(project, request, layoutId);
         return createTableIndex(project, request.getModelId(), newLayout, 
request.isLoadData());
     }
@@ -325,7 +325,7 @@ public class IndexPlanService extends BasicService 
implements TableIndexPlanSupp
     @Deprecated
     @Transaction(project = 0)
     public void removeTableIndex(String project, String model, final long id) {
-        aclEvaluate.checkProjectWritePermission(project);
+        aclEvaluate.checkProjectOperationDesignPermission(project);
         val kylinConfig = KylinConfig.getInstanceFromEnv();
         val indexPlanManager = NIndexPlanManager.getInstance(kylinConfig, 
project);
 
@@ -372,7 +372,7 @@ public class IndexPlanService extends BasicService 
implements TableIndexPlanSupp
     @Transaction(project = 0)
     public void removeIndexes(String project, String modelId, Set<Long> ids, 
Set<Integer> invalidDimensions,
             Set<Integer> invalidMeasures) {
-        aclEvaluate.checkProjectWritePermission(project);
+        aclEvaluate.checkProjectOperationDesignPermission(project);
         if (CollectionUtils.isEmpty(ids)) {
             throw new KylinException(LAYOUT_LIST_EMPTY);
         }
@@ -421,7 +421,7 @@ public class IndexPlanService extends BasicService 
implements TableIndexPlanSupp
     }
 
     private boolean addIndexToBeDeleted(String project, String modelId, 
Set<Long> layoutIds) {
-        aclEvaluate.checkProjectWritePermission(project);
+        aclEvaluate.checkProjectOperationDesignPermission(project);
         val kylinConfig = KylinConfig.getInstanceFromEnv();
         val indexPlanManager = NIndexPlanManager.getInstance(kylinConfig, 
project);
 
@@ -455,7 +455,7 @@ public class IndexPlanService extends BasicService 
implements TableIndexPlanSupp
     }
 
     public DiffRuleBasedIndexResponse 
calculateDiffRuleBasedIndex(UpdateRuleBasedCuboidRequest request) {
-        aclEvaluate.checkProjectWritePermission(request.getProject());
+        
aclEvaluate.checkProjectOperationDesignPermission(request.getProject());
         UpdateRuleImpact diff = getIndexPlan(request.getProject(), 
request.getModelId())
                 .diffRuleBasedIndex(convertRequestToRuleBasedIndex(request));
 
@@ -463,7 +463,7 @@ public class IndexPlanService extends BasicService 
implements TableIndexPlanSupp
     }
 
     public AggIndexResponse 
calculateAggIndexCount(UpdateRuleBasedCuboidRequest request) {
-        aclEvaluate.checkProjectWritePermission(request.getProject());
+        
aclEvaluate.checkProjectOperationDesignPermission(request.getProject());
         val maxCount = getConfig().getCubeAggrGroupMaxCombination();
         // The agg group for updates which includes all agg group for the index
         List<NAggregationGroup> aggregationGroups = 
request.getAggregationGroups();
@@ -563,7 +563,7 @@ public class IndexPlanService extends BasicService 
implements TableIndexPlanSupp
 
     @Transaction(project = 0)
     public void updateShardByColumns(String project, AggShardByColumnsRequest 
request) {
-        aclEvaluate.checkProjectWritePermission(project);
+        aclEvaluate.checkProjectOperationDesignPermission(project);
 
         val modelId = request.getModelId();
         val indexPlanManager = getManager(NIndexPlanManager.class, project);
@@ -590,7 +590,7 @@ public class IndexPlanService extends BasicService 
implements TableIndexPlanSupp
     }
 
     public AggShardByColumnsResponse getShardByColumns(String project, String 
modelId) {
-        aclEvaluate.checkProjectWritePermission(project);
+        aclEvaluate.checkProjectOperationDesignPermission(project);
         val indexPlanManager = getManager(NIndexPlanManager.class, project);
         val indexPlan = indexPlanManager.getIndexPlan(modelId);
         val model = indexPlan.getModel();
@@ -785,7 +785,7 @@ public class IndexPlanService extends BasicService 
implements TableIndexPlanSupp
     }
 
     public RuleBasedIndex getRule(String project, String model) {
-        aclEvaluate.checkProjectWritePermission(project);
+        aclEvaluate.checkProjectOperationDesignPermission(project);
         val indexPlan = getIndexPlan(project, model);
         Preconditions.checkState(indexPlan != null);
 
@@ -1246,7 +1246,7 @@ public class IndexPlanService extends BasicService 
implements TableIndexPlanSupp
 
     @Transaction(project = 0)
     public BuildBaseIndexResponse createBaseIndex(String project, 
CreateBaseIndexRequest request) {
-        aclEvaluate.checkProjectWritePermission(project);
+        aclEvaluate.checkProjectOperationDesignPermission(project);
         NDataModel model = getManager(NDataModelManager.class, 
project).getDataModelDesc(request.getModelId());
         NIndexPlanManager indexPlanManager = 
getManager(NIndexPlanManager.class, project);
         IndexPlan indexPlan = 
indexPlanManager.getIndexPlan(request.getModelId());

Reply via email to