blob: 767bcbe02e0d511f52bc869e7b6a1ee1e6584a5a [file] [log] [blame]
/**
* 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.dbprovider;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
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.ArrayList;
import java.util.List;
import org.apache.sentry.tests.e2e.hive.AbstractTestWithStaticConfiguration;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import com.google.common.collect.Lists;
import com.google.common.io.Resources;
public class TestDbPrivilegeCleanupOnDrop extends
AbstractTestWithStaticConfiguration {
private final static int SHOW_GRANT_TABLE_POSITION = 2;
private final static int SHOW_GRANT_DB_POSITION = 1;
private final String SINGLE_TYPE_DATA_FILE_NAME = "kv1.dat";
private final static String tableName1 = "tb_1";
private final static String tableName2 = "tb_2";
private final static String tableName3 = "tb_3";
private final static String tableName4 = "tb_4";
private final static String renameTag = "_new";
@BeforeClass
public static void setupTestStaticConfiguration() throws Exception {
useSentryService = true;
if (!setMetastoreListener) {
setMetastoreListener = true;
}
AbstractTestWithStaticConfiguration.setupTestStaticConfiguration();
}
@Override
@Before
public void setup() throws Exception {
super.setupAdmin();
super.setup();
// context = createContext();
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();
}
@After
public void tearDown() throws Exception {
if (context != null) {
context.close();
}
}
/**
* drop table and verify that the no privileges are referring to it drop db
* and verify that the no privileges are referring to it drop db cascade
* verify that the no privileges are referring to db and tables under it
*
* @throws Exception
*/
@Test
public void testDropObjects() throws Exception {
Connection connection = context.createConnection(ADMIN1);
Statement statement = context.createStatement(connection);
setupRoles(statement); // create required roles
setupDbObjects(statement); // create test DBs and Tables
setupPrivileges(statement); // setup privileges for USER1
dropDbObjects(statement); // drop objects
verifyPrivilegesDropped(statement); // verify privileges are removed
statement.close();
connection.close();
}
/**
* Return the remaining rows of the current resultSet
* Cautiously it will modify the cursor position of the resultSet
*
*/
private void assertRemainingRows(ResultSet resultSet, int expected) throws SQLException{
int count = 0;
while(resultSet.next()) {
count++;
}
assertThat(count, is(expected));
}
/**
* drop table and verify that the no privileges are referring to it drop db
* and verify that the no privileges are referring to it drop db cascade
* verify that the no privileges are referring to db and tables under it
*
* @throws Exception
*/
@Test
public void testReCreateObjects() throws Exception {
Connection connection = context.createConnection(ADMIN1);
Statement statement = context.createStatement(connection);
setupRoles(statement); // create required roles
setupDbObjects(statement); // create test DBs and Tables
setupPrivileges(statement); // setup privileges for USER1
dropDbObjects(statement); // drop DB and tables
setupDbObjects(statement); // recreate same DBs and tables
verifyPrivilegesDropped(statement); // verify the stale privileges removed
}
/**
* rename table and verify that the no privileges are referring to it old table
* verify that the same privileges are created for the new table name
*
* @throws Exception
*/
@Test
public void testRenameTables() throws Exception {
Connection connection = context.createConnection(ADMIN1);
Statement statement = context.createStatement(connection);
setupRoles(statement); // create required roles
setupDbObjects(statement); // create test DBs and Tables
setupPrivileges(statement); // setup privileges for USER1
// verify privileges on the created tables
statement.execute("USE " + DB2);
verifyTablePrivilegeExist(statement,
Lists.newArrayList("select_tbl1", "insert_tbl1", "all_tbl1"),
tableName1);
verifyTablePrivilegeExist(statement, Lists.newArrayList("all_tbl2"),
tableName2);
renameTables(statement); // alter tables to rename
// verify privileges removed for old tables
verifyTablePrivilegesDropped(statement);
// verify privileges created for new tables
statement.execute("USE " + DB2);
verifyTablePrivilegeExist(statement,
Lists.newArrayList("select_tbl1", "insert_tbl1", "all_tbl1"),
tableName1 + renameTag);
verifyTablePrivilegeExist(statement, Lists.newArrayList("all_tbl2"),
tableName2 + renameTag);
statement.close();
connection.close();
}
/**
* After we drop/rename table, we will drop/rename all privileges(ALL,SELECT,INSERT,ALTER,DROP...)
* from this role
*
* @throws Exception
*/
@Test
public void testDropAndRenameWithMultiAction() throws Exception {
Connection connection = context.createConnection(ADMIN1);
Statement statement = context.createStatement(connection);
statement.execute("CREATE ROLE user_role");
statement.execute("GRANT ROLE user_role TO GROUP " + USERGROUP1);
statement.execute("CREATE DATABASE " + DB1);
statement.execute("USE " + DB1);
statement.execute("CREATE TABLE t1 (c1 string)");
// Grant SELECT/INSERT/DROP/ALTER to TABLE t1
statement.execute("GRANT SELECT ON TABLE t1 TO ROLE user_role");
statement.execute("GRANT INSERT ON TABLE t1 TO ROLE user_role");
statement.execute("GRANT ALTER ON TABLE t1 TO ROLE user_role");
statement.execute("GRANT DROP ON TABLE t1 TO ROLE user_role");
// For rename, grant CREATE to DB1
statement.execute("GRANT CREATE ON DATABASE " + DB1 + " TO ROLE user_role");
// After rename table t1 to t2, user_role will have no permission to drop t1
connection = context.createConnection(USER1_1);
statement = context.createStatement(connection);
statement.execute("USE " + DB1);
statement.execute("ALTER TABLE t1 RENAME TO t2");
context.assertSentrySemanticException(statement, "drop table t1", semanticException);
// After rename table t1 to t2, user_role should have permission to drop t2
statement.execute("drop table t2");
ResultSet resultSet = statement.executeQuery("SHOW GRANT ROLE user_role");
// user_role will revoke all privilege from table t2, only remain CREATE on db_1
assertRemainingRows(resultSet, 1);
statement.close();
connection.close();
}
// Create test roles
private void setupRoles(Statement statement) throws Exception {
statement.execute("CREATE ROLE all_db1");
statement.execute("CREATE ROLE read_db1");
statement.execute("CREATE ROLE select_tbl1");
statement.execute("CREATE ROLE insert_tbl1");
statement.execute("CREATE ROLE all_tbl1");
statement.execute("CREATE ROLE all_tbl2");
statement.execute("CREATE ROLE all_prod");
statement.execute("GRANT ROLE all_db1, read_db1, select_tbl1, insert_tbl1,"
+ " all_tbl1, all_tbl2, all_prod to GROUP " + USERGROUP1);
statement.execute("DROP DATABASE IF EXISTS " + DB1 + " CASCADE");
statement.execute("DROP DATABASE IF EXISTS " + DB2 + " CASCADE");
}
// create test DBs and Tables
private void setupDbObjects(Statement statement) throws Exception {
statement.execute("CREATE DATABASE " + DB1);
statement.execute("CREATE DATABASE " + DB2);
statement.execute("create table " + DB2 + "." + tableName1
+ " (under_col int comment 'the under column', value string)");
statement.execute("create table " + DB2 + "." + tableName2
+ " (under_col int comment 'the under column', value string)");
statement.execute("create table " + DB1 + "." + tableName3
+ " (under_col int comment 'the under column', value string)");
statement.execute("create table " + DB1 + "." + tableName4
+ " (under_col int comment 'the under column', value string)");
}
// Create privileges on DB and Tables
private void setupPrivileges(Statement statement) throws Exception {
statement.execute("GRANT ALL ON DATABASE " + DB1 + " TO ROLE all_db1");
statement.execute("GRANT SELECT ON DATABASE " + DB1
+ " TO ROLE read_db1");
statement.execute("GRANT ALL ON DATABASE " + DB2 + " TO ROLE all_prod");
statement.execute("USE " + DB2);
statement.execute("GRANT SELECT ON TABLE " + tableName1
+ " TO ROLE select_tbl1");
statement.execute("GRANT INSERT ON TABLE " + tableName1
+ " TO ROLE insert_tbl1");
statement.execute("GRANT ALL ON TABLE " + tableName1 + " TO ROLE all_tbl1");
statement.execute("GRANT ALL ON TABLE " + tableName2 + " TO ROLE all_tbl2");
}
// Drop test DBs and Tables
private void dropDbObjects(Statement statement) throws Exception {
statement.execute("DROP TABLE " + DB2 + "." + tableName1);
statement.execute("DROP TABLE " + DB2 + "." + tableName2);
statement.execute("DROP DATABASE " + DB2);
statement.execute("DROP DATABASE " + DB1 + " CASCADE");
}
// rename tables
private void renameTables(Statement statement) throws Exception {
statement.execute("USE " + DB2);
statement.execute("ALTER TABLE " + tableName1 + " RENAME TO " + tableName1
+ renameTag);
statement.execute("ALTER TABLE " + tableName2 + " RENAME TO " + tableName2
+ renameTag);
statement.execute("USE " + DB1);
statement.execute("ALTER TABLE " + tableName3 + " RENAME TO " + tableName3
+ renameTag);
statement.execute("ALTER TABLE " + tableName4 + " RENAME TO " + tableName4
+ renameTag);
}
// verify all the test privileges are dropped as we drop the objects
private void verifyPrivilegesDropped(Statement statement)
throws Exception {
verifyDbPrivilegesDropped(statement);
verifyTablePrivilegesDropped(statement);
}
// verify all the test privileges are dropped as we drop the objects
private void verifyTablePrivilegesDropped(Statement statement)
throws Exception {
List<String> roles = getRoles(statement);
verifyPrivilegeDropped(statement, roles, tableName1,
SHOW_GRANT_TABLE_POSITION);
verifyPrivilegeDropped(statement, roles, tableName2,
SHOW_GRANT_TABLE_POSITION);
verifyPrivilegeDropped(statement, roles, tableName3,
SHOW_GRANT_TABLE_POSITION);
verifyPrivilegeDropped(statement, roles, tableName4,
SHOW_GRANT_TABLE_POSITION);
}
// verify all the test privileges are dropped as we drop the objects
private void verifyDbPrivilegesDropped(Statement statement) throws Exception {
List<String> roles = getRoles(statement);
verifyPrivilegeDropped(statement, roles, DB2, SHOW_GRANT_DB_POSITION);
verifyPrivilegeDropped(statement, roles, DB1, SHOW_GRANT_DB_POSITION);
}
// verify given table/DB has no longer permissions
private void verifyPrivilegeDropped(Statement statement, List<String> roles,
String objectName, int resultPos) throws Exception {
for (String roleName : roles) {
ResultSet resultSet = statement.executeQuery("SHOW GRANT ROLE "
+ roleName);
while (resultSet.next()) {
String returned = resultSet.getString(resultPos);
assertFalse("value " + objectName + " shouldn't be detected, but actually " + returned + " is found from resultSet",
objectName.equalsIgnoreCase(returned));
}
resultSet.close();
}
}
// verify given table is part of the role
private void verifyTablePrivilegeExist(Statement statement,
List<String> roles, String tableName) throws Exception {
for (String roleName : roles) {
ResultSet resultSet = statement.executeQuery("SHOW GRANT ROLE "
+ roleName + " ON TABLE " + tableName);
assertTrue(resultSet.next());
resultSet.close();
}
}
private List<String> getRoles(Statement statement) throws Exception {
ArrayList<String> roleList = Lists.newArrayList();
ResultSet resultSet = statement.executeQuery("SHOW ROLES ");
while (resultSet.next()) {
roleList.add(resultSet.getString(1));
}
return roleList;
}
}