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());