/* * 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.ranger.authorization.kylin.authorizer; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.apache.commons.lang.RandomStringUtils; import org.apache.kylin.common.KylinConfig; import org.apache.kylin.metadata.project.ProjectInstance; import org.apache.kylin.metadata.project.ProjectManager; import org.apache.kylin.metadata.project.RealizationEntry; import org.apache.kylin.rest.util.AclEvaluate; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.FixMethodOrder; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.MethodSorters; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.util.ReflectionTestUtils; /** * * Here we plug the Ranger RangerKylinAuthorizer into Kylin. * * A custom RangerAdminClient is plugged into Ranger in turn, which loads security policies from a local file. * These policies were generated in the Ranger Admin UI for a kylin service called "kylinTest": * * a) A user "kylin" can do all permissions(contains "ADMIN", "MANAGEMENT", "OPERATION", "QUERY") * on all kylin projects; * b) A user "zhangqiang" can do a "ADMIN" on the project "test_project", * and do a "OPERATION" on the project "kylin_project"; * c) A user "yuwen" can do a "ADMIN" on the project "test_project", * and do a "OPERATION" on the project "kylin_project"; * d) A user "admin" has role "ROLE_ADMIN", * and the others have role "ROLE_USER" by mock for test. * */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "classpath*:applicationContext.xml", "classpath*:kylinSecurity.xml" }) @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class RangerKylinAuthorizerTest { private static final Map<String, ProjectInstance> uuid2Projects = new HashMap<>(); private static final Map<String, ProjectInstance> name2Projects = new HashMap<>(); private static final String ADMIN = "admin"; private static final String KYLIN = "kylin"; private static final String ZHANGQIANG = "zhangqiang"; private static final String YUWEN = "yuwen"; private static final String LEARN_PROJECT = "learn_project"; private static final String TEST_PROJECT = "test_project"; private static final String KYLIN_PROJECT = "kylin_project"; private static final String[] PROJECTNAMES = new String[] { LEARN_PROJECT, TEST_PROJECT, KYLIN_PROJECT }; private static final String ROLE_ADMIN = "ADMIN"; private static final String ROLE_USER = "USER"; @Autowired private AclEvaluate aclEvaluate; @BeforeClass public static void setup() throws Exception { // set kylin conf path System.setProperty(KylinConfig.KYLIN_CONF, "src/test/resources"); // init kylin projects initKylinProjects(); // mock kylin projects, to match projectUuid and projectName for kylin mockKylinProjects(); } @AfterClass public static void cleanup() throws Exception { // do nothing } /** * Help function: init kylin projects */ private static void initKylinProjects() { for (String projectName : PROJECTNAMES) { ProjectInstance projectInstance = getProjectInstance(projectName); name2Projects.put(projectName, projectInstance); uuid2Projects.put(projectInstance.getUuid(), projectInstance); } } /** * Help function: mock kylin projects, to match projectUuid and projectName */ private static void mockKylinProjects() { KylinConfig kylinConfig = KylinConfig.getInstanceFromEnv(); ProjectManager projectManager = mock(ProjectManager.class); @SuppressWarnings({ "rawtypes", "unchecked" }) Map<Class, Object> managersCache = (Map<Class, Object>) ReflectionTestUtils.getField(kylinConfig, "managersCache"); managersCache.put(ProjectManager.class, projectManager); Answer<ProjectInstance> answer = new Answer<ProjectInstance>() { @Override public ProjectInstance answer(InvocationOnMock invocation) throws Throwable { Object[] args = invocation.getArguments(); if (args == null || args.length == 0) { return null; } String uuid = (String) args[0]; return uuid2Projects.get(uuid); } }; when(projectManager.getPrjByUuid(anyString())).thenAnswer(answer); } /** * Help function: get random project instance for test */ private static ProjectInstance getRandomProjectInstance() { String name = RandomStringUtils.randomAlphanumeric(10) + "-project"; return getProjectInstance(name); } /** * Help function: get specific project instance for test */ private static ProjectInstance getProjectInstance(String name) { String owner = null; String description = null; LinkedHashMap<String, String> overrideProps = null; List<RealizationEntry> realizationEntries = null; List<String> models = null; return ProjectInstance.create(name, owner, description, overrideProps, realizationEntries, models); } // No.1 hasProjectReadPermission test start /** * no credentials read any project failed */ @Test(expected = AuthenticationCredentialsNotFoundException.class) public void readProjectAnyWithoutCredentials() { ProjectInstance project = getRandomProjectInstance(); aclEvaluate.hasProjectReadPermission(project); } /** * admin read all projects sueecss */ @Test @WithMockUser(username = ADMIN, roles = { ROLE_ADMIN }) public void readProjectAllAsRoleAdmin() { for (ProjectInstance project : uuid2Projects.values()) { boolean result = aclEvaluate.hasProjectReadPermission(project); Assert.assertTrue(result); } } /** * kylin read all projects success */ @Test @WithMockUser(username = KYLIN, roles = { ROLE_USER }) public void readProjectAllWithAdminPermission() { for (ProjectInstance project : uuid2Projects.values()) { boolean result = aclEvaluate.hasProjectReadPermission(project); Assert.assertTrue(result); } } /** * zhangqiang read test_project success */ @Test @WithMockUser(username = ZHANGQIANG, roles = { ROLE_USER }) public void readProjectTestWithAdminPermission() { ProjectInstance project = name2Projects.get(TEST_PROJECT); boolean result = aclEvaluate.hasProjectReadPermission(project); Assert.assertTrue(result); } /** * zhangqiang read kylin_project success */ @Test @WithMockUser(username = ZHANGQIANG, roles = { ROLE_USER }) public void readProjectKylinWithOperationPermission() { ProjectInstance project = name2Projects.get(KYLIN_PROJECT); boolean result = aclEvaluate.hasProjectReadPermission(project); Assert.assertTrue(result); } /** * yuwen read test_project success */ @Test @WithMockUser(username = YUWEN, roles = { ROLE_USER }) public void readProjectTestWithManagementPermission() { ProjectInstance project = name2Projects.get(TEST_PROJECT); boolean result = aclEvaluate.hasProjectReadPermission(project); Assert.assertTrue(result); } /** * yuwen read kylin_project success */ @Test @WithMockUser(username = YUWEN, roles = { ROLE_USER }) public void readProjectKylinWithQueryPermission() { ProjectInstance project = name2Projects.get(KYLIN_PROJECT); boolean result = aclEvaluate.hasProjectReadPermission(project); Assert.assertTrue(result); } /** * yuwen read learn_project failed */ @Test @WithMockUser(username = YUWEN, roles = { ROLE_USER }) public void readProjectLearnWithoutPermission() { ProjectInstance project = name2Projects.get(LEARN_PROJECT); boolean result = aclEvaluate.hasProjectReadPermission(project); Assert.assertFalse(result); } // No.1 hasProjectReadPermission test end // No.2 hasProjectOperationPermission test start /** * no credentials operation any project failed */ @Test(expected = AuthenticationCredentialsNotFoundException.class) public void operationProjectAnyWithoutCredentials() { ProjectInstance project = getRandomProjectInstance(); aclEvaluate.hasProjectOperationPermission(project); } /** * admin operation all projects sueecss */ @Test @WithMockUser(username = ADMIN, roles = { ROLE_ADMIN }) public void operationProjectAllAsRoleAdmin() { for (ProjectInstance project : uuid2Projects.values()) { boolean result = aclEvaluate.hasProjectOperationPermission(project); Assert.assertTrue(result); } } /** * kylin operation all projects success */ @Test @WithMockUser(username = KYLIN, roles = { ROLE_USER }) public void operationProjectAllWithAdminPermission() { for (ProjectInstance project : uuid2Projects.values()) { boolean result = aclEvaluate.hasProjectOperationPermission(project); Assert.assertTrue(result); } } /** * zhangqiang operation test_project success */ @Test @WithMockUser(username = ZHANGQIANG, roles = { ROLE_USER }) public void operationProjectTestWithAdminPermission() { ProjectInstance project = name2Projects.get(TEST_PROJECT); boolean result = aclEvaluate.hasProjectOperationPermission(project); Assert.assertTrue(result); } /** * zhangqiang operation kylin_project success */ @Test @WithMockUser(username = ZHANGQIANG, roles = { ROLE_USER }) public void operationProjectKylinWithOperationPermission() { ProjectInstance project = name2Projects.get(KYLIN_PROJECT); boolean result = aclEvaluate.hasProjectOperationPermission(project); Assert.assertTrue(result); } /** * yuwen operation test_project success */ @Test @WithMockUser(username = YUWEN, roles = { ROLE_USER }) public void operationProjectTestWithManagementPermission() { ProjectInstance project = name2Projects.get(TEST_PROJECT); boolean result = aclEvaluate.hasProjectOperationPermission(project); Assert.assertTrue(result); } /** * yuwen operation kylin_project failed */ @Test @WithMockUser(username = YUWEN, roles = { ROLE_USER }) public void operationProjectKylinWithoutPermission() { ProjectInstance project = name2Projects.get(KYLIN_PROJECT); boolean result = aclEvaluate.hasProjectOperationPermission(project); Assert.assertFalse(result); } /** * yuwen operation learn_project failed */ @Test @WithMockUser(username = YUWEN, roles = { ROLE_USER }) public void operationProjectLearnWithoutPermission() { ProjectInstance project = name2Projects.get(LEARN_PROJECT); boolean result = aclEvaluate.hasProjectOperationPermission(project); Assert.assertFalse(result); } // No.2 hasProjectOperationPermission test end // No.3 hasProjectWritePermission test start /** * no credentials write any project failed */ @Test(expected = AuthenticationCredentialsNotFoundException.class) public void writeProjectAnyWithoutCredentials() { ProjectInstance project = getRandomProjectInstance(); aclEvaluate.hasProjectWritePermission(project); } /** * admin write all projects sueecss */ @Test @WithMockUser(username = ADMIN, roles = { ROLE_ADMIN }) public void writeProjectAllAsRoleAdmin() { for (ProjectInstance project : uuid2Projects.values()) { boolean result = aclEvaluate.hasProjectWritePermission(project); Assert.assertTrue(result); } } /** * kylin write all projects success */ @Test @WithMockUser(username = KYLIN, roles = { ROLE_USER }) public void writeProjectAllWithAdminPermission() { for (ProjectInstance project : uuid2Projects.values()) { boolean result = aclEvaluate.hasProjectWritePermission(project); Assert.assertTrue(result); } } /** * zhangqiang write test_project success */ @Test @WithMockUser(username = ZHANGQIANG, roles = { ROLE_USER }) public void writeProjectTestWithAdminPermission() { ProjectInstance project = name2Projects.get(TEST_PROJECT); boolean result = aclEvaluate.hasProjectWritePermission(project); Assert.assertTrue(result); } /** * zhangqiang write kylin_project failed */ @Test @WithMockUser(username = ZHANGQIANG, roles = { ROLE_USER }) public void writeProjectKylinWithoutPermission() { ProjectInstance project = name2Projects.get(KYLIN_PROJECT); boolean result = aclEvaluate.hasProjectWritePermission(project); Assert.assertFalse(result); } /** * yuwen write test_project success */ @Test @WithMockUser(username = YUWEN, roles = { ROLE_USER }) public void writeProjectTestWithManagementPermission() { ProjectInstance project = name2Projects.get(TEST_PROJECT); boolean result = aclEvaluate.hasProjectWritePermission(project); Assert.assertTrue(result); } /** * yuwen write kylin_project failed */ @Test @WithMockUser(username = YUWEN, roles = { ROLE_USER }) public void writeProjectKylinWithoutPermission2() { ProjectInstance project = name2Projects.get(KYLIN_PROJECT); boolean result = aclEvaluate.hasProjectWritePermission(project); Assert.assertFalse(result); } /** * yuwen write learn_project failed */ @Test @WithMockUser(username = YUWEN, roles = { ROLE_USER }) public void writeProjectLearnWithoutPermission() { ProjectInstance project = name2Projects.get(LEARN_PROJECT); boolean result = aclEvaluate.hasProjectWritePermission(project); Assert.assertFalse(result); } // No.3 hasProjectWritePermission test end // No.4 hasProjectAdminPermission test start /** * no credentials admin any project failed */ @Test(expected = AuthenticationCredentialsNotFoundException.class) public void adminProjectAnyWithoutCredentials() { ProjectInstance project = getRandomProjectInstance(); aclEvaluate.hasProjectAdminPermission(project); } /** * admin admin all projects sueecss */ @Test @WithMockUser(username = ADMIN, roles = { ROLE_ADMIN }) public void adminProjectAllAsRoleAdmin() { for (ProjectInstance project : uuid2Projects.values()) { boolean result = aclEvaluate.hasProjectAdminPermission(project); Assert.assertTrue(result); } } /** * kylin admin all projects success */ @Test @WithMockUser(username = KYLIN, roles = { ROLE_USER }) public void adminProjectAllWithAdminPermission() { for (ProjectInstance project : uuid2Projects.values()) { boolean result = aclEvaluate.hasProjectAdminPermission(project); Assert.assertTrue(result); } } /** * zhangqiang admin test_project success */ @Test @WithMockUser(username = ZHANGQIANG, roles = { ROLE_USER }) public void adminProjectTestWithAdminPermission() { ProjectInstance project = name2Projects.get(TEST_PROJECT); boolean result = aclEvaluate.hasProjectAdminPermission(project); Assert.assertTrue(result); } /** * zhangqiang admin kylin_project failed */ @Test @WithMockUser(username = ZHANGQIANG, roles = { ROLE_USER }) public void adminProjectKylinWithoutPermission() { ProjectInstance project = name2Projects.get(KYLIN_PROJECT); boolean result = aclEvaluate.hasProjectAdminPermission(project); Assert.assertFalse(result); } /** * yuwen admin test_project failed */ @Test @WithMockUser(username = YUWEN, roles = { ROLE_USER }) public void adminProjectTestWithoutPermission() { ProjectInstance project = name2Projects.get(TEST_PROJECT); boolean result = aclEvaluate.hasProjectAdminPermission(project); Assert.assertFalse(result); } /** * yuwen admin kylin_project failed */ @Test @WithMockUser(username = YUWEN, roles = { ROLE_USER }) public void adminProjectKylinWithoutPermission2() { ProjectInstance project = name2Projects.get(KYLIN_PROJECT); boolean result = aclEvaluate.hasProjectAdminPermission(project); Assert.assertFalse(result); } /** * yuwen admin learn_project failed */ @Test @WithMockUser(username = YUWEN, roles = { ROLE_USER }) public void adminProjectLearnWithoutPermission() { ProjectInstance project = name2Projects.get(LEARN_PROJECT); boolean result = aclEvaluate.hasProjectAdminPermission(project); Assert.assertFalse(result); } // No.4 hasProjectAdminPermission test end }