http://git-wip-us.apache.org/repos/asf/sentry/blob/bfb354f2/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestPolicyImportExport.java ---------------------------------------------------------------------- diff --git a/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestPolicyImportExport.java b/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestPolicyImportExport.java new file mode 100644 index 0000000..c72aea3 --- /dev/null +++ b/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestPolicyImportExport.java @@ -0,0 +1,195 @@ +/* + * Copyright 2014 The Apache Software Foundation. + * + * Licensed 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.sentry.tests.e2e.hive; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.File; +import java.io.FileOutputStream; +import java.util.Map; +import java.util.Set; + +import org.apache.sentry.binding.hive.SentryPolicyFileFormatFactory; +import org.apache.sentry.binding.hive.SentryPolicyFileFormatter; +import org.apache.sentry.binding.hive.authz.SentryConfigTool; +import org.apache.sentry.policy.common.PolicyConstants; +import org.apache.sentry.provider.common.PolicyFileConstants; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.common.io.Resources; + +public class TestPolicyImportExport extends AbstractTestWithStaticConfiguration { + + // resources/testPolicyImport.ini is used for the import test and all the following + // privileges(PRIVILIEGE1...8) are defined the same as in testPolicyImport.ini, used for verifying + // the test result. + public static String PRIVILIEGE1 = "server=server1"; + public static String PRIVILIEGE2 = "server=server1->action=select->grantoption=false"; + public static String PRIVILIEGE3 = "server=server1->db=db2->action=insert->grantoption=true"; + public static String PRIVILIEGE4 = "server=server1->db=db1->table=tbl1->action=insert"; + public static String PRIVILIEGE5 = "server=server1->db=db1->table=tbl2->column=col1->action=insert"; + public static String PRIVILIEGE6 = "server=server1->db=db1->table=tbl3->column=col1->action=*->grantoption=true"; + public static String PRIVILIEGE7 = "server=server1->db=db1->table=tbl4->column=col1->action=all->grantoption=true"; + public static String PRIVILIEGE8 = "server=server1->uri=hdfs://testserver:9999/path2->action=insert"; + + private SentryConfigTool configTool; + private Map<String, Map<String, Set<String>>> policyFileMappingData; + + @BeforeClass + public static void setupTestStaticConfiguration() throws Exception{ + useSentryService = true; + // add current user to admin group to get the permission for import/export + String requestorUserName = System.getProperty("user.name", ""); + StaticUserGroup.getStaticMapping().put(requestorUserName, ADMINGROUP); + AbstractTestWithStaticConfiguration.setupTestStaticConfiguration(); + } + + @Before + public void setup() throws Exception { + configTool = new SentryConfigTool(); + configTool.setPolicyFile(context.getPolicyFile().getPath()); + configTool.setupConfig(); + importAdminPrivilege(); + } + + private void importAdminPrivilege() throws Exception { + prepareForImport("testPolicyImportAdmin.ini"); + configTool.importPolicy(); + } + + private void prepareExceptedData() { + // test data for: + // [groups] + // group1=roleImport1,roleImport2 + // group2=roleImport1,roleImport2,roleImport3 + // group3=roleImport2,roleImport3 + // [roles] + // roleImport1=privilege1,privilege2,privilege3,privilege4 + // roleImport2=privilege3,privilege4,privilege5,privilege6 + // roleImport3=privilege5,privilege6,privilege7,privilege8 + policyFileMappingData = Maps.newHashMap(); + Map<String, Set<String>> groupRolesMap = Maps.newHashMap(); + Map<String, Set<String>> rolePrivilegesMap = Maps.newHashMap(); + groupRolesMap.put("group1", Sets.newHashSet("roleimport1", "roleimport2")); + groupRolesMap.put("group2", Sets.newHashSet("roleimport1", "roleimport2", "roleimport3")); + groupRolesMap.put("group3", Sets.newHashSet("roleimport2", "roleimport3")); + // the adminrole is defined in testPolicyImportAdmin.ini + groupRolesMap.put("admin", Sets.newHashSet("adminrole")); + rolePrivilegesMap.put("roleimport1", + Sets.newHashSet(PRIVILIEGE1, PRIVILIEGE2, PRIVILIEGE3, PRIVILIEGE4)); + rolePrivilegesMap.put("roleimport2", + Sets.newHashSet(PRIVILIEGE3, PRIVILIEGE4, PRIVILIEGE5, PRIVILIEGE6)); + rolePrivilegesMap.put("roleimport3", + Sets.newHashSet(PRIVILIEGE5, PRIVILIEGE6, PRIVILIEGE7, PRIVILIEGE8)); + // the adminrole is defined in testPolicyImportAdmin.ini + rolePrivilegesMap.put("adminrole", Sets.newHashSet(PRIVILIEGE1)); + policyFileMappingData.put(PolicyFileConstants.GROUPS, groupRolesMap); + policyFileMappingData.put(PolicyFileConstants.ROLES, rolePrivilegesMap); + + } + + @Test + public void testImportExportPolicy() throws Exception { + String importFileName = "testPolicyImport.ini"; + String exportFileName = "testPolicyExport.ini"; + File importFile = new File(dataDir, importFileName); + File exportFile = new File(dataDir, exportFileName); + FileOutputStream to = new FileOutputStream(importFile); + Resources.copy(Resources.getResource(importFileName), to); + to.close(); + configTool.setImportPolicyFilePath(importFile.getAbsolutePath()); + configTool.importPolicy(); + + configTool.setExportPolicyFilePath(exportFile.getAbsolutePath()); + configTool.exportPolicy(); + + SentryPolicyFileFormatter sentryPolicyFileFormatter = SentryPolicyFileFormatFactory + .createFileFormatter(configTool.getAuthzConf()); + Map<String, Map<String, Set<String>>> exportMappingData = sentryPolicyFileFormatter.parse( + exportFile.getAbsolutePath(), configTool.getAuthzConf()); + + prepareExceptedData(); + validateSentryMappingData(exportMappingData, policyFileMappingData); + } + + @Test + public void testImportExportPolicyForError() throws Exception { + prepareForImport("testPolicyImportError.ini"); + try { + configTool.importPolicy(); + fail("IllegalArgumentException should be thrown for: Invalid key value: server [server]"); + } catch (IllegalArgumentException ex) { + // ignore + } + } + + private void prepareForImport(String resorceName) throws Exception { + File importFile = new File(dataDir, resorceName); + FileOutputStream to = new FileOutputStream(importFile); + Resources.copy(Resources.getResource(resorceName), to); + to.close(); + configTool.setImportPolicyFilePath(importFile.getAbsolutePath()); + } + + // verify the mapping data + public void validateSentryMappingData(Map<String, Map<String, Set<String>>> actualMappingData, + Map<String, Map<String, Set<String>>> expectedMappingData) { + validateGroupRolesMap(actualMappingData.get(PolicyFileConstants.GROUPS), + expectedMappingData.get(PolicyFileConstants.GROUPS)); + validateRolePrivilegesMap(actualMappingData.get(PolicyFileConstants.ROLES), + expectedMappingData.get(PolicyFileConstants.ROLES)); + } + + // verify the mapping data for [group,role] + private void validateGroupRolesMap(Map<String, Set<String>> actualMap, + Map<String, Set<String>> expectedMap) { + assertEquals(expectedMap.keySet().size(), actualMap.keySet().size()); + for (String groupName : actualMap.keySet()) { + Set<String> actualRoles = actualMap.get(groupName); + Set<String> expectedRoles = expectedMap.get(groupName); + assertEquals(actualRoles.size(), expectedRoles.size()); + assertTrue(actualRoles.equals(expectedRoles)); + } + } + + // verify the mapping data for [role,privilege] + private void validateRolePrivilegesMap(Map<String, Set<String>> actualMap, + Map<String, Set<String>> expectedMap) { + assertEquals(expectedMap.keySet().size(), actualMap.keySet().size()); + for (String roleName : actualMap.keySet()) { + Set<String> actualPrivileges = actualMap.get(roleName); + Set<String> exceptedPrivileges = expectedMap.get(roleName); + assertEquals(exceptedPrivileges.size(), actualPrivileges.size()); + for (String actualPrivilege : actualPrivileges) { + boolean isFound = exceptedPrivileges.contains(actualPrivilege); + if (!isFound) { + String withOptionPrivilege = PolicyConstants.AUTHORIZABLE_JOINER.join(actualPrivilege, + PolicyConstants.KV_JOINER.join(PolicyFileConstants.PRIVILEGE_GRANT_OPTION_NAME, + "false")); + isFound = exceptedPrivileges.contains(withOptionPrivilege); + } + assertTrue(isFound); + } + } + } +}
http://git-wip-us.apache.org/repos/asf/sentry/blob/bfb354f2/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestPrivilegeAtTransform.java ---------------------------------------------------------------------- diff --git a/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestPrivilegeAtTransform.java b/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestPrivilegeAtTransform.java new file mode 100644 index 0000000..310610e --- /dev/null +++ b/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestPrivilegeAtTransform.java @@ -0,0 +1,118 @@ +/* + * 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.sentry.tests.e2e.hive; + +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.io.FileOutputStream; +import java.sql.Connection; +import java.sql.Statement; + +import org.apache.sentry.provider.file.PolicyFile; +import org.junit.Before; +import org.junit.Test; + +import com.google.common.io.Resources; + +public class TestPrivilegeAtTransform extends AbstractTestWithStaticConfiguration { + private final String SINGLE_TYPE_DATA_FILE_NAME = "kv1.dat"; + private File dataDir; + private File dataFile; + private PolicyFile policyFile; + + @Override + @Before + public void setup() throws Exception { + dataDir = context.getDataDir(); + dataFile = new File(dataDir, SINGLE_TYPE_DATA_FILE_NAME); + FileOutputStream to = new FileOutputStream(dataFile); + Resources.copy(Resources.getResource(SINGLE_TYPE_DATA_FILE_NAME), to); + to.close(); + policyFile = PolicyFile.setAdminOnServer1(ADMINGROUP); + } + + /** + * Steps: + * 1. admin create database, create table, load data into it + * 2. all@server can issue transforms command + * 3. all@database cannot issue transform command + * 4. insert@table select@table cannot issue transform command + * 5. select@view cannot issue transform command + * 6. transform@server can issue the transform command + * 7. non-admin user with URI privilege on transform can execute query + */ + @Test + public void testTransform1() throws Exception { + policyFile + .addPermissionsToRole("all_db1", "server=server1->db=" + DB1) + .addPermissionsToRole("transform_uri", "server=server1->uri=file:///bin/cat") + .addRolesToGroup(USERGROUP1, "all_db1") + .addRolesToGroup(USERGROUP2, "all_db1", "transform_uri") + .setUserGroupMapping(StaticUserGroup.getStaticMapping()); + writePolicyFile(policyFile); + + // verify by SQL + // 1, 2 + String tableName1 = "tb_1"; + String query = "select TRANSFORM(a.under_col, a.value) " + + "USING '/bin/cat' AS (tunder_col, tvalue) FROM " + DB1 + "." + tableName1 + " a"; + Connection connection = context.createConnection(ADMIN1); + Statement statement = context.createStatement(connection); + statement.execute("DROP DATABASE IF EXISTS " + DB1 + " CASCADE"); + statement.execute("CREATE DATABASE " + DB1); + statement.execute("DROP TABLE IF EXISTS " + DB1 + "." + tableName1); + statement.execute("create table " + DB1 + "." + tableName1 + + " (under_col int, value string)"); + statement.execute("load data local inpath '" + dataFile.getPath() + + "' into table " + DB1 + "." + tableName1); + assertTrue(query, statement.execute(query)); + + statement.close(); + connection.close(); + + // 3 + connection = context.createConnection(USER1_1); + statement = context.createStatement(connection); + context.assertAuthzException(statement, query); + + // 4 + policyFile + .addPermissionsToRole("select_tb1", "server=server1->db=" + DB1 + "->table=tb_1->action=select") + .addPermissionsToRole("insert_tb1", "server=server1->db=" + DB1 + "->table=tb_1->action=insert") + .addRolesToGroup(USERGROUP1, "select_tb1", "insert_tb1"); + writePolicyFile(policyFile); + context.assertAuthzException(statement, query); + + // 5 + policyFile + .addPermissionsToRole("all_server1", "server=server1") + .addRolesToGroup(USERGROUP1, "all_server1"); + writePolicyFile(policyFile); + assertTrue(query, statement.execute(query)); + statement.close(); + connection.close(); + + connection = context.createConnection(USER2_1); + statement = context.createStatement(connection); + assertTrue(query, statement.execute(query)); + statement.close(); + connection.close(); + + } +} http://git-wip-us.apache.org/repos/asf/sentry/blob/bfb354f2/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestPrivilegesAtColumnScope.java ---------------------------------------------------------------------- diff --git a/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestPrivilegesAtColumnScope.java b/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestPrivilegesAtColumnScope.java new file mode 100644 index 0000000..9aac78c --- /dev/null +++ b/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestPrivilegesAtColumnScope.java @@ -0,0 +1,518 @@ +/* + * 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.sentry.tests.e2e.hive; + +import java.io.File; +import java.io.FileOutputStream; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; + +import org.junit.Assert; + +import org.apache.sentry.provider.file.PolicyFile; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.google.common.io.Resources; + +/* Tests privileges at column scope within a single database. + */ + +public class TestPrivilegesAtColumnScope extends AbstractTestWithStaticConfiguration { + + private static PolicyFile policyFile; + private final static String MULTI_TYPE_DATA_FILE_NAME = "emp.dat"; + + @Before + public void setup() throws Exception { + policyFile = super.setupPolicy(); + super.setup(); + prepareDBDataForTest(); + } + + @BeforeClass + public static void setupTestStaticConfiguration() throws Exception { + AbstractTestWithStaticConfiguration.setupTestStaticConfiguration(); + } + + private static void prepareDBDataForTest() throws Exception { + // copy data file to test dir + File dataDir = context.getDataDir(); + File dataFile = new File(dataDir, MULTI_TYPE_DATA_FILE_NAME); + FileOutputStream to = new FileOutputStream(dataFile); + Resources.copy(Resources.getResource(MULTI_TYPE_DATA_FILE_NAME), to); + to.close(); + + // setup db objects needed by the test + Connection connection = context.createConnection(ADMIN1); + Statement statement = context.createStatement(connection); + + statement.execute("DROP DATABASE IF EXISTS DB_1 CASCADE"); + statement.execute("CREATE DATABASE DB_1"); + statement.execute("USE DB_1"); + statement.execute("CREATE TABLE TAB_1(A STRING, B STRING)"); + statement.execute("LOAD DATA LOCAL INPATH '" + dataFile.getPath() + "' INTO TABLE TAB_1"); + statement.execute("CREATE VIEW VIEW_1(A,B) AS SELECT A,B FROM TAB_1"); + statement.execute("CREATE TABLE TAB_2(A STRING, B STRING)"); + statement.execute("LOAD DATA LOCAL INPATH '" + dataFile.getPath() + "' INTO TABLE TAB_2"); + statement.execute("CREATE VIEW VIEW_2(A,B) AS SELECT A,B FROM TAB_2"); + //create table with partitions + statement.execute("CREATE TABLE TAB_3 (A STRING, B STRING) partitioned by (C STRING)"); + statement.execute("ALTER TABLE TAB_3 ADD PARTITION (C=1)"); + statement.execute("ALTER TABLE TAB_3 ADD PARTITION (C=2)"); + statement.execute("LOAD DATA LOCAL INPATH '" + dataFile.getPath() + "' INTO TABLE TAB_3 PARTITION (C=1)"); + statement.execute("LOAD DATA LOCAL INPATH '" + dataFile.getPath() + "' INTO TABLE TAB_3 PARTITION (C=2)"); + statement.close(); + connection.close(); + } + + /* + * Admin creates database DB_1, table TAB_1, TAB_2 in DB_1, loads data into + * TAB_1, TAB_2. + * Admin grants SELECT on just one column of TAB_1, TAB_2 to USER_GROUP1 of which + * user1 is a member. + * Admin grants SELECT on all column of TAB_1, TAB_2 to USER_GROUP2 of which + * user2 is a member. + */ + @Test + public void testSelectColumnOnTable() throws Exception { + policyFile + .addRolesToGroup(USERGROUP1, "select_tab1_A", "select_tab2_A") + .addRolesToGroup(USERGROUP2, "select_tab1_A", "select_tab1_B", "select_tab2_A", "select_tab2_B") + .addPermissionsToRole("select_tab1_A", "server=server1->db=DB_1->table=TAB_1->column=A->action=select") + .addPermissionsToRole("select_tab1_B", "server=server1->db=DB_1->table=TAB_1->column=B->action=select") + .addPermissionsToRole("select_tab2_A", "server=server1->db=DB_1->table=TAB_2->column=A->action=select") + .addPermissionsToRole("select_tab2_B", "server=server1->db=DB_1->table=TAB_2->column=B->action=select") + .setUserGroupMapping(StaticUserGroup.getStaticMapping()); + writePolicyFile(policyFile); + + // test execution on user1 + Connection connection = context.createConnection(USER1_1); + Statement statement = context.createStatement(connection); + statement.execute("USE DB_1"); + + // test user can execute query count on column A on tab_1 + statement.executeQuery("SELECT COUNT(A) FROM TAB_1"); + + // test user can execute query column A on tab_1 + statement.executeQuery("SELECT A FROM TAB_1"); + + // negative test: test user can't execute query count of column B on tab_1 + try { + statement.execute("SELECT COUNT(B) FROM TAB_1"); + Assert.fail("Expected SQL exception"); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + + // negative test: test user can't execute query column B on tab_1 + try { + statement.execute("SELECT B FROM TAB_1"); + Assert.fail("Expected SQL exception"); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + + // negative test: test user can't query view + try { + statement.execute("SELECT COUNT(A) FROM VIEW_1"); + Assert.fail("Expected SQL exception"); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + statement.close(); + connection.close(); + + // test execution on user2 + connection = context.createConnection(USER2_1); + statement = context.createStatement(connection); + statement.execute("USE DB_1"); + // test user can execute query count of column A on tab_1 + statement.executeQuery("SELECT COUNT(A) FROM TAB_1"); + + // test user can execute query count of column B on tab_1 + statement.executeQuery("SELECT COUNT(B) FROM TAB_1"); + + // test user can't execute query count using * on tab_1 + try { + statement.execute("SELECT COUNT(*) FROM TAB_1"); + Assert.fail("Expected SQL exception"); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + + // test user can execute SELECT * on tab_1 + statement.executeQuery("SELECT * FROM TAB_1"); + + statement.close(); + connection.close(); + } + + /* + * Admin creates database DB_1, table TAB_1, TAB_2 in DB_1, loads data into + * TAB_1, TAB_2. Admin view on TAB_1 and TAB_2 + * Admin grants SELECT on just one column of VIEW_1, VIEW_2 to USER_GROUP1 of which + * user1 is a member. + * Admin grants SELECT on all column of TAB_1, TAB_2 to USER_GROUP2 of which + * user2 is a member. + * Note: We don't support column level privilege on VIEW + */ + @Test + public void testSelectColumnOnView() throws Exception { + policyFile + .addRolesToGroup(USERGROUP1, "select_view1_A", "select_view2_A") + .addRolesToGroup(USERGROUP2, "select_view1_A", "select_view1_B", "select_view2_A", "select_view2_B") + .addPermissionsToRole("select_view1_A", "server=server1->db=DB_1->table=VIEW_1->column=A->action=select") + .addPermissionsToRole("select_view1_B", "server=server1->db=DB_1->table=VIEW_1->column=B->action=select") + .addPermissionsToRole("select_view2_A", "server=server1->db=DB_1->table=VIEW_2->column=A->action=select") + .addPermissionsToRole("select_view2_B", "server=server1->db=DB_1->table=VIEW_2->column=B->action=select") + .setUserGroupMapping(StaticUserGroup.getStaticMapping()); + writePolicyFile(policyFile); + + // test execution on user1 + Connection connection = context.createConnection(USER1_1); + Statement statement = context.createStatement(connection); + statement.execute("USE DB_1"); + // negative test: test user can't execute query count of column B on tab_1 + try { + statement.execute("SELECT COUNT(B) FROM TAB_1"); + Assert.fail("Expected SQL exception"); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + // negative test: test user can't execute query count of column A on tab_1 + try { + statement.execute("SELECT COUNT(A) FROM TAB_1"); + Assert.fail("Expected SQL exception"); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + + // negative test: test user can't query column of view + try { + statement.execute("SELECT COUNT(A) FROM VIEW_1"); + Assert.fail("Expected SQL exception"); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + // negative test: test user can't query column of view + try { + statement.execute("SELECT COUNT(B) FROM VIEW_1"); + Assert.fail("Expected SQL exception"); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + + statement.close(); + connection.close(); + + // test execution on user2 + connection = context.createConnection(USER2_1); + statement = context.createStatement(connection); + statement.execute("USE DB_1"); + // test user can execute query count of column A on tab_1 + try { + statement.execute("SELECT COUNT(A) FROM TAB_1"); + Assert.fail("Expected SQL Exception"); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + // test user can execute query count of column B on tab_1 + try { + statement.execute("SELECT COUNT(B) FROM TAB_1"); + Assert.fail("Expected SQL Exception"); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + // test user can't execute query count using * on tab_1 + try { + statement.execute("SELECT COUNT(*) FROM TAB_1"); + Assert.fail("Expected SQL exception"); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + try { + statement.execute("SELECT * FROM TAB_1"); + Assert.fail("Expected SQL Exception"); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + + // negative test: test user can't query view + try { + statement.execute("SELECT COUNT(A) FROM VIEW_1"); + Assert.fail("Expected SQL exception"); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + try { + statement.execute("SELECT COUNT(B) FROM VIEW_1"); + Assert.fail("Expected SQL exception"); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + + // negative test: test user can't create a new view + try { + statement.execute("CREATE VIEW VIEW_2(A) AS SELECT A FROM TAB_1"); + Assert.fail("Expected SQL Exception"); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + statement.close(); + connection.close(); + } + + /* + * Admin creates database DB_1, table TAB_1, TAB_2 in DB_1, VIEW_1 on TAB_1 + * loads data into TAB_1, TAB_2. Admin grants SELECT on TAB_1,TAB_2 to + * USER_GROUPS. All test cases in this method will do the authorization on the condition of join + * or where clause + */ + @Test + public void testSelectColumnOnTableJoin() throws Exception { + policyFile + .addRolesToGroup(USERGROUP1, "select_tab1_A", "select_tab1_B", "select_tab2_B") + .addPermissionsToRole("select_tab1_A", "server=server1->db=DB_1->table=TAB_1->column=A->action=select") + .addPermissionsToRole("select_tab1_B", "server=server1->db=DB_1->table=TAB_1->column=B->action=select") + .addPermissionsToRole("select_tab2_B", "server=server1->db=DB_1->table=TAB_2->column=B->action=select") + .setUserGroupMapping(StaticUserGroup.getStaticMapping()); + writePolicyFile(policyFile); + + // test execution user1 + Connection connection = context.createConnection(USER1_1); + Statement statement = context.createStatement(connection); + statement.execute("USE DB_1"); + + // test user can execute query TAB_1 JOIN TAB_2, do the column authorization on the condition of + // join clause + statement + .executeQuery("SELECT COUNT(T1.B) FROM TAB_1 T1 JOIN TAB_2 T2 ON T1.B = T2.B AND T1.A = '21' "); + + // negative test: test user can't execute query if do the column authorization on the condition + // of join clause failed + try { + statement + .execute("SELECT COUNT(T1.B) FROM TAB_1 T1 JOIN TAB_2 T2 ON T1.B = T2.B AND T1.A = '21' AND T2.A = '21'"); + Assert.fail("Expected SQL Exception"); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + + // test user can execute query TAB_1 JOIN TAB_2, do the column authorization on the condition of + // where clause + statement + .executeQuery("SELECT T1.* FROM TAB_1 T1, TAB_2 T2 WHERE T1.B = T2.B AND T1.A = '21'"); + + // negative test: test user can't execute query if do the column authorization on the condition + // of where clause failed + try { + statement + .execute("SELECT T1.* FROM TAB_1 T1, TAB_2 T2 WHERE T1.B = T2.B AND T1.A = '21' AND T2.A = '21'"); + Assert.fail("Expected SQL Exception"); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + + try { + statement + .execute("SELECT T1.* FROM TAB_1 T1, TAB_2 T2 WHERE T1.B = T2.B AND T1.A = '21' AND T2.A = '21'"); + Assert.fail("Expected SQL Exception"); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + + // negative test: test user can't execute query VIEW_1 JOIN TAB_2 + try { + statement.executeQuery("SELECT COUNT(*) FROM VIEW_1 V1 JOIN TAB_2 T2 ON (V1.B = T2.B)"); + Assert.fail("Expected SQL exception"); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + + statement.close(); + connection.close(); + } + + /* + * Admin creates database DB_1, table TAB_1, TAB_2 in DB_1, loads data into + * TAB_1, TAB_2. Admin view on TAB_1 and TAB_2 + * Admin grants SELECT on just one column of VIEW_1, VIEW_2 to USER_GROUP1 of which + * user1 is a member. + * Admin grants SELECT on all column of TAB_1, TAB_2 to USER_GROUP2 of which + * user2 is a member. + * Note: We don't support column level privilege on VIEW + */ + @Test + public void testSelectColumnOnViewJoin() throws Exception { + policyFile + .addRolesToGroup(USERGROUP1, "select_view1_A", "select_view1_B", "select_view2_B") + .addRolesToGroup(USERGROUP2, "select_view1_B", "select_view2_B") + .addRolesToGroup(USERGROUP3, "select_view1_B", "select_view2_A") + .addPermissionsToRole("select_view1_A", "server=server1->db=DB_1->table=VIEW_1->column=A->action=select") + .addPermissionsToRole("select_view1_B", "server=server1->db=DB_1->table=VIEW_1->column=B->action=select") + .addPermissionsToRole("select_view2_A", "server=server1->db=DB_1->table=VIEW_2->column=A->action=select") + .addPermissionsToRole("select_view2_B", "server=server1->db=DB_1->table=VIEW_2->column=B->action=select") + .setUserGroupMapping(StaticUserGroup.getStaticMapping()); + writePolicyFile(policyFile); + + // test execution + Connection connection = context.createConnection(USER1_1); + Statement statement = context.createStatement(connection); + statement.execute("USE DB_1"); + + // test user can't execute query VIEW_1 JOIN VIEW_2 + try { + statement.execute("SELECT COUNT(*) FROM VIEW_1 V1 JOIN VIEW_2 V2 ON (V1.B = V2.B)"); + Assert.fail("Expected SQL Exception"); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + + // test user can't execute query VIEW_1 JOIN TAB_2 + try { + statement.execute("SELECT COUNT(*) FROM VIEW_1 V1 JOIN TAB_2 T2 ON (V1.B = T2.B)"); + Assert.fail("Expected SQL Exception"); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + + // test user can't execute query TAB_1 JOIN TAB_2 + try { + statement.execute("SELECT COUNT(*) FROM TAB_1 T1 JOIN TAB_2 T2 ON (T1.B = T2.B)"); + Assert.fail("Expected SQL exception"); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + + statement.close(); + connection.close(); + } + + /* + * Admin creates database DB_1, table TAB_1, TAB_2 in DB_1, loads data into + * TAB_1, TAB_2. Admin view on TAB_1 and TAB_2 + * Admin grants SELECT on just one column of VIEW_1, VIEW_2 to USER_GROUP1 of which + * user1 is a member. + * Admin grants SELECT on all column of TAB_1, TAB_2 to USER_GROUP2 of which + * user2 is a member. + * Note: We don't support column level privilege on VIEW + */ + @Test + public void testSelectColumnOnTableViewJoin() throws Exception { + policyFile + .addRolesToGroup(USERGROUP1, "select_tab1_A", "select_tab1_B", "select_view2_B") + .addPermissionsToRole("select_tab1_A", "server=server1->db=DB_1->table=VIEW_1->column=A->action=select") + .addPermissionsToRole("select_tab1_B", "server=server1->db=DB_1->table=VIEW_1->column=B->action=select") + .addPermissionsToRole("select_view2_B", "server=server1->db=DB_1->table=VIEW_2->column=B->action=select") + .setUserGroupMapping(StaticUserGroup.getStaticMapping()); + writePolicyFile(policyFile); + + // test execution + Connection connection = context.createConnection(USER1_1); + Statement statement = context.createStatement(connection); + statement.execute("USE DB_1"); + + // test user can't execute query VIEW_1 JOIN TAB_2 + try { + statement.execute("SELECT COUNT(*) FROM VIEW_1 V1 JOIN TAB_2 T2 ON (V1.B = T2.B)"); + Assert.fail("Expected SQL Exception"); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + + // test user can't execute query VIEW_1 JOIN VIEW_2 + try { + statement.execute("SELECT COUNT(*) FROM VIEW_1 V1 JOIN VIEW_2 V2 ON (V1.B = V2.B)"); + Assert.fail("Expected SQL Exception"); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + + // test user can't execute query TAB_1 JOIN TAB_2 + try { + statement.execute("SELECT COUNT(*) FROM TAB_1 T1 JOIN TAB_2 T2 ON (T1.B = T2.B)"); + Assert.fail("Expected SQL exception"); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + + statement.close(); + connection.close(); + } + + @Test + public void testPartition() throws Exception{ + policyFile + .addRolesToGroup(USERGROUP1, "select_tab3_A", "select_tab3_C") + .addRolesToGroup(USERGROUP2, "select_tab3_A") + .addRolesToGroup(USERGROUP3, "select_tab3_C") + .addPermissionsToRole("select_tab3_A", "server=server1->db=DB_1->table=TAB_3->column=A->action=select") + .addPermissionsToRole("select_tab3_C", "server=server1->db=DB_1->table=TAB_3->column=C->action=select") + .setUserGroupMapping(StaticUserGroup.getStaticMapping()); + writePolicyFile(policyFile); + + // Users with privileges on partition column can access it + String [] positiveUsers = {USER1_1, USER3_1}; + for(String user:positiveUsers) { + Connection connection = context.createConnection(user); + Statement statement = context.createStatement(connection); + statement.execute("USE DB_1"); + statement.execute("SELECT C FROM TAB_3"); + statement.close(); + connection.close(); + } + + // Users with out privileges on partition column can not access it + Connection connection = context.createConnection(USER2_1); + Statement statement = context.createStatement(connection); + statement.execute("USE DB_1"); + try { + statement.execute("SELECT C FROM TAB_3"); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + statement.close(); + connection.close(); + } + + @Test + public void testMultipleColsPerRole() throws Exception { + + policyFile + .addRolesToGroup(USERGROUP1, "select_tab1_AB") + .addPermissionsToRole("select_tab1_AB", "server=server1->db=DB_1->table=TAB_1->column=A->action=select") + .addPermissionsToRole("select_tab1_AB", "server=server1->db=DB_1->table=TAB_1->column=B->action=select") + .setUserGroupMapping(StaticUserGroup.getStaticMapping()); + writePolicyFile(policyFile); + + // test execution on user1 + Connection connection = context.createConnection(USER1_1); + Statement statement = context.createStatement(connection); + statement.execute("USE DB_1"); + + // test user can execute query count on column A on tab_1 + statement.executeQuery("SELECT A,B FROM TAB_1"); + + statement.close(); + connection.close(); + } + +} http://git-wip-us.apache.org/repos/asf/sentry/blob/bfb354f2/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestPrivilegesAtDatabaseScope.java ---------------------------------------------------------------------- diff --git a/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestPrivilegesAtDatabaseScope.java b/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestPrivilegesAtDatabaseScope.java new file mode 100644 index 0000000..b28b6f4 --- /dev/null +++ b/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestPrivilegesAtDatabaseScope.java @@ -0,0 +1,399 @@ +/* + * 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.sentry.tests.e2e.hive; + +import org.apache.sentry.provider.file.PolicyFile; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.io.FileOutputStream; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.HashMap; +import java.util.Map; + +import org.junit.Assert; + +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.google.common.io.Resources; + +/* Tests privileges at table scope within a single database. + */ + +public class TestPrivilegesAtDatabaseScope extends AbstractTestWithStaticConfiguration { + private PolicyFile policyFile; + + Map <String, String >testProperties; + private static final String SINGLE_TYPE_DATA_FILE_NAME = "kv1.dat"; + + @BeforeClass + public static void setupTestStaticConfiguration () throws Exception { + AbstractTestWithStaticConfiguration.setupTestStaticConfiguration(); + } + + @Override + @Before + public void setup() throws Exception { + policyFile = super.setupPolicy(); + super.setup(); + testProperties = new HashMap<String, String>(); + } + + // SENTRY-285 test + @Test + public void testAllOnDb() throws Exception { + // setup db objects needed by the test + Connection connection = context.createConnection(ADMIN1); + Statement statement = context.createStatement(connection); + statement.execute("create database " + DB1); + statement.execute("create table " + DB1 + ".tab1(a int)"); + + policyFile + .addRolesToGroup(USERGROUP1, "all_db1") + .addPermissionsToRole("all_db1", "server=server1->db=" + DB1 + "->action=all") + .setUserGroupMapping(StaticUserGroup.getStaticMapping()); + writePolicyFile(policyFile); + + connection = context.createConnection(USER1_1); + statement = context.createStatement(connection); + statement.execute("use " + DB1); + statement.execute("select * from tab1"); + + policyFile + .addPermissionsToRole("all_db1", "server=server1->db=" + DB1); + writePolicyFile(policyFile); + statement.execute("use " + DB1); + statement.execute("select * from tab1"); + } + + + /* Admin creates database DB_1 + * Admin grants ALL to USER_GROUP of which user1 is a member. + */ + @Test + public void testAllPrivilege() throws Exception { + + //copy data file to test dir + File dataDir = context.getDataDir(); + File dataFile = new File(dataDir, SINGLE_TYPE_DATA_FILE_NAME); + FileOutputStream to = new FileOutputStream(dataFile); + Resources.copy(Resources.getResource(SINGLE_TYPE_DATA_FILE_NAME), to); + to.close(); + + // setup db objects needed by the test + Connection connection = context.createConnection(ADMIN1); + Statement statement = context.createStatement(connection); + statement.execute("CREATE DATABASE " + DB1); + statement.execute("CREATE DATABASE " + DB2); + statement.close(); + connection.close(); + + policyFile + .addRolesToGroup(USERGROUP1, "all_db1", "load_data") + .addRolesToGroup(USERGROUP2, "all_db2") + .addPermissionsToRole("all_db1", "server=server1->db=" + DB1) + .addPermissionsToRole("all_db2", "server=server1->db=" + DB2) + .addPermissionsToRole("load_data", "server=server1->uri=file://" + dataFile.getPath()) + .setUserGroupMapping(StaticUserGroup.getStaticMapping()); + writePolicyFile(policyFile); + + // test execution + connection = context.createConnection(USER1_1); + statement = context.createStatement(connection); + // test user can create table + statement.execute("CREATE TABLE " + DB1 + ".TAB_1(A STRING)"); + // test user can execute load + statement.execute("LOAD DATA LOCAL INPATH '" + dataFile.getPath() + "' INTO TABLE " + DB1 + ".TAB_1"); + statement.execute("CREATE TABLE " + DB1 + ".TAB_2(A STRING)"); + statement.execute("LOAD DATA LOCAL INPATH '" + dataFile.getPath() + "' INTO TABLE " + DB1 + ".TAB_2"); + + // test CTAS can reference UDFs + statement.execute("USE " + DB1); + statement.execute("create table table2 as select A, count(A) from TAB_1 GROUP BY A"); + + // test user can switch db + statement.execute("USE " + DB1); + //test user can create view + statement.execute("CREATE VIEW VIEW_1(A) AS SELECT A FROM TAB_1"); + + // test user can insert + statement.execute("INSERT INTO TABLE TAB_1 SELECT A FROM TAB_2"); + // test user can query table + ResultSet resultSet = statement.executeQuery("SELECT COUNT(A) FROM TAB_1"); + int count = 0; + int countRows = 0; + + while (resultSet.next()) { + count = resultSet.getInt(1); + countRows++; + } + assertTrue("Incorrect row count", countRows == 1); + assertTrue("Incorrect result", count == 1000); + + // test user can execute alter table rename + statement.execute("ALTER TABLE TAB_1 RENAME TO TAB_3"); + + // test user can execute create as select + statement.execute("CREATE TABLE TAB_4 AS SELECT * FROM TAB_2"); + + // test user can execute alter table rename cols + statement.execute("ALTER TABLE TAB_3 ADD COLUMNS (B INT)"); + + // test user can drop table + statement.execute("DROP TABLE TAB_3"); + + //negative test case: user can't drop another user's database + try { + statement.execute("DROP DATABASE " + DB2 + " CASCADE"); + Assert.fail("Expected SQL exception"); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + + //negative test case: user can't switch into another user's database + try { + statement.execute("USE " + DB2); + Assert.fail("Expected SQL exception"); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + + //User can drop own database + statement.execute("DROP DATABASE " + DB1 + " CASCADE"); + + statement.close(); + connection.close(); + } + + /* Admin creates database DB_1, creates table TAB_1, loads data into it + * Admin grants ALL to USER_GROUP of which user1 is a member. + */ + @Test + public void testAllPrivilegeOnObjectOwnedByAdmin() throws Exception { + + //copy data file to test dir + File dataDir = context.getDataDir(); + File dataFile = new File(dataDir, SINGLE_TYPE_DATA_FILE_NAME); + File externalTblDir = new File(dataDir, "exttab"); + FileOutputStream to = new FileOutputStream(dataFile); + Resources.copy(Resources.getResource(SINGLE_TYPE_DATA_FILE_NAME), to); + to.close(); + + // setup db objects needed by the test + Connection connection = context.createConnection(ADMIN1); + Statement statement = context.createStatement(connection); + statement.execute("CREATE DATABASE " + DB1); + statement.execute("CREATE DATABASE " + DB2); + statement.execute("USE " + DB1); + statement.execute("CREATE TABLE TAB_1(A STRING)"); + statement.execute("LOAD DATA LOCAL INPATH '" + dataFile.getPath() + "' INTO TABLE TAB_1"); + statement.execute("CREATE TABLE PART_TAB_1(A STRING) partitioned by (B INT) STORED AS TEXTFILE"); + statement.execute("LOAD DATA LOCAL INPATH '" + dataFile.getPath() + "' INTO TABLE PART_TAB_1 PARTITION(B=1)"); + statement.execute("LOAD DATA LOCAL INPATH '" + dataFile.getPath() + "' INTO TABLE PART_TAB_1 PARTITION(B=2)"); + statement.close(); + connection.close(); + + policyFile + .addRolesToGroup(USERGROUP1, "all_db1", "load_data", "exttab") + .addRolesToGroup(USERGROUP2, "all_db2") + .addPermissionsToRole("all_db1", "server=server1->db=" + DB1) + .addPermissionsToRole("all_db2", "server=server1->db=" + DB2) + .addPermissionsToRole("exttab", "server=server1->uri=file://" + dataDir.getPath()) + .addPermissionsToRole("load_data", "server=server1->uri=file://" + dataFile.getPath()) + .setUserGroupMapping(StaticUserGroup.getStaticMapping()); + writePolicyFile(policyFile); + + // test execution + connection = context.createConnection(USER1_1); + statement = context.createStatement(connection); + // test user can switch db + statement.execute("USE " + DB1); + // test user can execute load + statement.execute("LOAD DATA LOCAL INPATH '" + dataFile.getPath() + "' INTO TABLE TAB_1"); + statement.execute("CREATE TABLE TAB_2(A STRING)"); + statement.execute("LOAD DATA LOCAL INPATH '" + dataFile.getPath() + "' INTO TABLE TAB_2"); + + //test user can create view + statement.execute("CREATE VIEW VIEW_1(A) AS SELECT A FROM TAB_1"); + + // test user can insert + statement.execute("INSERT INTO TABLE TAB_1 SELECT A FROM TAB_2"); + // test user can query table + ResultSet resultSet = statement.executeQuery("SELECT COUNT(A) FROM TAB_1"); + int count = 0; + int countRows = 0; + + while (resultSet.next()) { + count = resultSet.getInt(1); + countRows++; + } + assertTrue("Incorrect row count", countRows == 1); + assertTrue("Incorrect result", count == 1500); + + // test user can execute alter table rename + statement.execute("ALTER TABLE TAB_1 RENAME TO TAB_3"); + + // test user can drop table + statement.execute("DROP TABLE TAB_3"); + + //positive test case: user can create external tables at given location + assertTrue("Unable to create directory for external table test" , externalTblDir.mkdir()); + statement.execute("CREATE EXTERNAL TABLE EXT_TAB_1(A STRING) STORED AS TEXTFILE LOCATION 'file:"+ + externalTblDir.getAbsolutePath() + "'"); + + //negative test case: user can't execute alter table set location, + // as the user does not have privileges on that location + context.assertSentrySemanticException(statement, "ALTER TABLE TAB_2 SET LOCATION 'file:///tab2'", semanticException); + + statement.close(); + connection.close(); + + connection = context.createConnection(USER2_1); + statement = context.createStatement(connection); + try { + statement.execute("CREATE EXTERNAL TABLE EXT_TAB_1(A STRING) STORED AS TEXTFILE LOCATION 'file:"+ + externalTblDir.getAbsolutePath() + "'"); + Assert.fail("Expected SQL exception"); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + + statement.close(); + connection.close(); + } + + /** + * Test privileges for 'use <db>' + * Admin should be able to run use <db> with server level access + * User with db level access should be able to run use <db> + * User with table level access should be able to run use <db> + * User with no access to that db objects, should NOT be able run use <db> + * @throws Exception + */ + @Test + public void testUseDbPrivilege() throws Exception { + // setup db objects needed by the test + Connection connection = context.createConnection(ADMIN1); + Statement statement = context.createStatement(connection); + statement.execute("CREATE DATABASE " + DB1); + statement.execute("use " + DB1); + statement.execute("CREATE TABLE TAB_1(A STRING)"); + statement.execute("CREATE DATABASE " + DB2); + statement.execute("use " + DB2); + statement.execute("CREATE TABLE TAB_2(A STRING)"); + context.close(); + + policyFile + .addRolesToGroup(USERGROUP1, "all_db1") + .addRolesToGroup(USERGROUP2, "select_db2") + .addRolesToGroup(USERGROUP3, "all_db3") + .addPermissionsToRole("all_db1", "server=server1->db=" + DB1) + .addPermissionsToRole("select_db2", "server=server1->db=" + DB2 + "->table=tab_2->action=select") + .addPermissionsToRole("all_db3", "server=server1->db=DB_3") + .setUserGroupMapping(StaticUserGroup.getStaticMapping()); + writePolicyFile(policyFile); + + // user1 should be able to connect db_1 + connection = context.createConnection(USER1_1); + statement = context.createStatement(connection); + statement.execute("use " + DB1); + context.close(); + + // user2 should not be able to connect db_1 + connection = context.createConnection(USER2_1); + statement = context.createStatement(connection); + try { + statement.execute("use " + DB1); + assertFalse("user2 shouldn't be able switch to " + DB1, true); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + statement.execute("use " + DB2); + context.close(); + + // user3 who is not listed in policy file should not be able to connect db_2 + connection = context.createConnection(USER3_1); + statement = context.createStatement(connection); + try { + statement.execute("use " + DB2); + assertFalse("user3 shouldn't be able switch to " + DB2, true); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + context.close(); + } + + /** + * Test access to default DB with out of box authz config + * All users should be able to switch to default, including the users that don't have any + * privilege on default db objects via policy file + * @throws Exception + */ + @Test + public void testDefaultDbPrivilege() throws Exception { + Connection connection = context.createConnection(ADMIN1); + Statement statement = context.createStatement(connection); + statement.execute("use default"); + statement.execute("create table tab1(a int)"); + statement.execute("CREATE DATABASE " + DB1); + statement.execute("use " + DB1); + statement.execute("CREATE TABLE TAB_1(A STRING)"); + statement.execute("CREATE DATABASE " + DB2); + statement.execute("use " + DB2); + statement.execute("CREATE TABLE TAB_2(A STRING)"); + context.close(); + + policyFile + .addRolesToGroup(USERGROUP1, "all_db1") + .addRolesToGroup(USERGROUP2, "select_db2") + .addRolesToGroup(USERGROUP3, "all_default") + .addPermissionsToRole("all_db1", "server=server1->db=" + DB1) + .addPermissionsToRole("select_db2", "server=server1->db=" + DB2 + "->table=tab_2->action=select") + .addPermissionsToRole("all_default", "server=server1->db=default") + .setUserGroupMapping(StaticUserGroup.getStaticMapping()); + writePolicyFile(policyFile); + + connection = context.createConnection(USER1_1); + statement = context.createStatement(connection); + statement.execute("use default"); + try { + statement.execute("select * from tab1"); + assertTrue("Should not be allowed !!", false); + } catch (Exception e) { + // Ignore + } + context.close(); + + connection = context.createConnection(USER2_1); + statement = context.createStatement(connection); + statement.execute("use default"); + context.close(); + + connection = context.createConnection(USER3_1); + statement = context.createStatement(connection); + statement.execute("use default"); + context.close(); + } + +} http://git-wip-us.apache.org/repos/asf/sentry/blob/bfb354f2/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestPrivilegesAtFunctionScope.java ---------------------------------------------------------------------- diff --git a/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestPrivilegesAtFunctionScope.java b/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestPrivilegesAtFunctionScope.java new file mode 100644 index 0000000..bb8d61d --- /dev/null +++ b/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestPrivilegesAtFunctionScope.java @@ -0,0 +1,262 @@ +/* +printf_test_3 * 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.sentry.tests.e2e.hive; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.fail; + +import java.io.File; +import java.io.FileOutputStream; +import java.security.CodeSource; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import org.apache.sentry.provider.file.PolicyFile; +import org.junit.Before; +import org.junit.Test; + +import com.google.common.io.Resources; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TestPrivilegesAtFunctionScope extends AbstractTestWithStaticConfiguration { + private static final Logger LOGGER = LoggerFactory + .getLogger(TestPrivilegesAtFunctionScope.class); + + private final String SINGLE_TYPE_DATA_FILE_NAME = "kv1.dat"; + private File dataDir; + private File dataFile; + private PolicyFile policyFile; + + @Before + public void setup() throws Exception { + dataDir = context.getDataDir(); + dataFile = new File(dataDir, SINGLE_TYPE_DATA_FILE_NAME); + FileOutputStream to = new FileOutputStream(dataFile); + Resources.copy(Resources.getResource(SINGLE_TYPE_DATA_FILE_NAME), to); + to.close(); + policyFile = PolicyFile.setAdminOnServer1(ADMINGROUP); + policyFile.setUserGroupMapping(StaticUserGroup.getStaticMapping()); + writePolicyFile(policyFile); + } + + /** + * admin should be able to create/drop temp functions + * user with db level access should be able to create/drop temp functions + * user with table level access should be able to create/drop temp functions + * user with no privilege should NOT be able to create/drop temp functions + */ + @Test + public void testFuncPrivileges1() throws Exception { + String tableName1 = "tb_1"; + String udfClassName = "org.apache.hadoop.hive.ql.udf.generic.GenericUDFPrintf"; + CodeSource udfSrc = Class.forName(udfClassName).getProtectionDomain().getCodeSource(); + String udfLocation = System.getProperty(EXTERNAL_HIVE_LIB); + if(udfLocation == null) { + udfLocation = udfSrc.getLocation().getPath(); + } + Connection connection = context.createConnection(ADMIN1); + Statement statement = context.createStatement(connection); + statement.execute("DROP DATABASE IF EXISTS " + DB1 + " CASCADE"); + statement.execute("CREATE DATABASE " + DB1); + statement.execute("USE " + DB1); + statement.execute("create table " + DB1 + "." + tableName1 + + " (under_col int comment 'the under column', value string)"); + statement.execute("LOAD DATA LOCAL INPATH '" + dataFile.getPath() + "' INTO TABLE " + + DB1 + "." + tableName1); + statement.execute("DROP TEMPORARY FUNCTION IF EXISTS printf_test"); + statement.execute("DROP TEMPORARY FUNCTION IF EXISTS printf_test_2"); + context.close(); + + policyFile + .addRolesToGroup(USERGROUP1, "db1_all", "UDF_JAR", "data_read") + .addRolesToGroup(USERGROUP2, "db1_tab1", "UDF_JAR") + .addRolesToGroup(USERGROUP3, "db1_tab1") + .addPermissionsToRole("db1_all", "server=server1->db=" + DB1) + .addPermissionsToRole("db1_tab1", "server=server1->db=" + DB1 + "->table=" + tableName1) + .addPermissionsToRole("UDF_JAR", "server=server1->uri=file://" + udfLocation) + .addPermissionsToRole("data_read", "server=server1->URI=" + "file:///tmp"); + writePolicyFile(policyFile); + + // user1 should be able create/drop temp functions + connection = context.createConnection(USER1_1); + statement = context.createStatement(connection); + statement.execute("USE " + DB1); + + try { + statement.execute("CREATE TEMPORARY FUNCTION printf_test AS 'org.apache.hadoop.hive.ql.udf.generic.GenericUDFPrintf'"); + LOGGER.info("Testing select from temp func printf_test"); + ResultSet res = statement.executeQuery("SELECT printf_test('%d', under_col) FROM " + tableName1); + while (res.next()) { + LOGGER.info(res.getString(1)); + } + res.close(); + statement.execute("DROP TEMPORARY FUNCTION printf_test"); + } catch (Exception ex) { + LOGGER.error("test temp func printf_test failed with reason: " + ex.getStackTrace() + " " + ex.getMessage()); + fail("fail to test temp func printf_test"); + } + + statement.execute( + "CREATE FUNCTION printf_test_perm AS 'org.apache.hadoop.hive.ql.udf.generic.GenericUDFPrintf' "); + statement.execute("SELECT printf_test_perm(value) FROM " + tableName1); + statement.execute("DROP FUNCTION printf_test_perm"); + + // test perm UDF with 'using file' syntax + statement + .execute("CREATE FUNCTION printf_test_perm AS 'org.apache.hadoop.hive.ql.udf.generic.GenericUDFPrintf' " + + " using file 'file:///tmp'"); + statement.execute("DROP FUNCTION printf_test_perm"); + + context.close(); + + // user2 has select privilege on one of the tables in db1, should be able create/drop temp functions + connection = context.createConnection(USER2_1); + statement = context.createStatement(connection); + statement.execute("USE " + DB1); + statement.execute( + "CREATE TEMPORARY FUNCTION printf_test_2 AS 'org.apache.hadoop.hive.ql.udf.generic.GenericUDFPrintf'"); + statement.execute("SELECT printf_test_2(value) FROM " + tableName1); + statement.execute("DROP TEMPORARY FUNCTION printf_test_2"); + + statement.execute( + "CREATE FUNCTION " + DB1 + ".printf_test_2_perm AS 'org.apache.hadoop.hive.ql.udf.generic.GenericUDFPrintf'"); + statement.execute("SELECT printf_test_2_perm(value) FROM " + tableName1); + statement.execute("DROP FUNCTION printf_test_2_perm"); + + // USER2 doesn't have URI perm on dataFile + try { + statement + .execute("CREATE FUNCTION " + + DB1 + + ".printf_test_2_perm AS 'org.apache.hadoop.hive.ql.udf.generic.GenericUDFPrintf'" + + " using file '" + "file://" + dataFile.getPath() + "'"); + assertFalse("CREATE TEMPORARY FUNCTION should fail for user3", true); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + + context.close(); + + // user3 shouldn't be able to create/drop temp functions since it doesn't have permission for jar + connection = context.createConnection(USER3_1); + statement = context.createStatement(connection); + statement.execute("USE " + DB1); + try { + statement.execute( + "CREATE TEMPORARY FUNCTION printf_test_bad AS 'org.apache.hadoop.hive.ql.udf.generic.GenericUDFPrintf'"); + assertFalse("CREATE TEMPORARY FUNCTION should fail for user3", true); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + + try { + statement.execute( + "CREATE FUNCTION printf_test_perm_bad AS 'org.apache.hadoop.hive.ql.udf.generic.GenericUDFPrintf'"); + assertFalse("CREATE FUNCTION should fail for user3", true); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + + context.close(); + + // user4 (not part of any group ) shouldn't be able to create/drop temp functions + connection = context.createConnection(USER4_1); + statement = context.createStatement(connection); + try { + statement.execute("USE default"); + statement.execute( + "CREATE TEMPORARY FUNCTION printf_test_bad AS 'org.apache.hadoop.hive.ql.udf.generic.GenericUDFPrintf'"); + assertFalse("CREATE TEMPORARY FUNCTION should fail for user4", true); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + context.close(); + + } + + @Test + public void testUdfWhiteList () throws Exception { + String tableName1 = "tab1"; + + Connection connection = context.createConnection(ADMIN1); + Statement statement = connection.createStatement(); + statement.execute("DROP DATABASE IF EXISTS " + DB1 + " CASCADE"); + statement.execute("CREATE DATABASE " + DB1); + statement.execute("USE " + DB1); + statement.execute("create table " + tableName1 + + " (under_col int comment 'the under column', value string)"); + + policyFile + .addRolesToGroup(USERGROUP1, "db1_all", "UDF_JAR") + .addRolesToGroup(USERGROUP2, "db1_tab1", "UDF_JAR") + .addRolesToGroup(USERGROUP3, "db1_tab1") + .addPermissionsToRole("db1_all", "server=server1->db=" + DB1) + .addPermissionsToRole("db1_tab1", "server=server1->db=" + DB1 + "->table=" + tableName1) + .addPermissionsToRole("UDF_JAR", "server=server1->uri=file://${user.home}/.m2"); + writePolicyFile(policyFile); + + statement.execute("LOAD DATA LOCAL INPATH '" + dataFile.getPath() + "' INTO TABLE " + + DB1 + "." + tableName1); + statement.execute("SELECT rand(), concat(value, '_foo') FROM " + tableName1); + + context.assertAuthzException(statement, + "SELECT reflect('java.net.URLDecoder', 'decode', 'http://www.apache.org', 'utf-8'), value FROM " + tableName1); + context.assertAuthzException(statement, + "SELECT java_method('java.net.URLDecoder', 'decode', 'http://www.apache.org', 'utf-8'), value FROM " + tableName1); + statement.close(); + connection.close(); + } + + /** + * User with db level access should be able to create/alter tables with buildin Serde. + */ + @Test + public void testSerdePrivileges() throws Exception { + String tableName1 = "tab1"; + String tableName2 = "tab2"; + + Connection connection = context.createConnection(ADMIN1); + Statement statement = context.createStatement(connection); + statement.execute("DROP DATABASE IF EXISTS " + DB1 + " CASCADE"); + statement.execute("CREATE DATABASE " + DB1); + + context.close(); + + policyFile + .addRolesToGroup(USERGROUP1, "db1_all") + .addPermissionsToRole("db1_all", "server=server1->db=" + DB1); + writePolicyFile(policyFile); + + connection = context.createConnection(USER1_1); + statement = context.createStatement(connection); + statement.execute("USE " + DB1); + statement.execute("create table " + DB1 + "." + tableName1 + + " (a string, b string) ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.OpenCSVSerde' " + + " STORED AS TEXTFILE"); + + statement.execute("create table " + DB1 + "." + tableName2 + " (a string, b string)"); + statement.execute("alter table " + DB1 + "." + tableName2 + + " SET SERDE 'org.apache.hadoop.hive.serde2.OpenCSVSerde'"); + + context.close(); + } +}
