| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.sentry.tests.e2e.hive; |
| |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotNull; |
| 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.Map; |
| |
| import org.apache.hadoop.hive.conf.HiveConf; |
| import org.apache.hadoop.hive.conf.HiveConf.ConfVars; |
| import org.apache.sentry.binding.hive.HiveAuthzBindingSessionHook; |
| import org.apache.sentry.binding.hive.conf.HiveAuthzConf; |
| import org.apache.sentry.provider.file.PolicyFile; |
| import org.apache.sentry.tests.e2e.hive.hiveserver.HiveServerFactory; |
| import org.junit.After; |
| import org.junit.Assert; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| import com.google.common.base.Charsets; |
| import com.google.common.collect.Maps; |
| |
| public class TestServerConfiguration extends AbstractTestWithHiveServer { |
| |
| // Context is created inside individual test cases, because the |
| // test cases for server configuration are properties based. |
| private static Context context; |
| private static Map<String, String> properties; |
| private PolicyFile policyFile; |
| |
| @Before |
| public void setupPolicyFile() throws Exception { |
| properties = Maps.newHashMap(); |
| policyFile = PolicyFile.setAdminOnServer1(ADMINGROUP); |
| } |
| |
| @After |
| public void tearDown() throws Exception { |
| if(context != null) { |
| context.close(); |
| } |
| |
| if(hiveServer != null) { |
| hiveServer.shutdown(); |
| hiveServer = null; |
| } |
| } |
| |
| /** |
| * hive.server2.enable.impersonation must be disabled |
| */ |
| @Test |
| public void testImpersonationIsDisabled() throws Exception { |
| properties.put(HiveServerFactory.ACCESS_TESTING_MODE, "false"); |
| properties.put("hive.server2.enable.impersonation", "true"); |
| verifyInvalidConfigurationException(properties); |
| } |
| |
| /** |
| * hive.server2.authentication must be set to LDAP or KERBEROS |
| */ |
| @Test |
| public void testAuthenticationIsStrong() throws Exception { |
| properties.put(HiveServerFactory.ACCESS_TESTING_MODE, "false"); |
| properties.put("hive.server2.authentication", "NONE"); |
| verifyInvalidConfigurationException(properties); |
| } |
| |
| private void verifyInvalidConfigurationException(Map<String, String> properties) throws Exception{ |
| context = createContext(properties); |
| policyFile |
| .setUserGroupMapping(StaticUserGroup.getStaticMapping()) |
| .write(context.getPolicyFile()); |
| Connection connection = context.createConnection(ADMIN1); |
| Statement statement = context.createStatement(connection); |
| try { |
| statement.execute("create table test (a string)"); |
| Assert.fail("Expected SQLException"); |
| } catch (SQLException e) { |
| context.verifyInvalidConfigurationException(e); |
| } finally { |
| if (context != null) { |
| context.close(); |
| } |
| } |
| } |
| |
| /** |
| * Test removal of policy file |
| */ |
| @Test |
| public void testRemovalOfPolicyFile() throws Exception { |
| context = createContext(properties); |
| Connection connection = context.createConnection(ADMIN1); |
| Statement statement = context.createStatement(connection); |
| try { |
| statement.execute("DROP TABLE IF EXISTS test CASCADE"); |
| statement.execute("create table test (a string)"); |
| Assert.fail("Expected SQLException"); |
| } catch (SQLException e) { |
| context.verifyAuthzException(e); |
| } |
| } |
| |
| /** |
| * Test corruption of policy file |
| */ |
| @Test |
| public void testCorruptionOfPolicyFile() throws Exception { |
| context = createContext(properties); |
| File policyFile = context.getPolicyFile(); |
| FileOutputStream out = new FileOutputStream(policyFile); |
| out.write("this is not valid".getBytes(Charsets.UTF_8)); |
| out.close(); |
| Connection connection = context.createConnection(ADMIN1); |
| Statement statement = context.createStatement(connection); |
| try { |
| statement.execute("DROP TABLE IF EXISTS test CASCADE"); |
| statement.execute("create table test (a string)"); |
| Assert.fail("Expected SQLException"); |
| } catch (SQLException e) { |
| context.verifyAuthzException(e); |
| } |
| } |
| |
| @Test |
| public void testAddDeleteDFSRestriction() throws Exception { |
| context = createContext(properties); |
| policyFile |
| .addRolesToGroup(USERGROUP1, "all_db1") |
| .addRolesToGroup(USERGROUP2, "select_tb1") |
| .addPermissionsToRole("select_tb1", "server=server1->db=db_1->table=tbl_1->action=select") |
| .addPermissionsToRole("all_db1", "server=server1->db=db_1") |
| .setUserGroupMapping(StaticUserGroup.getStaticMapping()) |
| .write(context.getPolicyFile()); |
| |
| Connection connection = context.createConnection(USER1_1); |
| Statement statement = context.createStatement(connection); |
| |
| // disallow external executables. The external.exec is set to false by session hooks |
| context.assertAuthzException(statement, "ADD JAR /usr/lib/hive/lib/hbase.jar"); |
| context.assertAuthzException(statement, "ADD FILE /tmp/tt.py"); |
| context.assertAuthzException(statement, "DFS -ls"); |
| context.assertAuthzException(statement, "DELETE JAR /usr/lib/hive/lib/hbase.jar"); |
| context.assertAuthzException(statement, "DELETE FILE /tmp/tt.py"); |
| statement.close(); |
| connection.close(); |
| } |
| |
| /** |
| * Test that the required access configs are set by session hook |
| */ |
| @Test |
| public void testAccessConfigRestrictions() throws Exception { |
| context = createContext(properties); |
| policyFile |
| .setUserGroupMapping(StaticUserGroup.getStaticMapping()) |
| .write(context.getPolicyFile()); |
| |
| String testUser = USER1_1; |
| // verify the config is set correctly by session hook |
| verifyConfig(testUser, ConfVars.SEMANTIC_ANALYZER_HOOK.varname, |
| HiveAuthzBindingSessionHook.SEMANTIC_HOOK); |
| verifyConfig(testUser, ConfVars.HIVE_CAPTURE_TRANSFORM_ENTITY.varname, |
| "true"); |
| verifyConfig(testUser, ConfVars.HIVE_SECURITY_COMMAND_WHITELIST.varname, "set"); |
| verifyConfig(testUser, ConfVars.SCRATCHDIRPERMISSION.varname, HiveAuthzBindingSessionHook.SCRATCH_DIR_PERMISSIONS); |
| verifyConfig(testUser, HiveConf.ConfVars.HIVE_CONF_RESTRICTED_LIST.varname, |
| HiveAuthzBindingSessionHook.ACCESS_RESTRICT_LIST); |
| verifyConfig(testUser, HiveAuthzConf.HIVE_ACCESS_SUBJECT_NAME, testUser); |
| } |
| |
| private void verifyConfig(String userName, String confVar, String expectedValue) throws Exception { |
| Connection connection = context.createConnection(userName); |
| Statement statement = context.createStatement(connection); |
| statement.execute("set " + confVar); |
| ResultSet res = statement.getResultSet(); |
| assertTrue(res.next()); |
| String configValue = res.getString(1); |
| assertNotNull(configValue); |
| String restrictListValues = (configValue.split("="))[1]; |
| assertFalse(restrictListValues.isEmpty()); |
| for (String restrictConfig: expectedValue.split(",")) { |
| assertTrue(restrictListValues.toLowerCase().contains(restrictConfig.toLowerCase())); |
| } |
| |
| } |
| |
| /** |
| * Test access to default DB with explicit privilege requirement |
| * Admin should be able to run use default with server level access |
| * User with db level access should be able to run use default |
| * User with table level access should be able to run use default |
| * User with no access to default db objects, should NOT be able run use default |
| * @throws Exception |
| */ |
| @Test |
| public void testDefaultDbRestrictivePrivilege() throws Exception { |
| properties.put(HiveAuthzConf.AuthzConfVars.AUTHZ_RESTRICT_DEFAULT_DB.getVar(), "true"); |
| context = createContext(properties); |
| |
| policyFile |
| .addRolesToGroup(USERGROUP1, "all_default") |
| .addRolesToGroup(USERGROUP2, "select_default") |
| .addRolesToGroup(USERGROUP3, "all_db1") |
| .addPermissionsToRole("all_default", "server=server1->db=default") |
| .addPermissionsToRole("select_default", "server=server1->db=default->table=tab_2->action=select") |
| .addPermissionsToRole("all_db1", "server=server1->db=DB_1") |
| .setUserGroupMapping(StaticUserGroup.getStaticMapping()) |
| .write(context.getPolicyFile()); |
| |
| Connection connection = context.createConnection(ADMIN1); |
| Statement statement = context.createStatement(connection); |
| statement.execute("use default"); |
| |
| connection = context.createConnection(USER1_1); |
| statement = context.createStatement(connection); |
| statement.execute("use default"); |
| |
| connection = context.createConnection(USER2_1); |
| statement = context.createStatement(connection); |
| statement.execute("use default"); |
| |
| connection = context.createConnection(USER3_1); |
| statement = context.createStatement(connection); |
| try { |
| // user3 doesn't have any implicit permission for default |
| statement.execute("use default"); |
| assertFalse("user3 shouldn't be able switch to default", true); |
| } catch (SQLException e) { |
| context.verifyAuthzException(e); |
| } |
| context.close(); |
| } |
| |
| } |