Repository: impala Updated Branches: refs/heads/master 4845f98be -> d7b8275ed
http://git-wip-us.apache.org/repos/asf/impala/blob/23f5338b/fe/src/test/resources/sentry-site_oo_nogrant.xml.template ---------------------------------------------------------------------- diff --git a/fe/src/test/resources/sentry-site_oo_nogrant.xml.template b/fe/src/test/resources/sentry-site_oo_nogrant.xml.template new file mode 100644 index 0000000..0407e87 --- /dev/null +++ b/fe/src/test/resources/sentry-site_oo_nogrant.xml.template @@ -0,0 +1,94 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/xsl" href="configuration.xsl"?> +<!-- + 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. +--> + +<configuration> + <property> + <name>sentry.service.security.mode</name> + <value>none</value> + </property> + <property> + <name>hive.sentry.server</name> + <value>server1</value> + </property> + <property> + <name>sentry.service.admin.group</name> + <value>${USER}</value> + </property> + <property> + <name>sentry.service.server.rpc-address</name> + <value>localhost</value> + </property> + <property> + <name>sentry.service.server.rpc-port</name> + <value>30911</value> + </property> + <property> + <name>sentry.service.client.server.rpc-address</name> + <value>localhost</value> + </property> + <property> + <!-- + TODO(IMPALA-5686): Remove this or the previous property when Sentry standardizes on one. + --> + <name>sentry.service.client.server.rpc-addresses</name> + <value>localhost</value> + </property> + <property> + <name>sentry.service.client.server.rpc-port</name> + <value>30911</value> + </property> + <property> + <name>sentry.store.jdbc.url</name> + <value>jdbc:postgresql://localhost:5432/${SENTRY_POLICY_DB}/;create=true</value> + </property> + <property> + <name>sentry.store.jdbc.user</name> + <value>hiveuser</value> + </property> + <property> + <name>sentry.store.jdbc.password</name> + <value>password</value> + </property> + <property> + <name>sentry.verify.schema.version</name> + <value>false</value> + </property> + <property> + <name>sentry.store.jdbc.driver</name> + <value>org.postgresql.Driver</value> + </property> + <!-- This property enables owner privilege without grant. --> + <property> + <name>sentry.db.policy.store.owner.as.privilege</name> + <value>all</value> + </property> + <!-- These properties enable HMS follower. --> + <property> + <name>sentry.service.processor.factories</name> + <value>org.apache.sentry.api.service.thrift.SentryPolicyStoreProcessorFactory,org.apache.sentry.hdfs.SentryHDFSServiceProcessorFactory</value> + </property> + <property> + <name>sentry.policy.store.plugins</name> + <value>org.apache.sentry.hdfs.SentryPlugin</value> + </property> + <property> + <name>sentry.hive.testing.mode</name> + <value>true</value> + </property> +</configuration> http://git-wip-us.apache.org/repos/asf/impala/blob/23f5338b/testdata/bin/run-sentry-service.sh ---------------------------------------------------------------------- diff --git a/testdata/bin/run-sentry-service.sh b/testdata/bin/run-sentry-service.sh index f49f88b..0373bea 100755 --- a/testdata/bin/run-sentry-service.sh +++ b/testdata/bin/run-sentry-service.sh @@ -23,7 +23,13 @@ setup_report_build_error . ${IMPALA_HOME}/bin/set-classpath.sh -SENTRY_SERVICE_CONFIG=${SENTRY_CONF_DIR}/sentry-site.xml +SENTRY_SERVICE_CONFIG=${SENTRY_SERVICE_CONFIG:-} + +if [ -z ${SENTRY_SERVICE_CONFIG} ] +then + SENTRY_SERVICE_CONFIG=${SENTRY_CONF_DIR}/sentry-site.xml +fi + LOGDIR="${IMPALA_CLUSTER_LOGS_DIR}"/sentry mkdir -p "${LOGDIR}" || true http://git-wip-us.apache.org/repos/asf/impala/blob/23f5338b/tests/authorization/test_grant_revoke.py ---------------------------------------------------------------------- diff --git a/tests/authorization/test_grant_revoke.py b/tests/authorization/test_grant_revoke.py index 36642fb..8973026 100644 --- a/tests/authorization/test_grant_revoke.py +++ b/tests/authorization/test_grant_revoke.py @@ -29,7 +29,8 @@ from tests.common.test_dimensions import create_uncompressed_text_dimension from tests.util.calculation_util import get_random_id from tests.verifiers.metric_verifier import MetricVerifier -SENTRY_CONFIG_FILE = getenv('IMPALA_HOME') + '/fe/src/test/resources/sentry-site.xml' +SENTRY_CONFIG_FILE = getenv('IMPALA_HOME') + \ + '/fe/src/test/resources/sentry-site.xml' class TestGrantRevoke(CustomClusterTestSuite, ImpalaTestSuite): @classmethod @@ -83,14 +84,16 @@ class TestGrantRevoke(CustomClusterTestSuite, ImpalaTestSuite): @pytest.mark.execute_serially @CustomClusterTestSuite.with_args( impalad_args="--server_name=server1", - catalogd_args="--sentry_config=" + SENTRY_CONFIG_FILE) + catalogd_args="--sentry_config=" + SENTRY_CONFIG_FILE, + sentry_config=SENTRY_CONFIG_FILE) def test_grant_revoke(self, vector): self.run_test_case('QueryTest/grant_revoke', vector, use_db="default") @pytest.mark.execute_serially @CustomClusterTestSuite.with_args( impalad_args="--server_name=server1", - catalogd_args="--sentry_config=" + SENTRY_CONFIG_FILE) + catalogd_args="--sentry_config=" + SENTRY_CONFIG_FILE, + sentry_config=SENTRY_CONFIG_FILE) def test_grant_revoke_kudu(self, vector): if getenv("KUDU_IS_SUPPORTED") == "false": pytest.skip("Kudu is not supported") @@ -100,7 +103,8 @@ class TestGrantRevoke(CustomClusterTestSuite, ImpalaTestSuite): @CustomClusterTestSuite.with_args( impalad_args="--server_name=server1", catalogd_args="--sentry_config=" + SENTRY_CONFIG_FILE + - " --sentry_catalog_polling_frequency_s=1") + " --sentry_catalog_polling_frequency_s=1", + sentry_config=SENTRY_CONFIG_FILE) def test_role_privilege_case(self, vector): """IMPALA-5582: Store sentry privileges in lower case. This test grants select privileges to roles assgined to tables/db @@ -164,7 +168,8 @@ class TestGrantRevoke(CustomClusterTestSuite, ImpalaTestSuite): @pytest.mark.execute_serially @CustomClusterTestSuite.with_args( impalad_args="--server_name=server1", - catalogd_args="--sentry_config=" + SENTRY_CONFIG_FILE) + catalogd_args="--sentry_config=" + SENTRY_CONFIG_FILE, + sentry_config=SENTRY_CONFIG_FILE) def test_role_update(self, vector): """IMPALA-5355: The initial update from the statestore has the privileges and roles in reverse order if a role was modified, but not the associated privilege. Verify that http://git-wip-us.apache.org/repos/asf/impala/blob/23f5338b/tests/authorization/test_owner_privileges.py ---------------------------------------------------------------------- diff --git a/tests/authorization/test_owner_privileges.py b/tests/authorization/test_owner_privileges.py new file mode 100644 index 0000000..2469c3a --- /dev/null +++ b/tests/authorization/test_owner_privileges.py @@ -0,0 +1,406 @@ +# 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. +# +# Client tests to ensure object ownership functionality +# +# These tests run with using the user 'root' since we need a user on the system that +# has a group that we can grant role to. The tests uses two clients, one for the root +# user and one for 'test_user'. test_user is used as a user to transfer ownership to +# but does not need a group for role grants. Additionally, in these tests we run all +# the tests twice. Once with just using cache, hence the long sentry poll, and once +# by ensuring refreshes happen from Sentry. + +import grp +import pytest +from getpass import getuser +from os import getenv +from time import sleep, time + +from tests.common.custom_cluster_test_suite import CustomClusterTestSuite +from tests.common.test_dimensions import create_uncompressed_text_dimension + +# The polling frequency used by catalogd to refresh Sentry privileges. +# The long polling is so we can check updates just for the cache. +# The other polling is so we can get the updates without having to wait. +SENTRY_LONG_POLLING_FREQUENCY_S = 60 +SENTRY_POLLING_FREQUENCY_S = 3 +# The timeout, in seconds, when waiting for a refresh of Sentry privileges. +SENTRY_REFRESH_TIMEOUT_S = 9 + +SENTRY_CONFIG_DIR = getenv('IMPALA_HOME') + '/fe/src/test/resources/' +SENTRY_BASE_LOG_DIR = getenv('IMPALA_CLUSTER_LOGS_DIR') + "/sentry" +SENTRY_CONFIG_FILE_OO = SENTRY_CONFIG_DIR + 'sentry-site_oo.xml' +SENTRY_CONFIG_FILE_OO_NOGRANT = SENTRY_CONFIG_DIR + 'sentry-site_oo_nogrant.xml' +SENTRY_CONFIG_FILE_NO_OO = SENTRY_CONFIG_DIR + 'sentry-site_no_oo.xml' + + +class TestObject(): + DATABASE = "database" + TABLE = "table" + VIEW = "view" + + def __init__(self, obj_type, obj_name, grant=False): + self.obj_name = obj_name + self.obj_type = obj_type + parts = obj_name.split(".") + self.db_name = parts[0] + self.table_name = None + self.table_def = "" + self.view_select = "" + if len(parts) > 1: + self.table_name = parts[1] + if obj_type == TestObject.VIEW: + self.grant_name = TestObject.TABLE + self.view_select = "as select * from functional.alltypes" + elif obj_type == TestObject.TABLE: + self.grant_name = TestObject.TABLE + self.table_def = "(col1 int)" + else: + self.grant_name = obj_type + self.grant = grant + + +class TestOwnerPrivileges(CustomClusterTestSuite): + @classmethod + def add_test_dimensions(cls): + super(TestOwnerPrivileges, cls).add_test_dimensions() + cls.ImpalaTestMatrix.add_dimension( + create_uncompressed_text_dimension(cls.get_workload())) + + def teardown_class(self): + super(self) + + def setup_method(self, method): + # Using user 'root' for these tests since that user and the group 'root' should + # exist on all systems running these tests which provides a group that can be + # granted to a role. + super(TestOwnerPrivileges, self).setup_method(method) + self._test_cleanup() + # Base roles for enabling tests. + self.execute_query("create role owner_priv_test_ROOT") + # Role for verifying grant. + self.execute_query("create role owner_priv_test_all_role") + # Role for verifying transfer to role. + self.execute_query("create role owner_priv_test_owner_role") + self.execute_query("grant role owner_priv_test_ROOT to group root") + self.execute_query("grant role owner_priv_test_owner_role to group root") + self.execute_query("grant create on server to owner_priv_test_ROOT") + self.execute_query("grant select on database functional to owner_priv_test_ROOT") + + def teardown_method(self, method): + self._test_cleanup() + self.execute_query("drop role owner_priv_admin") + super(TestOwnerPrivileges, self).teardown_method(method) + + def _test_cleanup(self): + # Admin for manipulation and cleaning up. + try: + self.execute_query("drop role owner_priv_admin") + except Exception: + # Ignore in case it wasn't created yet. + pass + self.execute_query("create role owner_priv_admin") + self.execute_query("grant all on server to owner_priv_admin with grant option") + group_name = grp.getgrnam(getuser()).gr_name + self.execute_query("grant role owner_priv_admin to group `%s`" % group_name) + # Clean up the test artifacts. + try: + self.cleanup_db("owner_priv_db", sync_ddl=0) + except Exception: + # Ignore this if we can't show tables. + pass + + # Clean up any old roles created by this test + for role_name in self.execute_query("show roles").data: + if "owner_priv_test" in role_name: + self.execute_query("drop role %s" % role_name) + + @pytest.mark.execute_serially + @CustomClusterTestSuite.with_args( + impalad_args="--server_name=server1 --sentry_config=" + SENTRY_CONFIG_FILE_OO, + catalogd_args="--sentry_config=" + SENTRY_CONFIG_FILE_OO + + " --sentry_catalog_polling_frequency_s=" + str(SENTRY_LONG_POLLING_FREQUENCY_S), + sentry_config=SENTRY_CONFIG_FILE_OO) + def test_owner_privileges_with_grant_long_poll(self, vector, unique_database): + self.__execute_owner_privilege_tests(TestObject(TestObject.DATABASE, "owner_priv_db", + grant=True)) + self.__execute_owner_privilege_tests(TestObject(TestObject.TABLE, + unique_database + ".owner_priv_tbl", grant=True)) + self.__execute_owner_privilege_tests(TestObject(TestObject.VIEW, + unique_database + ".owner_priv_view", grant=True)) + + @pytest.mark.execute_serially + @CustomClusterTestSuite.with_args( + impalad_args="--server_name=server1 --sentry_config=" + SENTRY_CONFIG_FILE_OO, + catalogd_args="--sentry_config=" + SENTRY_CONFIG_FILE_OO + + " --sentry_catalog_polling_frequency_s=" + str(SENTRY_POLLING_FREQUENCY_S), + sentry_config=SENTRY_CONFIG_FILE_OO) + def test_owner_privileges_with_grant(self, vector, unique_database): + self.__execute_owner_privilege_tests(TestObject(TestObject.DATABASE, "owner_priv_db", + grant=True), sentry_refresh_timeout_sec=SENTRY_REFRESH_TIMEOUT_S) + self.__execute_owner_privilege_tests(TestObject(TestObject.TABLE, + unique_database + ".owner_priv_tbl", grant=True), + sentry_refresh_timeout_sec=SENTRY_REFRESH_TIMEOUT_S) + self.__execute_owner_privilege_tests(TestObject(TestObject.VIEW, + unique_database + ".owner_priv_view", grant=True), + sentry_refresh_timeout_sec=SENTRY_REFRESH_TIMEOUT_S) + + def __execute_owner_privilege_tests(self, test_obj, sentry_refresh_timeout_sec=0): + """ + Executes all the statements required to validate owner privileges work correctly + for a specific database, table, or view. + """ + # Create object and ensure root gets owner privileges. + self.root_impalad_client = self.create_impala_client() + self.test_user_impalad_client = self.create_impala_client() + self.__root_query("create %s if not exists %s %s %s" % (test_obj.obj_type, + test_obj.obj_name, test_obj.table_def, test_obj.view_select)) + self.__validate_privileges(self.root_impalad_client, "root", "show grant user root", + test_obj, sentry_refresh_timeout_sec) + + # Ensure grant works. + self.__root_query("grant all on %s %s to role owner_priv_test_all_role" + % (test_obj.grant_name, test_obj.obj_name)) + self.__root_query("revoke all on %s %s from role owner_priv_test_all_role" + % (test_obj.grant_name, test_obj.obj_name)) + + # Change the database owner and ensure root does not have owner privileges. + self.__root_query("alter %s %s set owner user test_user" % (test_obj.obj_type, + test_obj.obj_name)) + result = self.__root_query("show grant user root") + assert len(result.data) == 0 + + # Ensure root cannot drop database after owner change. + self.__root_query_fail("drop %s %s" % (test_obj.obj_type, test_obj.obj_name), + "does not have privileges to execute 'DROP'") + + # test_user should have privileges for object now. + self.__validate_privileges(self.test_user_impalad_client, "test_user", + "show grant user test_user", test_obj, sentry_refresh_timeout_sec) + + # Change the owner to a role and ensure test_user doesn't have privileges. + # Set the owner back to root since for views, test_user doesn't have select + # privileges on the underlying table. + self.execute_query("alter %s %s set owner user root" % (test_obj.obj_type, + test_obj.obj_name)) + result = self.__test_user_query("show grant user test_user") + assert len(result.data) == 0 + self.__root_query("alter %s %s set owner role owner_priv_test_owner_role" + % (test_obj.obj_type, test_obj.obj_name)) + # Ensure root does not have user privileges. + result = self.__root_query("show grant user root") + assert len(result.data) == 0 + + # Ensure role has owner privileges. + self.__validate_privileges(self.root_impalad_client, "root", + "show grant role owner_priv_test_owner_role", test_obj, + sentry_refresh_timeout_sec) + + # Drop the object and ensure no role privileges. + self.__root_query("drop %s %s " % (test_obj.obj_type, test_obj.obj_name)) + result = self.__root_query("show grant role owner_priv_test_owner_role") + assert len(result.data) == 0 + + # Ensure user privileges are gone after drop. + self.__root_query("create %s if not exists %s %s %s" % (test_obj.obj_type, + test_obj.obj_name, test_obj.table_def, test_obj.view_select)) + self.__root_query("drop %s %s " % (test_obj.obj_type, test_obj.obj_name)) + result = self.__root_query("show grant user root") + assert len(result.data) == 0 + + def __str_to_bool(self, val): + if val.lower() == 'true': + return True + return False + + def __check_privileges(self, result, test_obj, null_create_date=True): + # results should have the following columns + # scope, database, table, column, uri, privilege, grant_option, create_time + for row in result.data: + col = row.split('\t') + assert col[0] == test_obj.grant_name + assert col[1] == test_obj.db_name + if test_obj.table_name is not None and len(test_obj.table_name) > 0: + assert col[2] == test_obj.table_name + assert self.__str_to_bool(col[6]) == test_obj.grant + if not null_create_date: + assert str(col[7]) != 'NULL' + + def __root_query(self, query): + return self.execute_query_expect_success(self.root_impalad_client, query, user="root") + + def __root_query_fail(self, query, error_msg): + e = self.execute_query_expect_failure(self.root_impalad_client, query, user="root") + self.__verify_exceptions(error_msg, str(e)) + + def __validate_privileges(self, client, user, query, test_obj, timeout_sec): + """Validate privileges. If timeout_sec is > 0 then retry until create_date is not null + or the timeout_sec is reached. + """ + if timeout_sec is None or timeout_sec <= 0: + self.__check_privileges(self.execute_query_expect_success(client, query, + user=user), test_obj) + else: + start_time = time() + while time() - start_time < timeout_sec: + result = self.execute_query_expect_success(client, query, user=user) + try: + self.__check_privileges(result, test_obj, null_create_date=False) + return True + except Exception: + pass + sleep(1) + return False + + def __test_user_query(self, query): + return self.execute_query_expect_success(self.test_user_impalad_client, query, + user="test_user") + + @pytest.mark.execute_serially + @CustomClusterTestSuite.with_args( + impalad_args="--server_name=server1 --sentry_config=" + SENTRY_CONFIG_FILE_NO_OO, + catalogd_args="--sentry_config=" + SENTRY_CONFIG_FILE_NO_OO + + " --sentry_catalog_polling_frequency_s=" + str(SENTRY_LONG_POLLING_FREQUENCY_S), + sentry_config=SENTRY_CONFIG_FILE_NO_OO) + def test_owner_privileges_disabled_long_poll(self, vector, unique_database): + self.__execute_owner_privilege_tests_no_oo(TestObject(TestObject.DATABASE, + "owner_priv_db")) + self.__execute_owner_privilege_tests_no_oo(TestObject(TestObject.TABLE, + unique_database + ".owner_priv_tbl")) + self.__execute_owner_privilege_tests_no_oo(TestObject(TestObject.VIEW, + unique_database + ".owner_priv_view")) + + @pytest.mark.execute_serially + @CustomClusterTestSuite.with_args( + impalad_args="--server_name=server1 --sentry_config=" + SENTRY_CONFIG_FILE_NO_OO, + catalogd_args="--sentry_config=" + SENTRY_CONFIG_FILE_NO_OO + + " --sentry_catalog_polling_frequency_s=" + str(SENTRY_POLLING_FREQUENCY_S), + sentry_config=SENTRY_CONFIG_FILE_NO_OO) + def test_owner_privileges_disabled(self, vector, unique_database): + self.__execute_owner_privilege_tests_no_oo(TestObject(TestObject.DATABASE, + "owner_priv_db"), sentry_refresh_timeout_sec=SENTRY_REFRESH_TIMEOUT_S) + self.__execute_owner_privilege_tests_no_oo(TestObject(TestObject.TABLE, + unique_database + ".owner_priv_tbl"), + sentry_refresh_timeout_sec=SENTRY_REFRESH_TIMEOUT_S) + self.__execute_owner_privilege_tests_no_oo(TestObject(TestObject.VIEW, + unique_database + ".owner_priv_view"), + sentry_refresh_timeout_sec=SENTRY_REFRESH_TIMEOUT_S) + + def __execute_owner_privilege_tests_no_oo(self, test_obj, sentry_refresh_timeout_sec=0): + """ + Executes all the statements required to validate owner privileges work correctly + for a specific database, table, or view. + """ + # Create object and ensure root gets owner privileges. + self.root_impalad_client = self.create_impala_client() + self.test_user_impalad_client = self.create_impala_client() + self.__root_query("create %s if not exists %s %s %s" % (test_obj.obj_type, + test_obj.obj_name, test_obj.table_def, test_obj.view_select)) + # For user privileges, if the user has no privileges, the user will not exist + # in the privileges list. + self.__root_query_fail("show grant user root", "User 'root' does not exist") + + # Ensure grant doesn't work. + self.__root_query_fail("grant all on %s %s to role owner_priv_test_all_role" + % (test_obj.grant_name, test_obj.obj_name), + "does not have privileges to execute: GRANT_PRIVILEGE") + + self.__root_query_fail("revoke all on %s %s from role owner_priv_test_all_role" + % (test_obj.grant_name, test_obj.obj_name), + "does not have privileges to execute: REVOKE_PRIVILEGE") + + # Ensure changing the database owner doesn't work. + self.__root_query_fail("alter %s %s set owner user test_user" + % (test_obj.obj_type, test_obj.obj_name), + "does not have privileges with 'GRANT OPTION'") + + # Ensure root cannot drop database. + self.__root_query_fail("drop %s %s" % (test_obj.obj_type, test_obj.obj_name), + "does not have privileges to execute 'DROP'") + + @pytest.mark.execute_serially + @CustomClusterTestSuite.with_args( + impalad_args="--server_name=server1 --sentry_config=" + SENTRY_CONFIG_FILE_OO_NOGRANT, + catalogd_args="--sentry_config=" + SENTRY_CONFIG_FILE_OO_NOGRANT + + " --sentry_catalog_polling_frequency_s=" + str(SENTRY_LONG_POLLING_FREQUENCY_S), + sentry_config=SENTRY_CONFIG_FILE_OO_NOGRANT) + def test_owner_privileges_without_grant_long_poll(self, vector, unique_database): + self.__execute_owner_privilege_tests_oo_nogrant(TestObject(TestObject.DATABASE, + "owner_priv_db")) + self.__execute_owner_privilege_tests_oo_nogrant(TestObject(TestObject.TABLE, + unique_database + ".owner_priv_tbl")) + self.__execute_owner_privilege_tests_oo_nogrant(TestObject(TestObject.VIEW, + unique_database + ".owner_priv_view")) + + @pytest.mark.execute_serially + @CustomClusterTestSuite.with_args( + impalad_args="--server_name=server1 --sentry_config=" + SENTRY_CONFIG_FILE_OO_NOGRANT, + catalogd_args="--sentry_config=" + SENTRY_CONFIG_FILE_OO_NOGRANT + + " --sentry_catalog_polling_frequency_s=" + str(SENTRY_POLLING_FREQUENCY_S), + sentry_config=SENTRY_CONFIG_FILE_OO_NOGRANT) + def test_owner_privileges_without_grant(self, vector, unique_database): + self.__execute_owner_privilege_tests_oo_nogrant(TestObject(TestObject.DATABASE, + "owner_priv_db"), sentry_refresh_timeout_sec=SENTRY_REFRESH_TIMEOUT_S) + self.__execute_owner_privilege_tests_oo_nogrant(TestObject(TestObject.TABLE, + unique_database + ".owner_priv_tbl"), + sentry_refresh_timeout_sec=SENTRY_REFRESH_TIMEOUT_S) + self.__execute_owner_privilege_tests_oo_nogrant(TestObject(TestObject.VIEW, + unique_database + ".owner_priv_view"), + sentry_refresh_timeout_sec=SENTRY_REFRESH_TIMEOUT_S) + + def __execute_owner_privilege_tests_oo_nogrant(self, test_obj, + sentry_refresh_timeout_sec=0): + """ + Executes all the statements required to validate owner privileges work correctly + for a specific database, table, or view. + """ + # Create object and ensure root gets owner privileges. + self.root_impalad_client = self.create_impala_client() + self.test_user_impalad_client = self.create_impala_client() + self.__root_query("create %s if not exists %s %s %s" % (test_obj.obj_type, + test_obj.obj_name, test_obj.table_def, test_obj.view_select)) + self.__validate_privileges(self.root_impalad_client, "root", "show grant user root", + test_obj, sentry_refresh_timeout_sec) + + # Ensure grant doesn't work. + self.__root_query_fail("grant all on %s %s to role owner_priv_test_all_role" + % (test_obj.grant_name, test_obj.obj_name), + "does not have privileges to execute: GRANT_PRIVILEGE") + + self.__root_query_fail("revoke all on %s %s from role owner_priv_test_all_role" + % (test_obj.grant_name, test_obj.obj_name), + "does not have privileges to execute: REVOKE_PRIVILEGE") + + self.__root_query_fail("alter %s %s set owner user test_user" + % (test_obj.obj_type, test_obj.obj_name), + "does not have privileges with 'GRANT OPTION'") + + self.__root_query("drop %s %s " % (test_obj.obj_type, test_obj.obj_name)) + result = self.__root_query("show grant user root") + assert len(result.data) == 0 + + def __verify_exceptions(self, expected_str, actual_str): + """ + Verifies that 'expected_str' is a substring of the actual exception string + 'actual_str'. + """ + actual_str = actual_str.replace('\n', '') + # Strip newlines so we can split error message into multiple lines + expected_str = expected_str.replace('\n', '') + if expected_str in actual_str: return + assert False, 'Unexpected exception string. Expected: %s\nNot found in actual: %s' % \ + (expected_str, actual_str) http://git-wip-us.apache.org/repos/asf/impala/blob/23f5338b/tests/common/custom_cluster_test_suite.py ---------------------------------------------------------------------- diff --git a/tests/common/custom_cluster_test_suite.py b/tests/common/custom_cluster_test_suite.py index 88b3875..cf4654a 100644 --- a/tests/common/custom_cluster_test_suite.py +++ b/tests/common/custom_cluster_test_suite.py @@ -24,6 +24,7 @@ import os.path import pipes import pytest import re +import subprocess from subprocess import check_call from tests.common.impala_test_suite import ImpalaTestSuite from tests.common.impala_cluster import ImpalaCluster @@ -40,6 +41,7 @@ STATESTORED_ARGS = 'state_store_args' CATALOGD_ARGS = 'catalogd_args' # Additional args passed to the start-impala-cluster script. START_ARGS = 'start_args' +SENTRY_CONFIG = 'sentry_config' # Run with fast topic updates by default to reduce time to first query running. DEFAULT_STATESTORE_ARGS = '--statestore_update_frequency_ms=50 \ @@ -89,7 +91,8 @@ class CustomClusterTestSuite(ImpalaTestSuite): pass @staticmethod - def with_args(impalad_args=None, statestored_args=None, catalogd_args=None, start_args=None): + def with_args(impalad_args=None, statestored_args=None, catalogd_args=None, + start_args=None, sentry_config=None): """Records arguments to be passed to a cluster by adding them to the decorated method's func_dict""" def decorate(func): @@ -104,6 +107,8 @@ class CustomClusterTestSuite(ImpalaTestSuite): func.func_dict[CATALOGD_ARGS] = catalogd_args if start_args is not None: func.func_dict[START_ARGS] = start_args + if sentry_config is not None: + func.func_dict[SENTRY_CONFIG] = sentry_config return func return decorate @@ -115,6 +120,8 @@ class CustomClusterTestSuite(ImpalaTestSuite): if START_ARGS in method.func_dict: cluster_args.append(method.func_dict[START_ARGS]) + if SENTRY_CONFIG in method.func_dict: + self._start_sentry_service(method.func_dict[SENTRY_CONFIG]) # Start a clean new cluster before each test self._start_impala_cluster(cluster_args) super(CustomClusterTestSuite, self).setup_class() @@ -131,6 +138,21 @@ class CustomClusterTestSuite(ImpalaTestSuite): sleep(2) @classmethod + def _start_sentry_service(cls, sentry_service_config): + sentry_env = dict(os.environ) + sentry_env['SENTRY_SERVICE_CONFIG'] = sentry_service_config + call = subprocess.Popen( + [os.path.join(IMPALA_HOME, 'testdata/bin/run-sentry-service.sh')], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + stdin=file("/dev/null"), + env=sentry_env) + (stdout, stderr) = call.communicate() + call.wait() + if call.returncode != 0: + raise RuntimeError("unable to start sentry") + + @classmethod def _start_impala_cluster(cls, options, log_dir=os.getenv('LOG_DIR', "/tmp/"), cluster_size=CLUSTER_SIZE, num_coordinators=NUM_COORDINATORS, use_exclusive_coordinators=False, log_level=1, expected_num_executors=CLUSTER_SIZE): http://git-wip-us.apache.org/repos/asf/impala/blob/23f5338b/tests/common/impala_test_suite.py ---------------------------------------------------------------------- diff --git a/tests/common/impala_test_suite.py b/tests/common/impala_test_suite.py index 48a53f2..7d34fdf 100644 --- a/tests/common/impala_test_suite.py +++ b/tests/common/impala_test_suite.py @@ -533,18 +533,20 @@ class ImpalaTestSuite(BaseTestSuite): @classmethod @execute_wrapper - def execute_query_expect_success(cls, impalad_client, query, query_options=None): + def execute_query_expect_success(cls, impalad_client, query, query_options=None, + user=None): """Executes a query and asserts if the query fails""" - result = cls.__execute_query(impalad_client, query, query_options) + result = cls.__execute_query(impalad_client, query, query_options, user) assert result.success return result @execute_wrapper - def execute_query_expect_failure(self, impalad_client, query, query_options=None): + def execute_query_expect_failure(self, impalad_client, query, query_options=None, + user=None): """Executes a query and asserts if the query succeeds""" result = None try: - result = self.__execute_query(impalad_client, query, query_options) + result = self.__execute_query(impalad_client, query, query_options, user) except Exception, e: return e