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/TestCrossDbOps.java ---------------------------------------------------------------------- diff --git a/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestCrossDbOps.java b/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestCrossDbOps.java new file mode 100644 index 0000000..b123dcd --- /dev/null +++ b/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestCrossDbOps.java @@ -0,0 +1,669 @@ +/* + * 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.assertEquals; +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.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; + +import org.junit.Assert; + +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.google.common.io.Resources; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/* Tests privileges at table scope with cross database access */ + +public class TestCrossDbOps extends AbstractTestWithStaticConfiguration { + private static final Logger LOGGER = LoggerFactory + .getLogger(TestCrossDbOps.class); + + private File dataFile; + private PolicyFile policyFile; + private String loadData; + + @BeforeClass + public static void setupTestStaticConfiguration() throws Exception{ + LOGGER.info("TestCrossDbOps setupTestStaticConfiguration"); + policyOnHdfs = true; + AbstractTestWithStaticConfiguration.setupTestStaticConfiguration(); + + } + + @Before + public void setup() throws Exception { + LOGGER.info("TestCrossDbOps setup"); + policyFile = super.setupPolicy(); + super.setup(); + File dataDir = context.getDataDir(); + // copy data file to test dir + 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(); + loadData = "server=server1->uri=file://" + dataFile.getPath(); + } + + /* + * Admin creates DB_1, DB2, tables (tab_1 ) and (tab_2, tab_3) in DB_1 and + * DB_2 respectively. User user1 has select on DB_1.tab_1, insert on + * DB2.tab_2 User user2 has select on DB2.tab_3 Test show database and show + * tables for both user1 and user2 + */ + @Test + public void testShowDatabasesAndShowTables() throws Exception { + // admin create two databases + 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 TAB1(id int)"); + statement.executeQuery("SHOW TABLES"); + statement.execute("USE " + DB2); + statement.execute("CREATE TABLE TAB2(id int)"); + statement.execute("CREATE TABLE TAB3(id int)"); + + // load policy file and grant role with privileges + policyFile + .addRolesToGroup(USERGROUP1, "select_tab1", "insert_tab2") + .addRolesToGroup(USERGROUP2, "select_tab3") + .addPermissionsToRole("select_tab1", "server=server1->db=" + DB1 + "->table=tab1->action=select") + .addPermissionsToRole("select_tab3", "server=server1->db=" + DB2 + "->table=tab3->action=select") + .addPermissionsToRole("insert_tab2", "server=server1->db=" + DB2 + "->table=tab2->action=insert") + .setUserGroupMapping(StaticUserGroup.getStaticMapping()); + writePolicyFile(policyFile); + + // show grant to validate roles and privileges + if(useSentryService) { + PrivilegeResultSet pRset = new PrivilegeResultSet(statement, "SHOW GRANT ROLE select_tab1 ON DATABASE " + DB1); + LOGGER.info("SHOW GRANT ROLE select_tab1 ON DATABASE " + DB1 + " : " + pRset.toString()); + pRset.verifyResultSetColumn("database", DB1); + pRset.verifyResultSetColumn("table", "tab1"); + + pRset = new PrivilegeResultSet(statement, "SHOW GRANT ROLE insert_tab2 ON DATABASE " + DB2); + LOGGER.info("SHOW GRANT ROLE insert_tab2 ON DATABASE " + DB2 + " : " + pRset.toString()); + pRset.verifyResultSetColumn("database", DB2); + pRset.verifyResultSetColumn("table", "tab2"); + + pRset = new PrivilegeResultSet(statement, "SHOW GRANT ROLE select_tab3 ON DATABASE " + DB2); + LOGGER.info("SHOW GRANT ROLE select_tab3 ON DATABASE " + DB2 + " : " + pRset.toString()); + pRset.verifyResultSetColumn("database", DB2); + pRset.verifyResultSetColumn("table", "tab3"); + } + + // test show databases + // show databases shouldn't filter any of the dbs from the resultset + Connection conn = context.createConnection(USER1_1); + Statement stmt = context.createStatement(conn); + PrivilegeResultSet pRset = new PrivilegeResultSet(stmt, "SHOW DATABASES"); + LOGGER.info("found databases :" + pRset.toString()); + pRset.verifyResultSetColumn("database_name", DB1); + pRset.verifyResultSetColumn("database_name", DB2); + + // test show tables + stmt.execute("USE " + DB1); + pRset = new PrivilegeResultSet(stmt, "SHOW TABLES"); + LOGGER.info("found tables :" + pRset.toString()); + pRset.verifyResultSetColumn("tab_name", "tab1"); + + stmt.execute("USE " + DB2); + pRset = new PrivilegeResultSet(stmt, "SHOW TABLES"); + LOGGER.info("found tables :" + pRset.toString()); + pRset.verifyResultSetColumn("tab_name", "tab2"); + + try { + stmt.close(); + conn.close(); + } catch (Exception ex) { + // nothing to do + } + + // test show databases and show tables for user2_1 + conn = context.createConnection(USER2_1); + stmt = context.createStatement(conn); + + pRset = new PrivilegeResultSet(stmt, "SHOW DATABASES"); + pRset.verifyResultSetColumn("database_name", DB2); + + // test show tables + stmt.execute("USE " + DB2); + pRset = new PrivilegeResultSet(stmt, "SHOW TABLES"); + pRset.verifyResultSetColumn("tab_name", "tab3"); + + try { + stmt.execute("USE " + DB1); + Assert.fail("Expected SQL exception"); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + + context.close(); + } + + /* + * Admin creates DB_1, DB2, tables (tab_1 ) and (tab_2, tab_3) in DB_1 and + * DB_2 respectively. User user1 has select on DB_1.tab_1, insert on + * DB2.tab_2 User user2 has select on DB2.tab_3 Test show database and show + * tables for both user1 and user2 + */ + @Test + public void testJDBCGetSchemasAndGetTables() throws Exception { + // admin create two databases + 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 TAB1(id int)"); + statement.executeQuery("SHOW TABLES"); + statement.execute("USE " + DB2); + statement.execute("CREATE TABLE TAB2(id int)"); + statement.execute("CREATE TABLE TAB3(id int)"); + + // edit policy file + policyFile.addRolesToGroup(USERGROUP1, "select_tab1", "insert_tab2") + .addRolesToGroup(USERGROUP2, "select_tab3") + .addPermissionsToRole("select_tab1", "server=server1->db=" + DB1 + "->table=tab1->action=select") + .addPermissionsToRole("select_tab3", "server=server1->db=" + DB2 + "->table=tab3->action=select") + .addPermissionsToRole("insert_tab2", "server=server1->db=" + DB2 + "->table=tab2->action=insert") + .setUserGroupMapping(StaticUserGroup.getStaticMapping()); + writePolicyFile(policyFile); + + // test show databases + // show databases shouldn't filter any of the dbs from the resultset + Connection conn = context.createConnection(USER1_1); + Statement stmt = context.createStatement(conn); + // test direct JDBC metadata API + ResultSet res = stmt.executeQuery("SHOW DATABASES"); + res = conn.getMetaData().getSchemas(); + ResultSetMetaData resMeta = res.getMetaData(); + assertEquals(2, resMeta.getColumnCount()); + assertEquals("TABLE_SCHEM", resMeta.getColumnName(1)); + assertEquals("TABLE_CATALOG", resMeta.getColumnName(2)); + + List<String> expectedResult = new ArrayList<String>(); + List<String> returnedResult = new ArrayList<String>(); + + expectedResult.add(DB1); + expectedResult.add(DB2); + expectedResult.add("default"); + while (res.next()) { + returnedResult.add(res.getString(1).trim()); + } + validateReturnedResult(expectedResult, returnedResult); + returnedResult.clear(); + expectedResult.clear(); + res.close(); + + // test direct JDBC metadata API + res = conn.getMetaData().getTables(null, DB1, "tab%", null); + expectedResult.add("tab1"); + while (res.next()) { + returnedResult.add(res.getString(3).trim()); + } + validateReturnedResult(expectedResult, returnedResult); + returnedResult.clear(); + expectedResult.clear(); + res.close(); + + // test direct JDBC metadata API + res = conn.getMetaData().getTables(null, DB2, "tab%", null); + expectedResult.add("tab2"); + while (res.next()) { + returnedResult.add(res.getString(3).trim()); + } + validateReturnedResult(expectedResult, returnedResult); + returnedResult.clear(); + expectedResult.clear(); + res.close(); + + res = conn.getMetaData().getTables(null, "DB%", "tab%", null); + expectedResult.add("tab2"); + expectedResult.add("tab1"); + while (res.next()) { + returnedResult.add(res.getString(3).trim()); + } + validateReturnedResult(expectedResult, returnedResult); + returnedResult.clear(); + expectedResult.clear(); + res.close(); + + //test show columns + res = conn.getMetaData().getColumns(null, "DB%", "tab%","i%" ); + expectedResult.add("id"); + + while (res.next()) { + returnedResult.add(res.getString(4).trim()); + } + validateReturnedResult(expectedResult, returnedResult); + returnedResult.clear(); + expectedResult.clear(); + res.close(); + + conn.close(); + + // test show databases and show tables for user2 + conn = context.createConnection(USER2_1); + + // test direct JDBC metadata API + res = conn.getMetaData().getSchemas(); + resMeta = res.getMetaData(); + assertEquals(2, resMeta.getColumnCount()); + assertEquals("TABLE_SCHEM", resMeta.getColumnName(1)); + assertEquals("TABLE_CATALOG", resMeta.getColumnName(2)); + + expectedResult.add(DB2); + expectedResult.add("default"); + + while (res.next()) { + returnedResult.add(res.getString(1).trim()); + } + validateReturnedResult(expectedResult, returnedResult); + returnedResult.clear(); + expectedResult.clear(); + res.close(); + + // test JDBC direct API + res = conn.getMetaData().getTables(null, "DB%", "tab%", null); + expectedResult.add("tab3"); + + while (res.next()) { + returnedResult.add(res.getString(3).trim()); + } + validateReturnedResult(expectedResult, returnedResult); + returnedResult.clear(); + expectedResult.clear(); + res.close(); + + + //test show columns + res = conn.getMetaData().getColumns(null, "DB%", "tab%","i%" ); + expectedResult.add("id"); + + while (res.next()) { + returnedResult.add(res.getString(4).trim()); + } + validateReturnedResult(expectedResult, returnedResult); + returnedResult.clear(); + expectedResult.clear(); + res.close(); + + //test show columns + res = conn.getMetaData().getColumns(null, DB1, "tab%","i%" ); + + while (res.next()) { + returnedResult.add(res.getString(4).trim()); + } + validateReturnedResult(expectedResult, returnedResult); + returnedResult.clear(); + expectedResult.clear(); + res.close(); + + context.close(); + } + + /** + * 2.8 admin user create two database, DB_1, DB_2 admin grant all to USER1_1, + * USER1_2 on DB_1, admin grant all to user1's group, user2's group on DB_2 + * positive test case: user1, user2 has ALL privilege on both DB_1 and DB_2 + * negative test case: user1, user2 don't have ALL privilege on SERVER + */ + @Test + public void testDbPrivileges() throws Exception { + createDb(ADMIN1, DB1, DB2); + + // edit policy file + policyFile.addRolesToGroup(USERGROUP1, "db1_all,db2_all, load_data") + .addPermissionsToRole("db1_all", "server=server1->db=" + DB1) + .addPermissionsToRole("db2_all", "server=server1->db=" + DB2) + .addPermissionsToRole("load_data", "server=server1->URI=file://" + dataFile.getPath()) + .setUserGroupMapping(StaticUserGroup.getStaticMapping()); + writePolicyFile(policyFile); + + for (String user : new String[]{USER1_1, USER1_2}) { + for (String dbName : new String[]{DB1, DB2}) { + Connection userConn = context.createConnection(user); + String tabName = user + "_tab1"; + Statement userStmt = context.createStatement(userConn); + // Positive case: test user1 and user2 has permissions to access + // db1 and + // db2 + userStmt.execute("Use " + dbName); + userStmt + .execute("create table " + dbName + "." + tabName + " (id int)"); + userStmt.execute("LOAD DATA LOCAL INPATH '" + dataFile.getPath() + + "' INTO TABLE " + tabName); + userStmt.execute("select * from " + dbName + "." + tabName); + context.close(); + } + } + } + + /** + * Test Case 2.11 admin user create a new database DB_1 and grant ALL to + * himself on DB_1 should work + */ + @Test + public void testAdminDbPrivileges() throws Exception { + createDb(ADMIN1, DB1); + + policyFile + .setUserGroupMapping(StaticUserGroup.getStaticMapping()); + writePolicyFile(policyFile); + + Connection adminCon = context.createConnection(ADMIN1); + Statement adminStmt = context.createStatement(adminCon); + String tabName = DB1 + "." + "admin_tab1"; + adminStmt.execute("create table " + tabName + "(c1 string)"); + adminStmt.execute("load data local inpath '" + dataFile.getPath() + "' into table " + + tabName); + assertTrue(adminStmt.executeQuery("select * from " + tabName).next()); + adminStmt.close(); + adminCon.close(); + } + + /** + * Test Case 2.14 admin user create a new database DB_1 create TABLE_1 in DB_1 + * admin user grant INSERT to user1's group on TABLE_1 negative test case: + * user1 try to do following on TABLE_1 will fail: --explain --analyze + * --describe --describe function --show columns --show table status --show + * table properties --show create table --show partitions --show indexes + * --select * from TABLE_1. + */ + @Test + public void testNegativeUserPrivileges() throws Exception { + Connection adminCon = context.createConnection(ADMIN1); + Statement adminStmt = context.createStatement(adminCon); + adminStmt.execute("use default"); + adminStmt.execute("CREATE DATABASE " + DB1); + adminStmt.execute("create table " + DB1 + ".table_1 (id int)"); + adminStmt.execute("create table " + DB1 + ".table_2 (id int)"); + adminStmt.close(); + adminCon.close(); + + // edit policy file + policyFile.addRolesToGroup(USERGROUP1, "db1_tab1_insert", "db1_tab2_all") + .addPermissionsToRole("db1_tab2_all", "server=server1->db=" + DB1 + "->table=table_2") + .addPermissionsToRole("db1_tab1_insert", "server=server1->db=" + DB1 + "->table=table_1->action=insert") + .setUserGroupMapping(StaticUserGroup.getStaticMapping()); + writePolicyFile(policyFile); + + Connection userConn = context.createConnection(USER1_1); + Statement userStmt = context.createStatement(userConn); + context.assertAuthzException(userStmt, "select * from " + DB1 + ".table_1"); + userConn.close(); + userStmt.close(); + } + + /** + * Test Case 2.16 admin user create a new database DB_1 create TABLE_1 and + * TABLE_2 (same schema) in DB_1 admin user grant SELECT, INSERT to user1's + * group on TABLE_2 negative test case: user1 try to do following on TABLE_1 + * will fail: --insert overwrite TABLE_2 select * from TABLE_1 + */ + @Test + public void testNegativeUserDMLPrivileges() throws Exception { + createDb(ADMIN1, DB1); + Connection adminCon = context.createConnection(ADMIN1); + Statement adminStmt = context.createStatement(adminCon); + adminStmt.execute("create table " + DB1 + ".table_1 (id int)"); + adminStmt.execute("create table " + DB1 + ".table_2 (id int)"); + adminStmt.close(); + adminCon.close(); + + policyFile + .addPermissionsToRole("db1_tab2_all", "server=server1->db=" + DB1 + "->table=table_2") + .addRolesToGroup(USERGROUP1, "db1_tab2_all") + .setUserGroupMapping(StaticUserGroup.getStaticMapping()); + writePolicyFile(policyFile); + + Connection userConn = context.createConnection(USER1_1); + Statement userStmt = context.createStatement(userConn); + context.assertAuthzException(userStmt, "insert overwrite table " + DB1 + + ".table_2 select * from " + DB1 + ".table_1"); + context.assertAuthzException(userStmt, "insert overwrite directory '" + dataDir.getPath() + + "' select * from " + DB1 + ".table_1"); + userStmt.close(); + userConn.close(); + } + + /** + * Test Case 2.17 Execution steps + * a) Admin user creates a new database DB_1, + * b) Admin user grants ALL on DB_1 to group GROUP_1 + * c) User from GROUP_1 creates table TAB_1, TAB_2 in DB_1 + * d) Admin user grants SELECT on TAB_1 to group GROUP_2 + * + * 1) verify users from GROUP_2 have only SELECT privileges on TAB_1. They + * shouldn't be able to perform any operation other than those listed as + * requiring SELECT in the privilege model. + * + * 2) verify users from GROUP_2 can't perform queries involving join between + * TAB_1 and TAB_2. + * + * 3) verify users from GROUP_1 can't perform operations requiring ALL @ + * SERVER scope. Refer to list + */ + @Test + public void testNegUserPrivilegesAll() throws Exception { + // create dbs + Connection adminCon = context.createConnection(ADMIN1); + Statement adminStmt = context.createStatement(adminCon); + adminStmt.execute("use default"); + adminStmt.execute("drop table if exists table_def"); + adminStmt.execute("create table table_def (name string)"); + adminStmt + .execute("load data local inpath '" + dataFile.getPath() + "' into table table_def"); + + adminStmt.execute("CREATE DATABASE " + DB1); + adminStmt.execute("use " + DB1); + + adminStmt.execute("create table table_1 (name string)"); + adminStmt + .execute("load data local inpath '" + dataFile.getPath() + "' into table table_1"); + adminStmt.execute("create table table_2 (name string)"); + adminStmt + .execute("load data local inpath '" + dataFile.getPath() + "' into table table_2"); + adminStmt.execute("create view v1 AS select * from table_1"); + adminStmt + .execute("create table table_part_1 (name string) PARTITIONED BY (year INT)"); + adminStmt.execute("ALTER TABLE table_part_1 ADD PARTITION (year = 2012)"); + + adminStmt.close(); + adminCon.close(); + + policyFile + .addRolesToGroup(USERGROUP1, "db1_all") + .addRolesToGroup(USERGROUP2, "db1_tab1_select") + .addPermissionsToRole("db1_all", "server=server1->db=" + DB1) + .addPermissionsToRole("db1_tab1_select", "server=server1->db=" + DB1 + "->table=table_1->action=select") + .setUserGroupMapping(StaticUserGroup.getStaticMapping()); + writePolicyFile(policyFile); + + Connection userConn = context.createConnection(USER2_1); + Statement userStmt = context.createStatement(userConn); + + context.assertAuthzException(userStmt, "drop database " + DB1); + + // Hive currently doesn't support cross db index DDL + + context.assertAuthzException(userStmt, "CREATE TEMPORARY FUNCTION strip AS 'org.apache.hadoop.hive.ql.udf.generic.GenericUDFPrintf'"); + context.assertAuthzException(userStmt, "create table " + DB1 + + ".c_tab_2 as select * from " + DB1 + ".table_2"); + context.assertAuthzException(userStmt, "select * from " + DB1 + ".table_2"); + context.assertAuthzException(userStmt, "ALTER DATABASE " + DB1 + + " SET DBPROPERTIES ('foo' = 'bar')"); + context.assertAuthzException(userStmt, "drop table " + DB1 + ".table_1"); + context.assertAuthzException(userStmt, "DROP VIEW IF EXISTS " + DB1 + ".v1"); + context.assertAuthzException(userStmt, "create table " + DB1 + ".table_5 (name string)"); + context.assertAuthzException(userStmt, "ALTER TABLE " + DB1 + ".table_1 RENAME TO " + + DB1 + ".table_99"); + context.assertAuthzException(userStmt, "insert overwrite table " + DB1 + + ".table_2 select * from " + DB1 + ".table_1"); + context.assertAuthzException(userStmt, "insert overwrite table " + DB1 + + ".table_2 select * from " + "table_def"); + context.assertAuthzException(userStmt, "ALTER TABLE " + DB1 + + ".table_part_1 ADD IF NOT EXISTS PARTITION (year = 2012)"); + context.assertAuthzException(userStmt, "ALTER TABLE " + DB1 + + ".table_part_1 PARTITION (year = 2012) SET LOCATION '/etc'"); + userStmt.close(); + userConn.close(); + } + + /** + * Steps: 1. admin user create databases, DB_1 and DB_2, no table or other + * object in database + * 2. admin grant all to user1's group on DB_1 and DB_2 + * positive test case: + * a)user1 has the privilege to create table, load data, + * drop table, create view, insert more data on both databases + * b) user1 can switch between DB_1 and DB_2 without exception + * negative test case: + * c) user1 cannot drop database + */ + @Test + public void testSandboxOpt9() throws Exception { + createDb(ADMIN1, DB1, DB2); + + policyFile + .addPermissionsToRole(GROUP1_ROLE, ALL_DB1, ALL_DB2, loadData) + .addRolesToGroup(USERGROUP1, GROUP1_ROLE) + .setUserGroupMapping(StaticUserGroup.getStaticMapping()); + writePolicyFile(policyFile); + + Connection connection = context.createConnection(USER1_1); + Statement statement = context.createStatement(connection); + + // a + statement.execute("DROP TABLE IF EXISTS " + DB1 + "." + TBL1); + statement.execute("create table " + DB1 + "." + TBL1 + + " (under_col int comment 'the under column', value string)"); + statement.execute("load data local inpath '" + dataFile.getPath() + + "' into table " + DB1 + "." + TBL1); + statement.execute("DROP VIEW IF EXISTS " + DB1 + "." + VIEW1); + statement.execute("CREATE VIEW " + DB1 + "." + VIEW1 + + " (value) AS SELECT value from " + DB1 + "." + TBL1 + + " LIMIT 10"); + statement.execute("DROP TABLE IF EXISTS " + DB2 + "." + TBL1); + statement.execute("CREATE TABLE " + DB2 + "." + TBL1 + + " AS SELECT value from " + DB1 + "." + TBL1 + + " LIMIT 10"); + + // b + statement.execute("DROP TABLE IF EXISTS " + DB2 + "." + TBL2); + statement.execute("create table " + DB2 + "." + TBL2 + + " (under_col int comment 'the under column', value string)"); + statement.execute("load data local inpath '" + dataFile.getPath() + + "' into table " + DB2 + "." + TBL2); + statement.execute("DROP TABLE IF EXISTS " + DB2 + "." + TBL3); + statement.execute("create table " + DB2 + "." + TBL3 + + " (under_col int comment 'the under column', value string)"); + statement.execute("load data local inpath '" + dataFile.getPath() + + "' into table " + DB2 + "." + TBL3); + + policyFile.removePermissionsFromRole(GROUP1_ROLE, ALL_DB2); + writePolicyFile(policyFile); + // create db1.view1 as select from db2.tbl2 + statement.execute("DROP VIEW IF EXISTS " + DB1 + "." + VIEW2); + context.assertAuthzException(statement, "CREATE VIEW " + DB1 + "." + VIEW2 + + " (value) AS SELECT value from " + DB2 + "." + TBL2 + " LIMIT 10"); + // create db1.tbl2 as select from db2.tbl2 + statement.execute("DROP TABLE IF EXISTS " + DB1 + "." + TBL2); + context.assertAuthzException(statement, "CREATE TABLE " + DB1 + "." + TBL2 + + " AS SELECT value from " + DB2 + "." + TBL2 + " LIMIT 10"); + + statement.close(); + connection.close(); + } + + /** + * Steps: 1. admin user create databases, DB_1 and DB_2, no table or other + * object in database positive test case: + * d) user1 has the privilege to create view on tables in DB_1 negative test case: + * e) user1 cannot create view in DB_1 that select from tables in DB_2 + * with no select privilege 2. + * positive test case: + * f) user1 has the privilege to create view to select from DB_1.tb_1 + * and DB_2.tb_2 negative test case: + * g) user1 cannot create view to select from DB_1.tb_1 and DB_2.tb_3 + */ + @Test + public void testCrossDbViewOperations() throws Exception { + // admin create two databases + createDb(ADMIN1, DB1, DB2); + Connection connection = context.createConnection(ADMIN1); + Statement statement = context.createStatement(connection); + statement + .execute("CREATE TABLE " + DB1 + "." + TBL1 + "(id int)"); + statement + .execute("CREATE TABLE " + DB2 + "." + TBL1 + "(id int)"); + statement + .execute("CREATE TABLE " + DB2 + "." + TBL2 + "(id int)"); + context.close(); + + // edit policy file + policyFile + .addRolesToGroup(USERGROUP1, "all_db1", "load_data", "select_tb2") + .addPermissionsToRole("all_db1", "server=server1->db=" + DB1) + .addPermissionsToRole("all_db2", "server=server1->db=" + DB2) + .addPermissionsToRole("select_tb2", "server=server1->db=" + DB2 + "->table=tb_1->action=select") + .addPermissionsToRole("load_data", "server=server1->URI=file://" + dataFile.getPath()) + .setUserGroupMapping(StaticUserGroup.getStaticMapping()); + writePolicyFile(policyFile); + + connection = context.createConnection(USER1_1); + statement = context.createStatement(connection); + + // d + statement.execute("DROP TABLE IF EXISTS " + DB1 + "." + TBL1); + statement.execute("create table " + DB1 + "." + TBL1 + + " (under_col int comment 'the under column', value string)"); + + // e + statement.execute("DROP VIEW IF EXISTS " + DB1 + "." + VIEW1); + context.assertAuthzException(statement, "CREATE VIEW " + DB1 + "." + VIEW1 + + " (value) AS SELECT value from " + DB2 + "." + TBL2 + + " LIMIT 10"); + // f + statement.execute("DROP VIEW IF EXISTS " + DB1 + "." + VIEW2); + statement.execute("CREATE VIEW " + DB1 + "." + VIEW2 + + " (value) AS SELECT value from " + DB1 + "." + TBL1 + + " LIMIT 10"); + + // g + statement.execute("DROP VIEW IF EXISTS " + DB1 + "." + VIEW3); + context.assertAuthzException(statement, "CREATE VIEW " + DB1 + "." + VIEW3 + + " (value) AS SELECT value from " + DB2 + "." + TBL2 + + " LIMIT 10"); + } +}
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/TestCustomSerdePrivileges.java ---------------------------------------------------------------------- diff --git a/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestCustomSerdePrivileges.java b/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestCustomSerdePrivileges.java new file mode 100644 index 0000000..2851ed6 --- /dev/null +++ b/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestCustomSerdePrivileges.java @@ -0,0 +1,120 @@ +/* + * 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 com.google.common.collect.Maps; + +import org.apache.hadoop.hive.conf.HiveConf; +import org.apache.sentry.binding.hive.conf.HiveAuthzConf; +import org.apache.sentry.provider.file.PolicyFile; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.security.CodeSource; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Map; + +public class TestCustomSerdePrivileges extends AbstractTestWithHiveServer { + private static Context context; + private static Map<String, String> properties; + private PolicyFile policyFile; + + @BeforeClass + public static void setUp() throws Exception { + properties = Maps.newHashMap(); + + // Start the Hive Server without buildin Serde, such as + // "org.apache.hadoop.hive.serde2.OpenCSVSerde". Instead, + // used a bogus class name for testing. + properties.put(HiveAuthzConf.HIVE_SENTRY_SERDE_WHITELIST, "org.example.com"); + properties.put(HiveAuthzConf.HIVE_SENTRY_SERDE_URI_PRIVILIEGES_ENABLED, "true"); + properties.put(HiveConf.ConfVars.METASTORE_AUTO_CREATE_ALL.varname, "true"); + context = createContext(properties); + } + + @AfterClass + public static void tearDown() throws Exception { + if(context != null) { + context.close(); + } + } + + @Before + public void setupPolicyFile() throws Exception { + policyFile = PolicyFile.setAdminOnServer1(ADMINGROUP); + } + + /** + * User with db level access and Uri privileges on the Serde Jar should be able + * to create tables with Serde. + * User with db level access but without Uri privileges on the Serde Jar will fail + * on creating tables with Serde. + */ + @Test + public void testSerdePrivilegesWithoutBuildinJar() throws Exception { + String db = "db1"; + String tableName1 = "tab1"; + + String serdeClassName = "org.apache.hadoop.hive.serde2.OpenCSVSerde"; + CodeSource serdeSrc = Class.forName(serdeClassName).getProtectionDomain().getCodeSource(); + String serdeLocation = serdeSrc.getLocation().getPath(); + + policyFile + .addRolesToGroup(USERGROUP1, "db1_all") + .addRolesToGroup(USERGROUP2, "db1_all", "SERDE_JAR") + .addPermissionsToRole("db1_all", "server=server1->db=" + db) + .addPermissionsToRole("db1_tab1", "server=server1->db=" + db + "->table=" + tableName1) + .addPermissionsToRole("SERDE_JAR", "server=server1->uri=file://" + serdeLocation) + .setUserGroupMapping(StaticUserGroup.getStaticMapping()); + policyFile.write(context.getPolicyFile()); + + Connection connection = context.createConnection(ADMIN1); + Statement statement = context.createStatement(connection); + statement.execute("DROP DATABASE IF EXISTS " + db + " CASCADE"); + statement.execute("CREATE DATABASE " + db); + context.close(); + + // User1 does not have the URI privileges to use the Serde Jar. + // The table creation will fail. + connection = context.createConnection(USER1_1); + statement = context.createStatement(connection); + statement.execute("USE " + db); + try { + statement.execute("create table " + db + "." + tableName1 + " (a string, b string) " + + "ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.OpenCSVSerde' " + " STORED AS TEXTFILE"); + Assert.fail("Expect create table with Serde to fail"); + } catch (SQLException e) { + context.verifyAuthzException(e); + } + context.close(); + + // User2 has the URI privileges to use the Serde Jar. + // The table creation will succeed. + connection = context.createConnection(USER2_1); + statement = context.createStatement(connection); + statement.execute("USE " + db); + statement.execute("create table " + db + "." + tableName1 + " (a string, b string) ROW FORMAT" + + " SERDE 'org.apache.hadoop.hive.serde2.OpenCSVSerde' " + " STORED AS TEXTFILE"); + 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/TestEndToEnd.java ---------------------------------------------------------------------- diff --git a/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestEndToEnd.java b/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestEndToEnd.java new file mode 100644 index 0000000..23577c2 --- /dev/null +++ b/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestEndToEnd.java @@ -0,0 +1,128 @@ +/* + * 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.Statement; + +import org.apache.sentry.provider.file.PolicyFile; +import org.junit.Before; +import org.junit.Test; + +import com.google.common.io.Resources; + +public class TestEndToEnd extends AbstractTestWithStaticConfiguration { + private final String SINGLE_TYPE_DATA_FILE_NAME = "kv1.dat"; + private File dataFile; + private PolicyFile policyFile; + + + @Before + public void setup() throws Exception { + 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 a new experimental database + * 2. admin create a new production database, create table, load data + * 3. admin grant privilege all@'experimental database' to usergroup1 + * 4. user create table, load data in experimental DB + * 5. user create view based on table in experimental DB + * 6. admin create table (same name) in production DB + * 7. admin grant [email protected] to group + * admin grant [email protected] to group + * 8. user load data from experimental table to production table + */ + @Test + public void testEndToEnd1() throws Exception { + policyFile + .setUserGroupMapping(StaticUserGroup.getStaticMapping()); + writePolicyFile(policyFile); + String tableName1 = "tb_1"; + String tableName2 = "tb_2"; + String viewName1 = "view_1"; + Connection connection = context.createConnection(ADMIN1); + Statement statement = context.createStatement(connection); + // 1 + statement.execute("DROP DATABASE IF EXISTS " + DB1 + " CASCADE"); + statement.execute("CREATE DATABASE " + DB1); + // 2 + statement.execute("DROP DATABASE IF EXISTS " + DB2 + " CASCADE"); + statement.execute("CREATE DATABASE " + DB2); + statement.execute("USE " + DB2); + statement.execute("DROP TABLE IF EXISTS " + DB2 + "." + tableName2); + statement.execute("create table " + DB2 + "." + tableName2 + + " (under_col int comment 'the under column', value string)"); + statement.execute("load data local inpath '" + dataFile.getPath() + + "' into table " + tableName2); + statement.close(); + connection.close(); + + // 3 + policyFile + .addRolesToGroup(USERGROUP1, "all_db1", "data_uri", "select_tb1", "insert_tb1") + .addPermissionsToRole("all_db1", "server=server1->db=" + DB1) + .addPermissionsToRole("select_tb1", "server=server1->db=" + DB2 + "->table=tb_1->action=select") + .addPermissionsToRole("insert_tb2", "server=server1->db=" + DB2 + "->table=tb_2->action=insert") + .addPermissionsToRole("insert_tb1", "server=server1->db=" + DB2 + "->table=tb_2->action=insert") + .addPermissionsToRole("data_uri", "server=server1->uri=file://" + dataDir.getPath()); + writePolicyFile(policyFile); + + // 4 + connection = context.createConnection(USER1_1); + statement = context.createStatement(connection); + statement.execute("USE " + DB1); + statement.execute("DROP TABLE IF EXISTS " + DB1 + "." + tableName1); + 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 " + tableName1); + // 5 + statement.execute("CREATE VIEW " + viewName1 + " (value) AS SELECT value from " + tableName1 + " LIMIT 10"); + statement.close(); + connection.close(); + + // 7 + connection = context.createConnection(ADMIN1); + statement = context.createStatement(connection); + statement.execute("USE " + DB2); + statement.execute("DROP TABLE IF EXISTS " + DB1 + "." + tableName1); + statement.execute("create table " + DB1 + "." + tableName1 + + " (under_col int comment 'the under column', value string)"); + statement.close(); + connection.close(); + + // 8 + connection = context.createConnection(USER1_1); + statement = context.createStatement(connection); + statement.execute("USE " + DB2); + statement.execute("INSERT OVERWRITE TABLE " + + DB2 + "." + tableName2 + " SELECT * FROM " + DB1 + + "." + tableName1); + 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/TestExportImportPrivileges.java ---------------------------------------------------------------------- diff --git a/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestExportImportPrivileges.java b/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestExportImportPrivileges.java new file mode 100644 index 0000000..5242bb1 --- /dev/null +++ b/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestExportImportPrivileges.java @@ -0,0 +1,162 @@ +/* + * 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.assertTrue; + +import java.io.File; +import java.io.FileOutputStream; +import java.sql.Connection; +import java.sql.Statement; + +import org.apache.hadoop.hive.conf.HiveConf; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.google.common.io.Resources; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TestExportImportPrivileges extends AbstractTestWithStaticConfiguration { + private static final Logger LOGGER = LoggerFactory. + getLogger(TestExportImportPrivileges.class); + private File dataFile; + private PolicyFile policyFile; + + @BeforeClass + public static void setupTestStaticConfiguration () throws Exception { + LOGGER.info("TestExportImportPrivileges setupTestStaticConfiguration"); + AbstractTestWithStaticConfiguration.setupTestStaticConfiguration(); + } + + @Before + public void setup() throws Exception { + LOGGER.info("TestExportImportPrivileges setup"); + policyFile = super.setupPolicy(); + super.setup(); + 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(); + } + + @Test + public void testInsertToDirPrivileges() throws Exception { + Connection connection = null; + Statement statement = null; + String dumpDir = dfs.getBaseDir() + "/hive_data_dump"; + + createDb(ADMIN1, DB1); + createTable(ADMIN1, DB1, dataFile, TBL1); + + policyFile + .addRolesToGroup(USERGROUP1, "db1_read", "db1_write", "data_dump") + .addRolesToGroup(USERGROUP2, "db1_read", "db1_write") + .addPermissionsToRole("db1_write", "server=server1->db=" + DB1 + "->table=" + TBL1 + "->action=INSERT") + .addPermissionsToRole("db1_read", "server=server1->db=" + DB1 + "->table=" + TBL1 + "->action=SELECT") + .addPermissionsToRole("data_dump", "server=server1->URI=" + dumpDir); + writePolicyFile(policyFile); + + // Negative test, user2 doesn't have access to write to dir + connection = context.createConnection(USER2_1); + statement = context.createStatement(connection); + statement.execute("use " + DB1); + context.assertAuthzException(statement, "INSERT OVERWRITE DIRECTORY '" + dumpDir + "' SELECT * FROM " + TBL1); + statement.close(); + connection.close(); + + // Negative test, user2 doesn't have access to dir that's similar to scratch dir + String scratchLikeDir = context.getProperty(HiveConf.ConfVars.SCRATCHDIR.varname) + "_foo"; + dfs.assertCreateDir(scratchLikeDir); + connection = context.createConnection(USER2_1); + statement = context.createStatement(connection); + statement.execute("use " + DB1); + context.assertAuthzException(statement, "INSERT OVERWRITE DIRECTORY '" + scratchLikeDir + "/bar' SELECT * FROM " + TBL1); + statement.close(); + connection.close(); + + // positive test, user1 has access to write to dir + connection = context.createConnection(USER1_1); + statement = context.createStatement(connection); + statement.execute("use " + DB1); + assertTrue(statement.executeQuery("SELECT * FROM " + TBL1).next()); + statement.execute("INSERT OVERWRITE DIRECTORY '" + dumpDir + "' SELECT * FROM " + TBL1); + } + + @Test + public void testExportImportPrivileges() throws Exception { + Connection connection = null; + Statement statement = null; + String exportDir = dfs.getBaseDir() + "/hive_export1"; + createDb(ADMIN1, DB1); + createTable(ADMIN1, DB1, dataFile, TBL1); + + policyFile + .addRolesToGroup(USERGROUP1, "tab1_read", "tab1_write", "db1_all", "data_read", "data_export") + .addRolesToGroup(USERGROUP2, "tab1_write", "tab1_read") + .addRolesToGroup(USERGROUP3, "col1_read") + .addPermissionsToRole("tab1_write", "server=server1->db=" + DB1 + "->table=" + TBL1 + "->action=INSERT") + .addPermissionsToRole("tab1_read", "server=server1->db=" + DB1 + "->table=" + TBL1 + "->action=SELECT") + .addPermissionsToRole("col1_read", "server=server1->db=" + DB1 + "->table=" + TBL1 + "->column=under_col->action=SELECT") + .addPermissionsToRole("db1_all", "server=server1->db=" + DB1) + .addPermissionsToRole("data_read", "server=server1->URI=file://" + dataFile.getPath()) + .addPermissionsToRole("data_export", "server=server1->URI=" + exportDir); + writePolicyFile(policyFile); + + // Negative test, user2 doesn't have access to the file being loaded + connection = context.createConnection(USER2_1); + statement = context.createStatement(connection); + statement.execute("use " + DB1); + context.assertAuthzException(statement, "EXPORT TABLE " + TBL1 + " TO '" + exportDir + "'"); + statement.close(); + connection.close(); + + // Positive test, user1 have access to the target directory + connection = context.createConnection(USER1_1); + statement = context.createStatement(connection); + statement.execute("use " + DB1); + statement.execute("EXPORT TABLE " + TBL1 + " TO '" + exportDir + "'"); + statement.close(); + connection.close(); + + // Negative test, user2 doesn't have access to the directory loading from + connection = context.createConnection(USER2_1); + statement = context.createStatement(connection); + statement.execute("use " + DB1); + context.assertAuthzException(statement, "IMPORT TABLE " + TBL2 + " FROM '" + exportDir + "'"); + statement.close(); + connection.close(); + + // Positive test, user1 have access to the target directory + connection = context.createConnection(USER1_1); + statement = context.createStatement(connection); + statement.execute("use " + DB1); + statement.execute("IMPORT TABLE " + TBL2 + " FROM '" + exportDir + "'"); + statement.close(); + connection.close(); + + // Positive test, user3 have access to the target directory + connection = context.createConnection(USER3_1); + statement = context.createStatement(connection); + statement.execute("use " + DB1); + statement.execute("SELECT under_col FROM " + TBL1); + 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/TestJDBCInterface.java ---------------------------------------------------------------------- diff --git a/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestJDBCInterface.java b/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestJDBCInterface.java new file mode 100644 index 0000000..bc5c08b --- /dev/null +++ b/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestJDBCInterface.java @@ -0,0 +1,228 @@ +/* + * 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.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; + +import org.apache.sentry.provider.file.PolicyFile; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TestJDBCInterface extends AbstractTestWithStaticConfiguration { + private static final Logger LOGGER = LoggerFactory. + getLogger(TestJDBCInterface.class); + private static PolicyFile policyFile; + + @BeforeClass + public static void setupTestStaticConfiguration() throws Exception { + LOGGER.info("TestJDBCInterface setupTestStaticConfiguration"); + policyOnHdfs = true; + AbstractTestWithStaticConfiguration.setupTestStaticConfiguration(); + } + + @Before + public void setup() throws Exception { + LOGGER.info("TestJDBCInterface setup"); + policyFile = super.setupPolicy(); + super.setup(); + } + + /* + * Admin creates DB_1, DB2, tables (tab_1 ) and (tab_2, tab_3) in DB_1 and + * DB_2 respectively. User user1 has select on DB_1.tab_1, insert on + * DB2.tab_2 User user2 has select on DB2.tab_3 Test show database and show + * tables for both user1 and user2 + */ + @Test + public void testJDBCGetSchemasAndGetTables() throws Exception { + // admin create two databases + Connection connection = context.createConnection(ADMIN1); + Statement statement = context.createStatement(connection); + statement.execute("DROP DATABASE IF EXISTS DB_1 CASCADE"); + statement.execute("DROP DATABASE IF EXISTS DB_2 CASCADE"); + statement.execute("DROP DATABASE IF EXISTS DB1 CASCADE"); + statement.execute("DROP DATABASE IF EXISTS DB2 CASCADE"); + + statement.execute("CREATE DATABASE " + DB1); + statement.execute("CREATE DATABASE " + DB2); + statement.execute("USE " + DB1); + statement.execute("CREATE TABLE TAB1(id int)"); + statement.executeQuery("SHOW TABLES"); + statement.execute("USE " + DB2); + statement.execute("CREATE TABLE TAB2(id int)"); + statement.execute("CREATE TABLE TAB3(id int)"); + + // edit policy file + policyFile + .addRolesToGroup(USERGROUP1, "select_tab1", "insert_tab2") + .addRolesToGroup(USERGROUP2, "select_tab3") + .addPermissionsToRole("select_tab1", + "server=server1->db=" + DB1 + "->table=tab1->action=select") + .addPermissionsToRole("select_tab3", + "server=server1->db=" + DB2 + "->table=tab3->action=select") + .addPermissionsToRole("insert_tab2", + "server=server1->db=" + DB2 + "->table=tab2->action=insert"); + writePolicyFile(policyFile); + + // test show databases + // show databases shouldn't filter any of the dbs from the resultset + Connection conn = context.createConnection(USER1_1); + List<String> expectedResult = new ArrayList<String>(); + List<String> returnedResult = new ArrayList<String>(); + + // test direct JDBC metadata API + ResultSet res = conn.getMetaData().getSchemas(); + ResultSetMetaData resMeta = res.getMetaData(); + assertEquals(2, resMeta.getColumnCount()); + assertEquals("TABLE_SCHEM", resMeta.getColumnName(1)); + assertEquals("TABLE_CATALOG", resMeta.getColumnName(2)); + + expectedResult.add(DB1); + expectedResult.add(DB2); + expectedResult.add("default"); + + while (res.next()) { + returnedResult.add(res.getString(1)); + } + validateReturnedResult(expectedResult, returnedResult); + expectedResult.clear(); + returnedResult.clear(); + res.close(); + + // test direct JDBC metadata API + res = conn.getMetaData().getTables(null, DB1, "tab%", null); + expectedResult.add("tab1"); + + while (res.next()) { + returnedResult.add(res.getString(3)); + } + validateReturnedResult(expectedResult, returnedResult); + expectedResult.clear(); + returnedResult.clear(); + res.close(); + + // test direct JDBC metadata API + res = conn.getMetaData().getTables(null, DB2, "tab%", null); + expectedResult.add("tab2"); + + while (res.next()) { + returnedResult.add(res.getString(3)); + } + validateReturnedResult(expectedResult, returnedResult); + expectedResult.clear(); + returnedResult.clear(); + res.close(); + + res = conn.getMetaData().getTables(null, "DB%", "tab%", null); + expectedResult.add("tab2"); + expectedResult.add("tab1"); + + while (res.next()) { + returnedResult.add(res.getString(3)); + } + validateReturnedResult(expectedResult, returnedResult); + expectedResult.clear(); + returnedResult.clear(); + res.close(); + + // test show columns + res = conn.getMetaData().getColumns(null, "DB%", "tab%", "i%"); + expectedResult.add("id"); + expectedResult.add("id"); + + while (res.next()) { + returnedResult.add(res.getString(4)); + } + validateReturnedResult(expectedResult, returnedResult); + expectedResult.clear(); + returnedResult.clear(); + res.close(); + + conn.close(); + + // test show databases and show tables for user2 + conn = context.createConnection(USER2_1); + + // test direct JDBC metadata API + res = conn.getMetaData().getSchemas(); + resMeta = res.getMetaData(); + assertEquals(2, resMeta.getColumnCount()); + assertEquals("TABLE_SCHEM", resMeta.getColumnName(1)); + assertEquals("TABLE_CATALOG", resMeta.getColumnName(2)); + + expectedResult.add(DB2); + expectedResult.add("default"); + + while (res.next()) { + returnedResult.add(res.getString(1)); + } + validateReturnedResult(expectedResult, returnedResult); + expectedResult.clear(); + returnedResult.clear(); + res.close(); + + // test JDBC direct API + res = conn.getMetaData().getTables(null, "DB%", "tab%", null); + expectedResult.add("tab3"); + + while (res.next()) { + returnedResult.add(res.getString(3)); + } + validateReturnedResult(expectedResult, returnedResult); + expectedResult.clear(); + returnedResult.clear(); + res.close(); + + // test show columns + res = conn.getMetaData().getColumns(null, "DB%", "tab%", "i%"); + expectedResult.add("id"); + + while (res.next()) { + returnedResult.add(res.getString(4)); + } + validateReturnedResult(expectedResult, returnedResult); + expectedResult.clear(); + returnedResult.clear(); + res.close(); + + // test show columns + res = conn.getMetaData().getColumns(null, DB1, "tab%", "i%"); + + while (res.next()) { + returnedResult.add(res.getString(4)); + } + assertTrue("returned result shouldn't contain any value, actually returned result = " + returnedResult.toString(), + returnedResult.isEmpty()); + res.close(); + + 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/TestLockPrivileges.java ---------------------------------------------------------------------- diff --git a/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestLockPrivileges.java b/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestLockPrivileges.java new file mode 100644 index 0000000..0e403d8 --- /dev/null +++ b/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestLockPrivileges.java @@ -0,0 +1,214 @@ +/** + * 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 static org.junit.Assert.fail; + +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.HashMap; +import java.util.Map; + +import org.apache.sentry.provider.file.PolicyFile; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +public class TestLockPrivileges extends AbstractTestWithStaticConfiguration { + private PolicyFile policyFile; + final String tableName = "tb1"; + + static Map<String, String> privileges = new HashMap<String, String>(); + static { + privileges.put("all_db1_tb1", "server=server1->db=" + DB1 + "->table=tb1->action=all"); + privileges.put("select_db1_tb1", "server=server1->db=" + DB1 + "->table=tb1->action=select"); + privileges.put("insert_db1_tb1", "server=server1->db=" + DB1 + "->table=tb1->action=insert"); + privileges.put("alter_db1_tb1", "server=server1->db=" + DB1 + "->table=tb1->action=alter"); + privileges.put("lock_db1_tb1", "server=server1->db=" + DB1 + "->table=tb1->action=lock"); + + privileges.put("all_db1", "server=server1->db=" + DB1 + "->action=all"); + privileges.put("select_db1", "server=server1->db=" + DB1 + "->action=select"); + privileges.put("insert_db1", "server=server1->db=" + DB1 + "->action=insert"); + privileges.put("alter_db1", "server=server1->db=" + DB1 + "->action=alter"); + privileges.put("lock_db1", "server=server1->db=" + DB1 + "->action=lock"); + } + + @BeforeClass + public static void setHiveConcurrency() throws Exception { + enableHiveConcurrency = true; + setupTestStaticConfiguration(); + } + + private void adminCreate(String db, String table) throws Exception { + Connection connection = context.createConnection(ADMIN1); + Statement statement = context.createStatement(connection); + statement.execute("DROP DATABASE IF EXISTS " + db + " CASCADE"); + statement.execute("CREATE DATABASE " + db); + if (table != null) { + statement.execute("CREATE table " + db + "." + table + " (a string)"); + } + statement.close(); + connection.close(); + } + + @Before + public void setup() throws Exception { + policyFile = PolicyFile.setAdminOnServer1(ADMINGROUP).setUserGroupMapping( + StaticUserGroup.getStaticMapping()); + writePolicyFile(policyFile); + } + + @Test + public void testLockDatabase() throws Exception { + String partialErrorMsgForNoPrivilege = "No valid privileges"; + String assertErrorException = "The exception is not the same as the expectation."; + String assertExceptionThrown = "SQLException will be thrown."; + + adminCreate(DB1, null); + policyFile.addPermissionsToRole("lock_db1", privileges.get("lock_db1")) + .addRolesToGroup(USERGROUP1, "lock_db1") + .addPermissionsToRole("insert_db1", privileges.get("insert_db1")) + .addRolesToGroup(USERGROUP2, "insert_db1") + .addPermissionsToRole("select_db1", privileges.get("select_db1")) + .addRolesToGroup(USERGROUP2, "select_db1") + .addPermissionsToRole("alter_db1", privileges.get("alter_db1")) + .addRolesToGroup(USERGROUP2, "alter_db1") + .addPermissionsToRole("all_db1", privileges.get("all_db1")) + .addRolesToGroup(USERGROUP3, "all_db1"); + writePolicyFile(policyFile); + + // user1 has lock privilege only + Connection connection = context.createConnection(USER1_1); + Statement statement = context.createStatement(connection); + statement.execute("Use " + DB1); + statement.execute("LOCK DATABASE db_1 SHARED"); + try { + statement.execute("UNLOCK DATABASE db_1"); + fail(assertExceptionThrown); + } catch (SQLException se) { + // Authorization is successful. + assertTrue(assertErrorException, se.getMessage().indexOf(partialErrorMsgForNoPrivilege) == -1); + } + + // user2 has privileges with insert, select, alter, but has no lock privilege + connection = context.createConnection(USER2_1); + statement = context.createStatement(connection); + statement.execute("Use " + DB1); + try { + statement.execute("LOCK DATABASE db_1 SHARED"); + fail(assertExceptionThrown); + } catch (SQLException se) { + // Authorization is failed, the error message include "No valid privileges" + assertTrue(assertErrorException, se.getMessage().indexOf(partialErrorMsgForNoPrivilege) > 0); + } + try { + statement.execute("UNLOCK DATABASE db_1"); + fail(assertExceptionThrown); + } catch (SQLException se) { + // Authorization is failed, the error message include "No valid privileges" + assertTrue(assertErrorException, se.getMessage().indexOf(partialErrorMsgForNoPrivilege) > 0); + } + + // user3 has All privilege + connection = context.createConnection(USER3_1); + statement = context.createStatement(connection); + statement.execute("Use " + DB1); + statement.execute("LOCK DATABASE db_1 SHARED"); + try { + statement.execute("UNLOCK DATABASE db_1"); + fail(assertExceptionThrown); + } catch (SQLException se) { + // Authorization is successful. + assertTrue(assertErrorException, se.getMessage().indexOf(partialErrorMsgForNoPrivilege) == -1); + } + statement.close(); + connection.close(); + } + + @Test + public void testLockTable() throws Exception { + String partialErrorMsgForNoPrivilege = "No valid privileges"; + String assertErrorException = "The exception is not the same as the expectation."; + String assertExceptionThrown = "SQLException will be thrown."; + + adminCreate(DB1, tableName); + policyFile.addPermissionsToRole("lock_db1_tb1", privileges.get("lock_db1_tb1")) + .addRolesToGroup(USERGROUP1, "lock_db1_tb1") + .addPermissionsToRole("insert_db1_tb1", privileges.get("insert_db1_tb1")) + .addRolesToGroup(USERGROUP2, "insert_db1_tb1") + .addPermissionsToRole("select_db1_tb1", privileges.get("select_db1_tb1")) + .addRolesToGroup(USERGROUP2, "select_db1_tb1") + .addPermissionsToRole("alter_db1_tb1", privileges.get("alter_db1_tb1")) + .addRolesToGroup(USERGROUP2, "alter_db1_tb1") + .addPermissionsToRole("all_db1_tb1", privileges.get("all_db1_tb1")) + .addRolesToGroup(USERGROUP3, "all_db1_tb1"); + writePolicyFile(policyFile); + + // user1 has lock privilege only + Connection connection = context.createConnection(USER1_1); + Statement statement = context.createStatement(connection); + statement.execute("Use " + DB1); + statement.execute("LOCK TABLE tb1 SHARED"); + try { + statement.execute("UNLOCK TABLE tb1"); + fail(assertExceptionThrown); + } catch (SQLException se) { + // Authorization is successful. + assertTrue(assertErrorException, se.getMessage().indexOf(partialErrorMsgForNoPrivilege) == -1); + } + + // user2 has privileges with insert, select, alter, but has no lock privilege + connection = context.createConnection(USER2_1); + statement = context.createStatement(connection); + statement.execute("Use " + DB1); + try { + statement.execute("LOCK TABLE tb1 SHARED"); + fail(assertExceptionThrown); + } catch (SQLException se) { + // Authorization is failed, the error message include "No valid privileges" + assertTrue(assertErrorException, + se.getMessage().indexOf(partialErrorMsgForNoPrivilege) > 0); + } + try { + statement.execute("UNLOCK TABLE tb1"); + fail(assertExceptionThrown); + } catch (SQLException se) { + // Authorization is failed, the error message include "No valid privileges" + assertTrue(assertErrorException, + se.getMessage().indexOf(partialErrorMsgForNoPrivilege) > 0); + } + + // user3 has All privilege + connection = context.createConnection(USER3_1); + statement = context.createStatement(connection); + statement.execute("Use " + DB1); + statement.execute("LOCK TABLE tb1 SHARED"); + try { + statement.execute("UNLOCK TABLE tb1"); + fail(assertExceptionThrown); + } catch (SQLException se) { + // Authorization is successful. + assertTrue(assertErrorException, se.getMessage().indexOf(partialErrorMsgForNoPrivilege) == -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/TestMetadataObjectRetrieval.java ---------------------------------------------------------------------- diff --git a/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestMetadataObjectRetrieval.java b/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestMetadataObjectRetrieval.java new file mode 100644 index 0000000..3c23dc4 --- /dev/null +++ b/sentry-tests/sentry-tests-hive-v2/src/test/java/org/apache/sentry/tests/e2e/hive/TestMetadataObjectRetrieval.java @@ -0,0 +1,501 @@ +/* + * 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.assertEquals; +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.Statement; + +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.google.common.io.Resources; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TestMetadataObjectRetrieval extends AbstractTestWithStaticConfiguration { + private static final Logger LOGGER = LoggerFactory + .getLogger(TestMetadataObjectRetrieval.class); + private PolicyFile policyFile; + private File dataFile; + + @BeforeClass + public static void setupTestStaticConfiguration () throws Exception { + LOGGER.info("TestMetadataObjectRetrieval setupTestStaticConfiguration"); + AbstractTestWithStaticConfiguration.setupTestStaticConfiguration(); + } + + @Before + public void setup() throws Exception { + LOGGER.info("TestMetadataObjectRetrieval setup"); + policyFile = super.setupPolicy(); + super.setup(); + 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(); + } + + /** + * Method called to run positive tests: + * describe table + * describe table column + * show columns from table + * show create table table + * show tblproperties table + * + * The table is assumed to have two columns under_col int and value string. + */ + private void positiveDescribeShowTests(String user, String db, String table) throws Exception { + Connection connection = context.createConnection(user); + Statement statement = context.createStatement(connection); + statement.execute("USE " + db); + ResultSet rs = statement.executeQuery("DESCRIBE " + table); + assertTrue(rs.next()); + + assertTrue("describe table fail", rs.getString(1).trim().equals("under_col")); + assertTrue("describe table fail", rs.getString(2).trim().equals("int")); + assertTrue(rs.next()); + assertTrue("describe table fail", rs.getString(1).trim().equals("value")); + assertTrue("describe table fail", rs.getString(2).trim().equals("string")); + + rs = statement.executeQuery("DESCRIBE " + table + " under_col"); + assertTrue(rs.next()); + assertTrue("describe table fail", rs.getString(1).trim().equals("under_col")); + assertTrue("describe table fail", rs.getString(2).trim().equals("int")); + + rs = statement.executeQuery("DESCRIBE " + table + " value"); + assertTrue(rs.next()); + assertTrue("describe table fail", rs.getString(1).trim().equals("value")); + assertTrue("describe table fail", rs.getString(2).trim().equals("string")); + + rs = statement.executeQuery("DESCRIBE EXTENDED " + table); + assertTrue(rs.next()); + assertTrue(rs.getString(1), rs.getString(1).contains("under_col")); + assertTrue(rs.getString(2), rs.getString(2).contains("int")); + assertTrue(rs.next()); + assertTrue(rs.getString(1), rs.getString(1).contains("value")); + assertTrue(rs.getString(2), rs.getString(2).contains("string")); + assertTrue(rs.next()); + + rs = statement.executeQuery("DESCRIBE FORMATTED " + table); + // Skip the header + assertTrue(rs.next()); + assertTrue(rs.next()); + assertTrue(rs.next()); + assertTrue(rs.getString(1), rs.getString(1).contains("under_col")); + assertTrue(rs.getString(2), rs.getString(2).contains("int")); + assertTrue(rs.next()); + assertTrue(rs.getString(1), rs.getString(1).contains("value")); + assertTrue(rs.getString(2), rs.getString(2).contains("string")); + assertTrue(rs.next()); + + rs = statement.executeQuery("SHOW COLUMNS FROM " + table); + assertTrue(rs.next()); + assertTrue("show columns from fail", rs.getString(1).trim().equals("under_col")); + assertTrue(rs.next()); + assertTrue("show columns from fail", rs.getString(1).trim().equals("value")); + + rs = statement.executeQuery("SHOW CREATE TABLE " + table); + assertTrue("SHOW CREATE TABLE fail", rs.next()); + + rs = statement.executeQuery("SHOW TBLPROPERTIES " + table); + assertTrue("SHOW TBLPROPERTIES fail", rs.next()); + + statement.close(); + connection.close(); + } + /** + * Method called to run negative tests: + * describe table + * describe table column + * show columns from table + * show create table table + * show tblproperties table + * + * The table is assumed to have two columns under_col int and value string. + */ + private void negativeDescribeShowTests(String user, String db, String table) throws Exception { + Connection connection = context.createConnection(user); + Statement statement = context.createStatement(connection); + statement.execute("USE " + db); + context.assertAuthzException(statement, "DESCRIBE " + table + " under_col"); + context.assertAuthzException(statement, "DESCRIBE " + table + " value"); + context.assertAuthzException(statement, "DESCRIBE FORMATTED " + table); + context.assertAuthzException(statement, "DESCRIBE EXTENDED " + table); + context.assertAuthzException(statement, "SHOW COLUMNS FROM " + table); + context.assertAuthzException(statement, "SHOW CREATE TABLE " + table); + context.assertAuthzException(statement, "SHOW TBLPROPERTIES " + table); + statement.close(); + connection.close(); + } + + + /** + * Tests to ensure a user with all on server, + * insert|select on table can view metadata while + * a user with all on a different table cannot + * view the metadata. + + * Test both positive and negative of: + * describe table + * describe table column + * show columns from table + * show create table table + * show tblproperties table + * + * Positive tests are run with: + * all@server + * select@table + * insert@table + * Negative tests are run three times: + * none + * insert@different table + */ + @Test + public void testAllOnServerSelectInsertNegativeNoneAllOnDifferentTable() + throws Exception { + createDb(ADMIN1, DB1); + createTable(ADMIN1, DB1, dataFile, TBL1); + createTable(ADMIN1, DB1, dataFile, TBL2); + positiveDescribeShowTests(ADMIN1, DB1, TBL1); + + policyFile + .addPermissionsToRole(GROUP1_ROLE, "server=server1->db=" + DB1 + "->table=" + TBL2) + .addRolesToGroup(USERGROUP1, GROUP1_ROLE) + .setUserGroupMapping(StaticUserGroup.getStaticMapping()); + writePolicyFile(policyFile); + + negativeDescribeShowTests(USER1_1, DB1, TBL1); + + policyFile.addPermissionsToRole(GROUP1_ROLE, SELECT_DB1_TBL1); + writePolicyFile(policyFile); + positiveDescribeShowTests(USER1_1, DB1, TBL1); + + policyFile.removePermissionsFromRole(GROUP1_ROLE, SELECT_DB1_TBL1); + policyFile + .addPermissionsToRole(GROUP1_ROLE, INSERT_DB1_TBL1); + writePolicyFile(policyFile); + positiveDescribeShowTests(USER1_1, DB1, TBL1); + } + + /** + * Tests to ensure that a user is able to view metadata + * with all on db + * + * Test positive: + * describe table + * describe table column + * show columns from table + * show create table table + * show tblproperties table + * + * Positive tests are run twice: + * all@server + * all@db + */ + @Test + public void testAllOnServerAndAllOnDb() throws Exception { + createDb(ADMIN1, DB1); + createTable(ADMIN1, DB1, dataFile, TBL1); + positiveDescribeShowTests(ADMIN1, DB1, TBL1); + + policyFile + .addPermissionsToRole(GROUP1_ROLE, "server=server1->db=" + DB1) + .addRolesToGroup(USERGROUP1, GROUP1_ROLE) + .setUserGroupMapping(StaticUserGroup.getStaticMapping()); + writePolicyFile(policyFile); + + positiveDescribeShowTests(USER1_1, DB1, TBL1); + } + + /** + * Test to ensure that all on view do not result in + * metadata privileges on the underlying table + * + * Test both positive and negative of: + * describe table + * describe table column + * show columns from table + * show create table table + * show tblproperties table + * + * Positive tests are run with all@server + * Negative tests are run three times: + * none + * all@view + */ + @Test + public void testAllOnServerNegativeAllOnView() throws Exception { + createDb(ADMIN1, DB1); + createTable(ADMIN1, DB1, dataFile, TBL1); + Connection connection = context.createConnection(ADMIN1); + Statement statement = context.createStatement(connection); + statement.execute("USE " + DB1); + statement.execute("DROP VIEW IF EXISTS " + VIEW1); + statement.execute("CREATE VIEW " + VIEW1 + " (value) AS SELECT value from " + TBL1 + " LIMIT 10"); + positiveDescribeShowTests(ADMIN1, DB1, TBL1); + statement.close(); + connection.close(); + + policyFile + .addPermissionsToRole(GROUP1_ROLE, "server=server1->db=" + DB1 + "->table=" + VIEW1) + .addRolesToGroup(USERGROUP1, GROUP1_ROLE) + .setUserGroupMapping(StaticUserGroup.getStaticMapping()); + writePolicyFile(policyFile); + + negativeDescribeShowTests(USER1_1, DB1, TBL1); + } + + /** + * Tests to ensure that a user is able to view metadata + * with all on table + * + * Test positive: + * describe table + * describe table column + * show columns from table + * show create table table + * show tblproperties table + * + * Positive tests are run twice: + * all@server + * all@table + */ + @Test + public void testAllOnServerAndAllOnTable() throws Exception { + createDb(ADMIN1, DB1); + createTable(ADMIN1, DB1, dataFile, TBL1); + positiveDescribeShowTests(ADMIN1, DB1, TBL1); + + policyFile + .addPermissionsToRole(GROUP1_ROLE, "server=server1->db=" + DB1 + "->table=" + TBL1) + .addRolesToGroup(USERGROUP1, GROUP1_ROLE) + .setUserGroupMapping(StaticUserGroup.getStaticMapping()); + writePolicyFile(policyFile); + + positiveDescribeShowTests(USER1_1, DB1, TBL1); + } + + + /** + * Tests that admin and all@db can describe database + * and describe database extended. Also tests that a user + * with no privileges on a db cannot describe database. + */ + @Test + public void testDescribeDatabasesWithAllOnServerAndAllOnDb() + throws Exception { + dropDb(ADMIN1, DB1, DB2); + createDb(ADMIN1, DB1, DB2); + createTable(ADMIN1, DB1, dataFile, TBL1); + createTable(ADMIN1, DB2, dataFile, TBL1); + policyFile + .addPermissionsToRole(GROUP1_ROLE, "server=server1->db=" + DB1) + .addRolesToGroup(USERGROUP1, GROUP1_ROLE) + .setUserGroupMapping(StaticUserGroup.getStaticMapping()); + writePolicyFile(policyFile); + + Connection connection = context.createConnection(ADMIN1); + Statement statement = context.createStatement(connection); + assertTrue(statement.executeQuery("DESCRIBE DATABASE " + DB1).next()); + assertTrue(statement.executeQuery("DESCRIBE DATABASE EXTENDED " + DB1).next()); + statement.close(); + connection.close(); + + connection = context.createConnection(USER1_1); + statement = context.createStatement(connection); + assertTrue(statement.executeQuery("DESCRIBE DATABASE " + DB1).next()); + assertTrue(statement.executeQuery("DESCRIBE DATABASE EXTENDED " + DB1).next()); + context.assertAuthzException(statement, "DESCRIBE DATABASE " + DB2); + context.assertAuthzException(statement, "DESCRIBE DATABASE EXTENDED " + DB2); + + policyFile.addPermissionsToRole(GROUP1_ROLE, INSERT_DB2_TBL1); + writePolicyFile(policyFile); + context.assertAuthzException(statement, "DESCRIBE DATABASE " + DB2); + context.assertAuthzException(statement, "DESCRIBE DATABASE EXTENDED " + DB2); + statement.close(); + connection.close(); + } + + /** + * Tests that a user without db level privileges cannot describe default + */ + @Test + public void testDescribeDefaultDatabase() throws Exception { + createDb(ADMIN1, DB1, DB2); + createTable(ADMIN1, "default", dataFile, TBL1); + createTable(ADMIN1, DB1, dataFile, TBL1); + Connection connection = context.createConnection(ADMIN1); + Statement statement = context.createStatement(connection); + assertTrue(statement.executeQuery("DESCRIBE DATABASE default").next()); + statement.execute("USE " + DB1); + assertTrue(statement.executeQuery("DESCRIBE DATABASE default").next()); + assertTrue(statement.executeQuery("DESCRIBE DATABASE " + DB1).next()); + assertTrue(statement.executeQuery("DESCRIBE DATABASE " + DB2).next()); + statement.close(); + connection.close(); + + policyFile + .addPermissionsToRole(GROUP1_ROLE, "server=server1->db=default->table=" + TBL1 + "->action=select", + "server=server1->db=" + DB1 + "->table=" + TBL1 + "->action=select") + .addRolesToGroup(USERGROUP1, GROUP1_ROLE) + .setUserGroupMapping(StaticUserGroup.getStaticMapping()); + writePolicyFile(policyFile); + + connection = context.createConnection(USER1_1); + statement = context.createStatement(connection); + context.assertAuthzException(statement, "DESCRIBE DATABASE default"); + context.assertAuthzException(statement, "DESCRIBE DATABASE " + DB1); + statement.execute("USE " + DB1); + context.assertAuthzException(statement, "DESCRIBE DATABASE " + DB1); + context.assertAuthzException(statement, "DESCRIBE DATABASE " + DB2); + statement.close(); + connection.close(); + } + + /** + * Tests that users without privileges cannot execute show indexes + * and that users with all on table can execute show indexes + */ + @Test + public void testShowIndexes1() throws Exception { + createDb(ADMIN1, DB1); + createTable(ADMIN1, DB1, dataFile, TBL1); + createTable(ADMIN1, DB1, dataFile, TBL2); + Connection connection = context.createConnection(ADMIN1); + Statement statement = context.createStatement(connection); + statement.execute("USE " + DB1); + statement.execute("DROP INDEX IF EXISTS " + INDEX1 + " ON " + TBL1); + statement + .execute("CREATE INDEX " + + INDEX1 + + " ON TABLE " + + TBL1 + + "(value) AS 'org.apache.hadoop.hive.ql.index.compact.CompactIndexHandler' WITH DEFERRED REBUILD"); + statement.execute("DROP VIEW IF EXISTS " + VIEW1); + statement.execute("CREATE VIEW " + VIEW1 + " (value) AS SELECT value from " + TBL1 + " LIMIT 10"); + statement.close(); + connection.close(); + + // grant privilege to table2 to allow use db1 + policyFile.addPermissionsToRole(GROUP1_ROLE, SELECT_DB1_TBL2) + .addRolesToGroup(USERGROUP1, GROUP1_ROLE) + .setUserGroupMapping(StaticUserGroup.getStaticMapping()); + writePolicyFile(policyFile); + + connection = context.createConnection(USER1_1); + statement = context.createStatement(connection); + statement.execute("USE " + DB1); + context.assertAuthzException(statement, "SHOW INDEX ON " + TBL1); + + policyFile + .addPermissionsToRole(GROUP1_ROLE, SELECT_DB1_VIEW1); + writePolicyFile(policyFile); + context.assertAuthzException(statement, "SHOW INDEX ON " + TBL1); + + policyFile.removePermissionsFromRole(GROUP1_ROLE, SELECT_DB1_VIEW1) + .addPermissionsToRole(GROUP1_ROLE, SELECT_DB1_TBL1); + writePolicyFile(policyFile); + verifyIndex(statement, DB1, TBL1, INDEX1); + + policyFile.removePermissionsFromRole(GROUP1_ROLE, SELECT_DB1_TBL1) + .addPermissionsToRole(GROUP1_ROLE, INSERT_DB1_TBL1); + writePolicyFile(policyFile); + verifyIndex(statement, DB1, TBL1, INDEX1); + statement.close(); + connection.close(); + } + + private void verifyIndex(Statement statement, String dbName, String table, String index) throws Exception { + ResultSet rs = statement.executeQuery("SHOW INDEX ON " + table); + assertTrue(rs.next()); + assertEquals(index, rs.getString(1).trim()); + assertEquals(table, rs.getString(2).trim()); + assertEquals("value", rs.getString(3).trim()); + assertEquals(dbName + "__" + table + "_" + index + "__", + rs.getString(4).trim()); + assertEquals("compact", rs.getString(5).trim()); + } + + /** + * Tests that users without privileges cannot execute show partitions + * and that users with select on table can execute show partitions + */ + @Test + public void testShowPartitions1() throws Exception { + createDb(ADMIN1, DB1); + Connection connection = context.createConnection(ADMIN1); + Statement statement = context.createStatement(connection); + statement.execute("USE " + DB1); + statement.execute("DROP TABLE IF EXISTS " + TBL2); + statement.execute("create table " + TBL2 + + " (under_col int, value string) PARTITIONED BY (dt INT)"); + statement.execute("DROP TABLE IF EXISTS " + TBL1); + statement.execute("create table " + TBL1 + + " (under_col int, value string) PARTITIONED BY (dt INT)"); + statement.execute("load data local inpath '" + dataFile.getPath() + + "' into table " + TBL1 + " PARTITION (dt=3)"); + statement.execute("DROP VIEW IF EXISTS " + VIEW1); + statement.execute("CREATE VIEW " + VIEW1 + " (value) AS SELECT value from " + TBL1 + " LIMIT 10"); + statement.close(); + connection.close(); + + // grant privilege to table2 to allow use db1 + policyFile.addPermissionsToRole(GROUP1_ROLE, SELECT_DB1_TBL2) + .addRolesToGroup(USERGROUP1, GROUP1_ROLE) + .setUserGroupMapping(StaticUserGroup.getStaticMapping()); + writePolicyFile(policyFile); + + connection = context.createConnection(USER1_1); + statement = context.createStatement(connection); + statement.execute("USE " + DB1); + context.assertAuthzException(statement, "SHOW PARTITIONS " + TBL1); + + policyFile + .addPermissionsToRole(GROUP1_ROLE, SELECT_DB1_VIEW1); + writePolicyFile(policyFile); + context.assertAuthzException(statement, "SHOW PARTITIONS " + TBL1); + + policyFile + .removePermissionsFromRole(GROUP1_ROLE, SELECT_DB1_VIEW1) + .addPermissionsToRole(GROUP1_ROLE, SELECT_DB1_TBL1); + writePolicyFile(policyFile); + verifyParition(statement, TBL1); + + policyFile.removePermissionsFromRole(GROUP1_ROLE, SELECT_DB1_TBL1) + .addPermissionsToRole(GROUP1_ROLE, INSERT_DB1_TBL1); + writePolicyFile(policyFile); + verifyParition(statement, TBL1); + statement.close(); + connection.close(); + } + + private void verifyParition(Statement statement, String table) throws Exception { + ResultSet rs = statement.executeQuery("SHOW PARTITIONS " + table); + assertTrue(rs.next()); + assertEquals("dt=3", rs.getString(1).trim()); + } +}
