blob: 15cbc5a86b58c08ec35a2f9ff4977d1a2751578d [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.junit.Assert.*;
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.core.common.exception.SentryAccessDeniedException;
import org.apache.sentry.tests.e2e.hive.AbstractTestWithStaticConfiguration;
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 TestColumnEndToEnd extends AbstractTestWithStaticConfiguration {
private static final Logger LOGGER = LoggerFactory.
getLogger(TestColumnEndToEnd.class);
private final String SINGLE_TYPE_DATA_FILE_NAME = "kv1.dat";
private File dataFile;
@BeforeClass
public static void setupTestStaticConfiguration() throws Exception{
LOGGER.info("TestColumnEndToEnd setupTestStaticConfiguration");
useSentryService = true;
AbstractTestWithStaticConfiguration.setupTestStaticConfiguration();
}
@Override
@Before
public void setup() throws Exception {
super.setupAdmin();
super.setup();
super.setupPolicy();
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 testBasic() throws Exception {
Connection connection = context.createConnection(ADMIN1);
Statement statement = context.createStatement(connection);
statement.execute("CREATE database " + DB1);
statement.execute("USE " + DB1);
statement.execute("CREATE TABLE t1 (c1 string)");
statement.execute("CREATE ROLE user_role");
statement.execute("GRANT SELECT ON TABLE t1 TO ROLE user_role");
statement.execute("GRANT ROLE user_role TO GROUP " + USERGROUP1);
statement.close();
connection.close();
connection = context.createConnection(USER1_1);
statement = context.createStatement(connection);
context.assertSentryException(statement, "CREATE ROLE r2",
SentryAccessDeniedException.class.getSimpleName());
statement.execute("SELECT * FROM " + DB1 + ".t1");
statement.close();
connection.close();
}
@Test
public void testDescribeTbl() throws Exception {
Connection connection = context.createConnection(ADMIN1);
Statement statement = context.createStatement(connection);
statement.execute("CREATE TABLE IF NOT EXISTS t1 (c1 string, c2 string)");
statement.execute("CREATE TABLE t2 (c1 string, c2 string)");
statement.execute("CREATE ROLE user_role1");
statement.execute("GRANT SELECT (c1) ON TABLE t1 TO ROLE user_role1");
statement.execute("GRANT ROLE user_role1 TO GROUP " + USERGROUP1);
statement.close();
connection.close();
connection = context.createConnection(USER1_1);
statement = context.createStatement(connection);
// Expect that DESCRIBE table works with only column-level privileges, but other
// DESCRIBE variants like DESCRIBE FORMATTED fail. Note that if a user has privileges
// on any column they can describe all columns.
ResultSet rs = statement.executeQuery("DESCRIBE t1");
assertTrue(rs.next());
assertEquals("c1", rs.getString(1));
assertEquals("string", rs.getString(2));
assertTrue(rs.next());
assertEquals("c2", rs.getString(1));
assertEquals("string", rs.getString(2));
statement.executeQuery("DESCRIBE t1 c1");
statement.executeQuery("DESCRIBE t1 c2");
try {
statement.executeQuery("DESCRIBE t2");
fail("Expected DESCRIBE to fail on t2");
} catch (SQLException e) {
context.verifyAuthzException(e);
}
try {
statement.executeQuery("DESCRIBE FORMATTED t1");
fail("Expected DESCRIBE FORMATTED to fail");
} catch (SQLException e) {
context.verifyAuthzException(e);
}
try {
statement.executeQuery("DESCRIBE EXTENDED t1");
fail("Expected DESCRIBE EXTENDED to fail");
} catch (SQLException e) {
context.verifyAuthzException(e);
}
statement.close();
connection.close();
// Cleanup
connection = context.createConnection(ADMIN1);
statement = context.createStatement(connection);
statement.execute("DROP TABLE t1");
statement.execute("DROP TABLE t2");
statement.execute("DROP ROLE user_role1");
statement.close();
connection.close();
}
@Test
public void testNegative() throws Exception {
Connection connection = context.createConnection(ADMIN1);
Statement statement = context.createStatement(connection);
statement.execute("CREATE TABLE t1 (c1 string, c2 string)");
statement.execute("CREATE ROLE user_role1");
statement.execute("CREATE ROLE user_role2");
statement.execute("GRANT SELECT (c1) ON TABLE t1 TO ROLE user_role1");
statement.execute("GRANT SELECT (c1,c2) ON TABLE t1 TO ROLE user_role2");
//Make sure insert/all are not supported
try {
statement.execute("GRANT INSERT (c2) ON TABLE t1 TO ROLE user_role2");
assertTrue("Sentry should not support privilege: Insert on Column", false);
} catch (Exception e) {
assertTrue("The error should be 'Sentry does not support privilege: Insert on Column'",
e.getMessage().toUpperCase().contains("SENTRY DOES NOT SUPPORT PRIVILEGE: INSERT ON COLUMN"));
}
try {
statement.execute("GRANT ALL (c2) ON TABLE t1 TO ROLE user_role2");
assertTrue("Sentry should not support privilege: ALL on Column", false);
} catch (Exception e) {
assertTrue("The error should be 'Sentry does not support privilege: All on Column'",
e.getMessage().toUpperCase().contains("SENTRY DOES NOT SUPPORT PRIVILEGE: ALL ON COLUMN"));
}
statement.execute("GRANT ROLE user_role1 TO GROUP " + USERGROUP1);
statement.execute("GRANT ROLE user_role2 TO GROUP " + USERGROUP2);
statement.close();
connection.close();
/*
Behavior of select col, select count(col), select *, and select count(*), count(1)
*/
// 1.1 user_role1 select c1,c2 from t1, will throw exception
connection = context.createConnection(USER1_1);
statement = context.createStatement(connection);
try {
statement.execute("SELECT c1,c2 FROM t1");
assertTrue("User with privilege on one column is able to access other column!!", false);
} catch (SQLException e) {
context.verifyAuthzException(e);
}
// 1.2 user_role1 count(col) works, *, count(*) and count(1) fails
statement.execute("SELECT count(c1) FROM t1");
try {
statement.execute("SELECT * FROM t1");
assertTrue("Select * should fail - only SELECT allowed on t1.c1!!", false);
} catch (SQLException e) {
context.verifyAuthzException(e);
}
try {
statement.execute("SELECT count(*) FROM t1");
assertTrue("Select count(*) should fail - only SELECT allowed on t1.c1!!", false);
} catch (SQLException e) {
context.verifyAuthzException(e);
}
try {
statement.execute("SELECT count(1) FROM t1");
assertTrue("Select count(1) should fail - only SELECT allowed on t1.c1!!", false);
} catch (SQLException e) {
context.verifyAuthzException(e);
}
statement.close();
connection.close();
// 2.1 user_role2 can do *, count(col), but count(*) and count(1) fails
connection = context.createConnection(USER2_1);
statement = context.createStatement(connection);
statement.execute("SELECT count(c1) FROM t1");
statement.execute("SELECT * FROM t1");
//SENTRY-838
try {
statement.execute("SELECT count(*) FROM t1");
assertTrue("Select count(*) works only with table level privileges - User has select on all columns!!", false);
} catch (Exception e) {
// Ignore
}
try {
statement.execute("SELECT count(1) FROM t1");
assertTrue("Select count(1) works only with table level privileges - User has select on all columns!!", false);
} catch (Exception e) {
// Ignore
}
statement.close();
connection.close();
}
@Test
public void testPositive() throws Exception {
Connection connection = context.createConnection(ADMIN1);
Statement statement = context.createStatement(connection);
statement.execute("CREATE database " + DB1);
statement.execute("use " + DB1);
statement.execute("CREATE TABLE t1 (c1 string, c2 string)");
statement.execute("CREATE ROLE user_role1");
statement.execute("CREATE ROLE user_role2");
statement.execute("CREATE ROLE user_role3");
statement.execute("GRANT SELECT (c1) ON TABLE t1 TO ROLE user_role1");
statement.execute("GRANT SELECT (c1, c2) ON TABLE t1 TO ROLE user_role2");
statement.execute("GRANT SELECT ON TABLE t1 TO ROLE user_role3");
statement.execute("GRANT ROLE user_role1 TO GROUP " + USERGROUP1);
statement.execute("GRANT ROLE user_role2 TO GROUP " + USERGROUP2);
statement.execute("GRANT ROLE user_role3 TO GROUP " + USERGROUP3);
statement.close();
connection.close();
// 1 user_role1 select c1 on t1
connection = context.createConnection(USER1_1);
statement = context.createStatement(connection);
statement.execute("use " + DB1);
statement.execute("SELECT c1 FROM t1");
statement.execute("DESCRIBE t1");
// 2.1 user_role2 select c1,c2 on t1
connection = context.createConnection(USER2_1);
statement = context.createStatement(connection);
statement.execute("use " + DB1);
statement.execute("SELECT c1,c2 FROM t1");
// 2.2 user_role2 select * on t1
statement.execute("SELECT * FROM t1");
// 3.1 user_role3 select * on t1
connection = context.createConnection(USER3_1);
statement = context.createStatement(connection);
statement.execute("use " + DB1);
statement.execute("SELECT * FROM t1");
// 3.2 user_role3 select c1,c2 on t1
statement.execute("SELECT c1,c2 FROM t1");
statement.close();
connection.close();
}
@Test
public void testCreateTableAsSelect() throws Exception {
Connection connection = context.createConnection(ADMIN1);
Statement statement = context.createStatement(connection);
statement.execute("CREATE database " + DB1);
statement.execute("use " + DB1);
statement.execute("CREATE TABLE t1 (c1 string, c2 string)");
statement.execute("CREATE ROLE user_role1");
statement.execute("CREATE ROLE user_role2");
statement.execute("CREATE ROLE user_role3");
statement.execute("GRANT SELECT (c1) ON TABLE t1 TO ROLE user_role1");
statement.execute("GRANT SELECT (c1, c2) ON TABLE t1 TO ROLE user_role2");
statement.execute("GRANT SELECT ON TABLE t1 TO ROLE user_role3");
statement.execute("GRANT CREATE ON DATABASE " + DB1 + " TO ROLE user_role1");
statement.execute("GRANT CREATE ON DATABASE " + DB1 + " TO ROLE user_role2");
statement.execute("GRANT CREATE ON DATABASE " + DB1 + " TO ROLE user_role3");
statement.execute("GRANT ROLE user_role1 TO GROUP " + USERGROUP1);
statement.execute("GRANT ROLE user_role2 TO GROUP " + USERGROUP2);
statement.execute("GRANT ROLE user_role3 TO GROUP " + USERGROUP3);
statement.close();
connection.close();
// 1 user_role1 create table as select
connection = context.createConnection(USER1_1);
statement = context.createStatement(connection);
statement.execute("use " + DB1);
statement.execute("CREATE TABLE t1_1 AS SELECT c1 FROM t1");
try {
statement.execute("CREATE TABLE t1_2 AS SELECT * FROM t1");
assertTrue("no permission on table t1!!", false);
} catch (Exception e) {
// Ignore
}
// 2 user_role2 create table as select
connection = context.createConnection(USER2_1);
statement = context.createStatement(connection);
statement.execute("use " + DB1);
statement.execute("CREATE TABLE t2_1 AS SELECT c1 FROM t1");
statement.execute("CREATE TABLE t2_2 AS SELECT * FROM t1");
// 3 user_role3 create table as select
connection = context.createConnection(USER3_1);
statement = context.createStatement(connection);
statement.execute("use " + DB1);
statement.execute("CREATE TABLE t3_1 AS SELECT c1 FROM t1");
statement.execute("CREATE TABLE t3_2 AS SELECT * FROM t1");
statement.close();
connection.close();
}
@Test
public void testShowColumns() throws Exception {
// grant select on test_tb(s) to USER1_1
Connection connection = context.createConnection(ADMIN1);
Statement statement = context.createStatement(connection);
statement.execute("CREATE database " + DB1);
statement.execute("use " + DB1);
statement.execute("CREATE TABLE test_tb (s string, i string)");
statement.execute("CREATE ROLE user_role1");
statement.execute("GRANT SELECT (s) ON TABLE test_tb TO ROLE user_role1");
statement.execute("GRANT ROLE user_role1 TO GROUP " + USERGROUP1);
statement.close();
connection.close();
// USER1_1 executes "show columns in test_tb" and gets the s column information
connection = context.createConnection(USER1_1);
statement = context.createStatement(connection);
statement.execute("use " + DB1);
ResultSet res = statement.executeQuery("show columns in test_tb");
List<String> expectedResult = new ArrayList<String>();
List<String> returnedResult = new ArrayList<String>();
expectedResult.add("s");
while (res.next()) {
returnedResult.add(res.getString(1).trim());
}
validateReturnedResult(expectedResult, returnedResult);
returnedResult.clear();
expectedResult.clear();
res.close();
statement.close();
connection.close();
// grant select on test_tb(s, i) to USER2_1
connection = context.createConnection(ADMIN1);
statement = context.createStatement(connection);
statement.execute("use " + DB1);
statement.execute("CREATE ROLE user_role2");
statement.execute("GRANT SELECT(s, i) ON TABLE test_tb TO ROLE user_role2");
statement.execute("GRANT ROLE user_role2 TO GROUP " + USERGROUP2);
statement.close();
connection.close();
// USER2_1 executes "show columns in test_tb" and gets the s,i columns information
connection = context.createConnection(USER2_1);
statement = context.createStatement(connection);
statement.execute("use " + DB1);
res = statement.executeQuery("show columns in test_tb");
expectedResult.add("s");
expectedResult.add("i");
while (res.next()) {
returnedResult.add(res.getString(1).trim());
}
validateReturnedResult(expectedResult, returnedResult);
returnedResult.clear();
expectedResult.clear();
res.close();
statement.close();
connection.close();
// USER3_1 executes "show columns in test_tb" and the exception will be thrown
connection = context.createConnection(USER3_1);
statement = context.createStatement(connection);
try {
// USER3_1 has no privilege on any column, so "show columns in test_tb" will throw an exception
statement.execute("show columns in db_1.test_tb");
fail("No valid privileges exception should have been thrown");
} catch (Exception e) {
}
statement.close();
connection.close();
}
@Test
public void testCrossDbTableOperations() throws Exception {
//The privilege of user_role1 is used to test create table as select.
//The privilege of user_role2 is used to test create view as select.
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 ROLE user_role1");
statement.execute("CREATE ROLE user_role2");
statement.execute("CREATE TABLE tb1 (id int , name String)");
statement.execute("GRANT CREATE ON DATABASE db_1 TO ROLE user_role1");
statement.execute("GRANT CREATE ON DATABASE db_1 TO ROLE user_role2");
statement.execute("GRANT SELECT (id) ON TABLE tb1 TO ROLE user_role1");
statement.execute("GRANT SELECT ON TABLE tb1 TO ROLE user_role2");
statement.execute("GRANT ROLE user_role1 TO GROUP " + USERGROUP1);
statement.execute("GRANT ROLE user_role2 TO GROUP " + USERGROUP2);
statement.execute("load data local inpath '" + dataFile.getPath() + "' into table db_1.tb1" );
statement.execute("use " + DB2);
statement.execute("CREATE TABLE tb2 (id int, num String)");
statement.execute("GRANT SELECT (num) ON TABLE tb2 TO ROLE user_role1");
statement.execute("GRANT SELECT ON TABLE tb2 TO ROLE user_role2");
statement.execute("GRANT ROLE user_role1 TO GROUP " + USERGROUP1);
statement.execute("GRANT ROLE user_role2 TO GROUP " + USERGROUP2);
statement.execute("load data local inpath '" + dataFile.getPath() + "' into table db_2.tb2" );
statement.close();
connection.close();
connection =context.createConnection(USER1_1);
statement = context.createStatement(connection);
statement.execute("use " + DB1);
statement.execute("CREATE table db_1.t1 as select tb2.num from db_2.tb2");
// make sure the async processing is done before test clean up. Otherwise, the test may fail
Thread.sleep(1000);
statement.close();
connection.close();
}
@Test
public void testCrossDbTableOperations2() throws Exception {
//The privilege of user_role1 is used to test create table as select.
//The privilege of user_role2 is used to test create view as select.
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 ROLE user_role1");
statement.execute("CREATE ROLE user_role2");
statement.execute("CREATE TABLE tb1 (id int , name String)");
statement.execute("GRANT CREATE ON DATABASE db_1 TO ROLE user_role1");
statement.execute("GRANT CREATE ON DATABASE db_1 TO ROLE user_role2");
statement.execute("GRANT SELECT (id) ON TABLE tb1 TO ROLE user_role1");
statement.execute("GRANT SELECT ON TABLE tb1 TO ROLE user_role2");
statement.execute("GRANT ROLE user_role1 TO GROUP " + USERGROUP1);
statement.execute("GRANT ROLE user_role2 TO GROUP " + USERGROUP2);
statement.execute("load data local inpath '" + dataFile.getPath() + "' into table db_1.tb1" );
statement.execute("use " + DB2);
statement.execute("CREATE TABLE tb2 (id int, num String)");
statement.execute("CREATE TABLE tb3 (id int, val String)");
statement.execute("GRANT SELECT (num) ON TABLE tb2 TO ROLE user_role1");
statement.execute("GRANT SELECT (val) ON TABLE tb3 TO ROLE user_role1");
statement.execute("GRANT SELECT ON TABLE tb2 TO ROLE user_role2");
statement.execute("GRANT SELECT ON TABLE tb3 TO ROLE user_role2");
statement.execute("GRANT ROLE user_role1 TO GROUP " + USERGROUP1);
statement.execute("GRANT ROLE user_role2 TO GROUP " + USERGROUP2);
statement.execute("load data local inpath '" + dataFile.getPath() + "' into table db_2.tb2" );
statement.execute("load data local inpath '" + dataFile.getPath() + "' into table db_2.tb3" );
statement.close();
connection.close();
connection =context.createConnection(USER2_1);
statement = context.createStatement(connection);
//The db_1.tb1 and db_2.tb3 is same with db_2.tb2.
ResultSet res = statement.executeQuery("select * from db_2.tb2 limit 2");
List<String> expectedResult = new ArrayList<String>();
List<String> returnedResult = new ArrayList<String>();
expectedResult.add("238");
expectedResult.add("86");
while(res.next()){
returnedResult.add(res.getString(1).trim());
}
validateReturnedResult(expectedResult, returnedResult);
expectedResult.clear();
returnedResult.clear();
statement.execute("use " + DB1);
statement.execute("CREATE VIEW db_1.v1 as select tb1.id, tb3.val, tb2.num from db_1.tb1,db_2.tb3,db_2.tb2");
statement.close();
connection.close();
}
}