http://git-wip-us.apache.org/repos/asf/impala/blob/011acd07/fe/src/test/java/org/apache/impala/analysis/AuthorizationStmtTest.java ---------------------------------------------------------------------- diff --git a/fe/src/test/java/org/apache/impala/analysis/AuthorizationStmtTest.java b/fe/src/test/java/org/apache/impala/analysis/AuthorizationStmtTest.java new file mode 100644 index 0000000..1d4c593 --- /dev/null +++ b/fe/src/test/java/org/apache/impala/analysis/AuthorizationStmtTest.java @@ -0,0 +1,1935 @@ +// 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.impala.analysis; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Sets; +import org.apache.commons.lang.ArrayUtils; +import org.apache.impala.analysis.AnalysisContext.AnalysisResult; +import org.apache.impala.authorization.AuthorizationConfig; +import org.apache.impala.authorization.PrivilegeRequest; +import org.apache.impala.authorization.User; +import org.apache.impala.catalog.AuthorizationException; +import org.apache.impala.catalog.Role; +import org.apache.impala.catalog.RolePrivilege; +import org.apache.impala.catalog.ScalarFunction; +import org.apache.impala.catalog.Type; +import org.apache.impala.common.FrontendTestBase; +import org.apache.impala.common.ImpalaException; +import org.apache.impala.common.RuntimeEnv; +import org.apache.impala.service.Frontend; +import org.apache.impala.testutil.ImpaladTestCatalog; +import org.apache.impala.thrift.TColumnValue; +import org.apache.impala.thrift.TDescribeOutputStyle; +import org.apache.impala.thrift.TDescribeResult; +import org.apache.impala.thrift.TFunctionBinaryType; +import org.apache.impala.thrift.TPrivilege; +import org.apache.impala.thrift.TPrivilegeLevel; +import org.apache.impala.thrift.TPrivilegeScope; +import org.apache.impala.thrift.TResultRow; +import org.apache.impala.thrift.TTableName; +import org.apache.impala.util.SentryPolicyService; +import org.apache.sentry.provider.db.service.thrift.TSentryRole; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** + * This class contains authorization tests for SQL statements. + */ +public class AuthorizationStmtTest extends FrontendTestBase { + private static final String SENTRY_SERVER = "server1"; + private final static User USER = new User(System.getProperty("user.name")); + private final AnalysisContext analysisContext_; + private final SentryPolicyService sentryService_; + private final ImpaladTestCatalog authzCatalog_; + private final Frontend authzFrontend_; + + public AuthorizationStmtTest() { + AuthorizationConfig authzConfig = AuthorizationConfig.createHadoopGroupAuthConfig( + SENTRY_SERVER, null, System.getenv("IMPALA_HOME") + + "/fe/src/test/resources/sentry-site.xml"); + authzConfig.validateConfig(); + analysisContext_ = createAnalysisCtx(authzConfig, USER.getName()); + authzCatalog_ = new ImpaladTestCatalog(authzConfig); + authzFrontend_ = new Frontend(authzConfig, authzCatalog_); + sentryService_ = new SentryPolicyService(authzConfig.getSentryConfig()); + } + + @BeforeClass + public static void setUp() { + RuntimeEnv.INSTANCE.setTestEnv(true); + } + + @AfterClass + public static void cleanUp() { + RuntimeEnv.INSTANCE.reset(); + } + + @Before + public void before() throws ImpalaException { + // Remove existing roles in order to not interfere with these tests. + for (TSentryRole role: sentryService_.listAllRoles(USER)) { + authzCatalog_.removeRole(role.getRoleName()); + } + } + + private static final String[] ALLTYPES_COLUMNS_WITHOUT_ID = new String[]{"bool_col", + "tinyint_col", "smallint_col", "int_col", "bigint_col", "float_col", "double_col", + "date_string_col", "string_col", "timestamp_col", "year", "month"}; + + private static final String[] ALLTYPES_COLUMNS = (String[]) ArrayUtils.addAll( + new String[]{"id"}, ALLTYPES_COLUMNS_WITHOUT_ID); + + @Test + public void testPrivilegeRequests() throws ImpalaException { + // Used for select *, with, and union + Set<String> expectedAuthorizables = Sets.newHashSet( + "functional.alltypes", + "functional.alltypes.id", + "functional.alltypes.bool_col", + "functional.alltypes.tinyint_col", + "functional.alltypes.smallint_col", + "functional.alltypes.int_col", + "functional.alltypes.bigint_col", + "functional.alltypes.float_col", + "functional.alltypes.double_col", + "functional.alltypes.date_string_col", + "functional.alltypes.string_col", + "functional.alltypes.timestamp_col", + "functional.alltypes.year", + "functional.alltypes.month" + ); + // Select * + verifyPrivilegeReqs("select * from functional.alltypes", expectedAuthorizables); + verifyPrivilegeReqs("select alltypes.* from functional.alltypes", + expectedAuthorizables); + verifyPrivilegeReqs(createAnalysisCtx("functional"), "select * from alltypes", + expectedAuthorizables); + verifyPrivilegeReqs(createAnalysisCtx("functional"), + "select alltypes.* from alltypes", expectedAuthorizables); + verifyPrivilegeReqs("select a.* from functional.alltypes a", expectedAuthorizables); + + // With clause. + verifyPrivilegeReqs("with t as (select * from functional.alltypes) select * from t", + expectedAuthorizables); + verifyPrivilegeReqs(createAnalysisCtx("functional"), + "with t as (select * from alltypes) select * from t", expectedAuthorizables); + + // Union. + verifyPrivilegeReqs("select * from functional.alltypes union all " + + "select * from functional.alltypes", expectedAuthorizables); + verifyPrivilegeReqs(createAnalysisCtx("functional"), + "select * from alltypes union all select * from alltypes", + expectedAuthorizables); + + // Describe + expectedAuthorizables = Sets.newHashSet("functional.alltypes.*"); + verifyPrivilegeReqs("describe functional.alltypes", expectedAuthorizables); + verifyPrivilegeReqs(createAnalysisCtx("functional"), "describe alltypes", + expectedAuthorizables); + + // Select a specific column. + expectedAuthorizables = Sets.newHashSet( + "functional.alltypes", + "functional.alltypes.id" + ); + verifyPrivilegeReqs("select id from functional.alltypes", expectedAuthorizables); + verifyPrivilegeReqs("select alltypes.id from functional.alltypes", + expectedAuthorizables); + verifyPrivilegeReqs(createAnalysisCtx("functional"), + "select alltypes.id from alltypes", expectedAuthorizables); + verifyPrivilegeReqs(createAnalysisCtx("functional"), "select id from alltypes", + expectedAuthorizables); + verifyPrivilegeReqs("select alltypes.id from functional.alltypes", + expectedAuthorizables); + verifyPrivilegeReqs("select a.id from functional.alltypes a", expectedAuthorizables); + + // Insert. + expectedAuthorizables = Sets.newHashSet("functional.alltypes"); + verifyPrivilegeReqs("insert into functional.alltypes(id) partition(month, year) " + + "values(1, 1, 2018)", expectedAuthorizables); + verifyPrivilegeReqs(createAnalysisCtx("functional"), "insert into alltypes(id) " + + "partition(month, year) values(1, 1, 2018)", expectedAuthorizables); + + // Insert with constant select. + expectedAuthorizables = Sets.newHashSet("functional.zipcode_incomes"); + verifyPrivilegeReqs("insert into functional.zipcode_incomes(id) select '123'", + expectedAuthorizables); + verifyPrivilegeReqs(createAnalysisCtx("functional"), + "insert into zipcode_incomes(id) select '123'", expectedAuthorizables); + + // Truncate. + expectedAuthorizables = Sets.newHashSet("functional.alltypes"); + verifyPrivilegeReqs("truncate table functional.alltypes", expectedAuthorizables); + verifyPrivilegeReqs(createAnalysisCtx("functional"), + "truncate table alltypes", expectedAuthorizables); + + + // Load + expectedAuthorizables = Sets.newHashSet( + "functional.alltypes", + "hdfs://localhost:20500/test-warehouse/tpch.lineitem" + ); + verifyPrivilegeReqs("load data inpath " + + "'hdfs://localhost:20500/test-warehouse/tpch.lineitem' " + + "into table functional.alltypes partition(month=10, year=2009)", + expectedAuthorizables); + verifyPrivilegeReqs(createAnalysisCtx("functional"), "load data inpath " + + "'hdfs://localhost:20500/test-warehouse/tpch.lineitem' " + + "into table alltypes partition(month=10, year=2009)", + expectedAuthorizables); + + // Reset metadata. + expectedAuthorizables = Sets.newHashSet("functional.alltypes"); + verifyPrivilegeReqs("invalidate metadata functional.alltypes", expectedAuthorizables); + verifyPrivilegeReqs(createAnalysisCtx("functional"), "invalidate metadata alltypes", + expectedAuthorizables); + verifyPrivilegeReqs("refresh functional.alltypes", expectedAuthorizables); + verifyPrivilegeReqs(createAnalysisCtx("functional"), "refresh alltypes", + expectedAuthorizables); + + // Show tables. + expectedAuthorizables = Sets.newHashSet("functional.*.*"); + verifyPrivilegeReqs("show tables in functional", expectedAuthorizables); + verifyPrivilegeReqs(createAnalysisCtx("functional"), "show tables", + expectedAuthorizables); + + // Show partitions. + expectedAuthorizables = Sets.newHashSet("functional.alltypes"); + verifyPrivilegeReqs("show partitions functional.alltypes", expectedAuthorizables); + verifyPrivilegeReqs(createAnalysisCtx("functional"), "show partitions alltypes", + expectedAuthorizables); + + // Show range partitions. + expectedAuthorizables = Sets.newHashSet("functional_kudu.dimtbl"); + verifyPrivilegeReqs("show range partitions functional_kudu.dimtbl", + expectedAuthorizables); + verifyPrivilegeReqs(createAnalysisCtx("functional_kudu"), + "show range partitions dimtbl", expectedAuthorizables); + + // Show table stats. + expectedAuthorizables = Sets.newHashSet("functional.alltypes"); + verifyPrivilegeReqs("show table stats functional.alltypes", + expectedAuthorizables); + verifyPrivilegeReqs(createAnalysisCtx("functional"), "show table stats alltypes", + expectedAuthorizables); + + // Show column stats. + expectedAuthorizables = Sets.newHashSet("functional.alltypes"); + verifyPrivilegeReqs("show column stats functional.alltypes", expectedAuthorizables); + verifyPrivilegeReqs(createAnalysisCtx("functional"), "show column stats alltypes", + expectedAuthorizables); + + // Show create table. + expectedAuthorizables = Sets.newHashSet("functional.alltypes"); + verifyPrivilegeReqs("show create table functional.alltypes", expectedAuthorizables); + verifyPrivilegeReqs(createAnalysisCtx("functional"), + "show create table functional.alltypes", expectedAuthorizables); + + // Show create view. + expectedAuthorizables = Sets.newHashSet("functional.alltypes_view"); + verifyPrivilegeReqs("show create view functional.alltypes_view", + expectedAuthorizables); + verifyPrivilegeReqs(createAnalysisCtx("functional"), + "show create view functional.alltypes_view", expectedAuthorizables); + + // Compute stats. + expectedAuthorizables = Sets.newHashSet("functional.alltypes"); + verifyPrivilegeReqs("compute stats functional.alltypes", expectedAuthorizables); + verifyPrivilegeReqs(createAnalysisCtx("functional"), "compute stats alltypes", + expectedAuthorizables); + + // Drop stats. + expectedAuthorizables = Sets.newHashSet("functional.alltypes"); + verifyPrivilegeReqs("drop stats functional.alltypes", expectedAuthorizables); + verifyPrivilegeReqs(createAnalysisCtx("functional"), "drop stats alltypes", + expectedAuthorizables); + + // Create table. + expectedAuthorizables = Sets.newHashSet("functional.new_table"); + verifyPrivilegeReqs("create table functional.new_table(i int)", + expectedAuthorizables); + verifyPrivilegeReqs(createAnalysisCtx("functional"), "create table new_table(i int)", + expectedAuthorizables); + + // Create view. + expectedAuthorizables = Sets.newHashSet("functional.new_view"); + verifyPrivilegeReqs("create view functional.new_view as select 1", + expectedAuthorizables); + verifyPrivilegeReqs(createAnalysisCtx("functional"), + "create view new_view as select 1", expectedAuthorizables); + + // Drop table. + expectedAuthorizables = Sets.newHashSet("functional.alltypes"); + verifyPrivilegeReqs("drop table functional.alltypes", expectedAuthorizables); + verifyPrivilegeReqs(createAnalysisCtx("functional"), "drop table alltypes", + expectedAuthorizables); + + // Drop view. + expectedAuthorizables = Sets.newHashSet("functional.alltypes_view"); + verifyPrivilegeReqs("drop view functional.alltypes_view", expectedAuthorizables); + verifyPrivilegeReqs(createAnalysisCtx("functional"), "drop view alltypes_view", + expectedAuthorizables); + } + + @Test + public void testSelect() throws ImpalaException { + for (AuthzTest authzTest: new AuthzTest[]{ + // Select a specific column on a table. + authorize("select id from functional.alltypes"), + // With clause with select. + authorize("with t as (select id from functional.alltypes) select * from t")}) { + authzTest.ok(onServer(TPrivilegeLevel.ALL)) + .ok(onServer(TPrivilegeLevel.ALL)) + .ok(onServer(TPrivilegeLevel.SELECT)) + .ok(onDatabase("functional", TPrivilegeLevel.ALL)) + .ok(onDatabase("functional", TPrivilegeLevel.SELECT)) + .ok(onTable("functional", "alltypes", TPrivilegeLevel.ALL)) + .ok(onTable("functional", "alltypes", TPrivilegeLevel.SELECT)) + .ok(onColumn("functional", "alltypes", "id", TPrivilegeLevel.SELECT)) + .error(selectError("functional.alltypes")) + .error(selectError("functional.alltypes"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))) + .error(selectError("functional.alltypes"), onDatabase("functional", + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))) + .error(selectError("functional.alltypes"), onTable("functional", + "alltypes", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))); + } + + + // Select without referencing a column. + authorize("select 1 from functional.alltypes") + .ok(onServer(TPrivilegeLevel.ALL)) + .ok(onServer(TPrivilegeLevel.SELECT)) + .ok(onDatabase("functional", TPrivilegeLevel.ALL)) + .ok(onDatabase("functional", TPrivilegeLevel.SELECT)) + .ok(onTable("functional", "alltypes", TPrivilegeLevel.ALL)) + .ok(onTable("functional", "alltypes", TPrivilegeLevel.SELECT)) + .error(selectError("functional.alltypes")) + .error(selectError("functional.alltypes"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))) + .error(selectError("functional.alltypes"), onDatabase("functional", + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))) + .error(selectError("functional.alltypes"), onTable("functional", "alltypes", + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))); + + + // Select a specific column on a view. + // Column-level privileges on views are not currently supported. + authorize("select id from functional.alltypes_view") + .ok(onServer(TPrivilegeLevel.ALL)) + .ok(onServer(TPrivilegeLevel.SELECT)) + .ok(onDatabase("functional", TPrivilegeLevel.ALL)) + .ok(onDatabase("functional", TPrivilegeLevel.SELECT)) + .ok(onTable("functional", "alltypes_view", TPrivilegeLevel.ALL)) + .ok(onTable("functional", "alltypes_view", TPrivilegeLevel.SELECT)) + .error(selectError("functional.alltypes_view")) + .error(selectError("functional.alltypes_view"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))) + .error(selectError("functional.alltypes_view"), onDatabase("functional", + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))) + .error(selectError("functional.alltypes_view"), onTable("functional", + "alltypes_view", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))); + + // Constant select. + authorize("select 1").ok(); + + // Select on view and join table. + authorize("select a.id from functional.view_view a " + + "join functional.alltypesagg b ON (a.id = b.id)") + .ok(onServer(TPrivilegeLevel.ALL)) + .ok(onServer(TPrivilegeLevel.SELECT)) + .ok(onDatabase("functional", TPrivilegeLevel.ALL)) + .ok(onDatabase("functional", TPrivilegeLevel.SELECT)) + .ok(onTable("functional", "view_view", TPrivilegeLevel.ALL), + onTable("functional", "alltypesagg", TPrivilegeLevel.ALL)) + .ok(onTable("functional", "view_view", TPrivilegeLevel.ALL), + onTable("functional", "alltypesagg", TPrivilegeLevel.SELECT)) + .ok(onTable("functional", "view_view", TPrivilegeLevel.SELECT), + onTable("functional", "alltypesagg", TPrivilegeLevel.ALL)) + .ok(onTable("functional", "view_view", TPrivilegeLevel.SELECT), + onTable("functional", "alltypesagg", TPrivilegeLevel.SELECT)) + .error(selectError("functional.view_view")) + .error(selectError("functional.view_view"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))) + .error(selectError("functional.view_view"), onDatabase("functional", + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))) + .error(selectError("functional.view_view"), onTable("functional", "view_view", + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)), onTable("functional", + "alltypesagg", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))); + + // Tests authorization after a statement has been rewritten (IMPALA-3915). + authorize("select * from functional_seq_snap.subquery_view") + .ok(onServer(TPrivilegeLevel.ALL)) + .ok(onServer(TPrivilegeLevel.SELECT)) + .ok(onDatabase("functional_seq_snap", TPrivilegeLevel.ALL)) + .ok(onDatabase("functional_seq_snap", TPrivilegeLevel.SELECT)) + .ok(onTable("functional_seq_snap", "subquery_view", TPrivilegeLevel.ALL)) + .ok(onTable("functional_seq_snap", "subquery_view", TPrivilegeLevel.SELECT)) + .error(selectError("functional_seq_snap.subquery_view")) + .error(selectError("functional_seq_snap.subquery_view"), onServer( + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))) + .error(selectError("functional_seq_snap.subquery_view"), + onDatabase("functional_seq_snap", allExcept(TPrivilegeLevel.ALL, + TPrivilegeLevel.SELECT))) + .error(selectError("functional_seq_snap.subquery_view"), + onTable("functional_seq_snap", "subquery_view", allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))); + + // Select from non-existent database. + authorize("select 1 from nodb.alltypes") + .error(selectError("nodb.alltypes")); + + // Select from non-existent table. + authorize("select 1 from functional.notbl") + .error(selectError("functional.notbl")); + + // Select with inline view. + authorize("select a.* from (select * from functional.alltypes) a") + .ok(onServer(TPrivilegeLevel.ALL)) + .ok(onServer(TPrivilegeLevel.SELECT)) + .ok(onDatabase("functional", TPrivilegeLevel.ALL)) + .ok(onDatabase("functional", TPrivilegeLevel.SELECT)) + .ok(onTable("functional", "alltypes", TPrivilegeLevel.ALL)) + .ok(onTable("functional", "alltypes", TPrivilegeLevel.SELECT)) + .ok(onColumn("functional", "alltypes", ALLTYPES_COLUMNS, TPrivilegeLevel.SELECT)) + .error(selectError("functional.alltypes")) + .error(selectError("functional.alltypes"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))) + .error(selectError("functional.alltypes"), onDatabase("functional", + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))) + .error(selectError("functional.alltypes"), onTable("functional", "alltypes", + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))); + + // Select with columns referenced in function, where clause and group by. + authorize("select count(id), int_col from functional.alltypes where id = 10 " + + "group by id, int_col") + .ok(onServer(TPrivilegeLevel.ALL)) + .ok(onServer(TPrivilegeLevel.SELECT)) + .ok(onDatabase("functional", TPrivilegeLevel.ALL)) + .ok(onDatabase("functional", TPrivilegeLevel.SELECT)) + .ok(onTable("functional", "alltypes", TPrivilegeLevel.ALL)) + .ok(onTable("functional", "alltypes", TPrivilegeLevel.SELECT)) + .ok(onColumn("functional", "alltypes", new String[]{"id", "int_col"}, + TPrivilegeLevel.SELECT)) + .error(selectError("functional.alltypes")) + .error(selectError("functional.alltypes"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))) + .error(selectError("functional.alltypes"), onDatabase("functional", + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))) + .error(selectError("functional.alltypes"), onTable("functional", "alltypes", + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))); + + // Select on tables with complex types. + authorize("select a.int_struct_col.f1 from functional.allcomplextypes a " + + "where a.id = 1") + .ok(onServer(TPrivilegeLevel.ALL)) + .ok(onServer(TPrivilegeLevel.SELECT)) + .ok(onDatabase("functional", TPrivilegeLevel.ALL)) + .ok(onDatabase("functional", TPrivilegeLevel.SELECT)) + .ok(onTable("functional", "allcomplextypes", TPrivilegeLevel.ALL)) + .ok(onTable("functional", "allcomplextypes", TPrivilegeLevel.SELECT)) + .ok(onColumn("functional", "allcomplextypes", + new String[]{"id", "int_struct_col"}, TPrivilegeLevel.SELECT)) + .error(selectError("functional.allcomplextypes")) + .error(selectError("functional.allcomplextypes"), onServer( + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))) + .error(selectError("functional.allcomplextypes"), onDatabase("functional", + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))) + .error(selectError("functional.allcomplextypes"), onTable("functional", + "allcomplextypes", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))); + + authorize("select key, pos, item.f1, f2 from functional.allcomplextypes t, " + + "t.struct_array_col, functional.allcomplextypes.int_map_col") + .ok(onServer(TPrivilegeLevel.ALL)) + .ok(onServer(TPrivilegeLevel.SELECT)) + .ok(onDatabase("functional", TPrivilegeLevel.ALL)) + .ok(onDatabase("functional", TPrivilegeLevel.SELECT)) + .ok(onTable("functional", "allcomplextypes", TPrivilegeLevel.ALL)) + .ok(onTable("functional", "allcomplextypes", TPrivilegeLevel.SELECT)) + .ok(onColumn("functional", "allcomplextypes", + new String[]{"struct_array_col", "int_map_col"}, TPrivilegeLevel.SELECT)) + .error(selectError("functional.allcomplextypes")) + .error(selectError("functional.allcomplextypes"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))) + .error(selectError("functional.allcomplextypes"), onDatabase("functional", + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))) + .error(selectError("functional.allcomplextypes"), onTable("functional", + "allcomplextypes", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))); + + for (AuthzTest authzTest: new AuthzTest[]{ + // Select with cross join. + authorize("select * from functional.alltypes union all " + + "select * from functional.alltypessmall"), + // Union on tables. + authorize("select * from functional.alltypes a cross join " + + "functional.alltypessmall b")}) { + authzTest.ok(onServer(TPrivilegeLevel.ALL)) + .ok(onServer(TPrivilegeLevel.SELECT)) + .ok(onDatabase("functional", TPrivilegeLevel.ALL)) + .ok(onDatabase("functional", TPrivilegeLevel.SELECT)) + .ok(onTable("functional", "alltypes", TPrivilegeLevel.ALL), + onTable("functional", "alltypessmall", TPrivilegeLevel.ALL)) + .ok(onTable("functional", "alltypes", TPrivilegeLevel.SELECT), + onTable("functional", "alltypessmall", TPrivilegeLevel.ALL)) + .ok(onTable("functional", "alltypes", TPrivilegeLevel.ALL), + onTable("functional", "alltypessmall", TPrivilegeLevel.SELECT)) + .ok(onTable("functional", "alltypes", TPrivilegeLevel.SELECT), + onTable("functional", "alltypessmall", TPrivilegeLevel.SELECT)) + .ok(onColumn("functional", "alltypes", ALLTYPES_COLUMNS, + TPrivilegeLevel.SELECT), onColumn("functional", "alltypessmall", + ALLTYPES_COLUMNS, TPrivilegeLevel.SELECT)) + .error(selectError("functional.alltypes")) + .error(selectError("functional.alltypes"), onServer( + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))) + .error(selectError("functional.alltypes"), onDatabase("functional", + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))) + .error(selectError("functional.alltypes"), onTable("functional", "alltypes", + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)), + onTable("functional", "alltypessmall", allExcept(TPrivilegeLevel.ALL, + TPrivilegeLevel.SELECT))) + .error(selectError("functional.alltypessmall"), onColumn("functional", + "alltypes", ALLTYPES_COLUMNS, TPrivilegeLevel.SELECT)) + .error(selectError("functional.alltypes"), onColumn("functional", + "alltypessmall", ALLTYPES_COLUMNS, TPrivilegeLevel.SELECT)); + } + + // Union on views. + // Column-level privileges on views are not currently supported. + authorize("select id from functional.alltypes_view union all " + + "select x from functional.alltypes_view_sub") + .ok(onServer(TPrivilegeLevel.ALL)) + .ok(onServer(TPrivilegeLevel.SELECT)) + .ok(onDatabase("functional", TPrivilegeLevel.ALL)) + .ok(onDatabase("functional", TPrivilegeLevel.SELECT)) + .ok(onTable("functional", "alltypes_view", TPrivilegeLevel.ALL), + onTable("functional", "alltypes_view_sub", TPrivilegeLevel.ALL)) + .ok(onTable("functional", "alltypes_view", TPrivilegeLevel.SELECT), + onTable("functional", "alltypes_view_sub", TPrivilegeLevel.SELECT)) + .error(selectError("functional.alltypes_view")) + .error(selectError("functional.alltypes_view"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))) + .error(selectError("functional.alltypes_view"), onDatabase("functional", + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))) + .error(selectError("functional.alltypes_view"), onTable("functional", + "alltypes_view", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)), + onTable("functional", "alltypes_view_sub", TPrivilegeLevel.SELECT)) + .error(selectError("functional.alltypes_view_sub"), onTable("functional", + "alltypes_view_sub", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)), + onTable("functional", "alltypes_view", TPrivilegeLevel.SELECT)); + + // Union from non-existent databases. + authorize("select id from nodb.alltypes union all " + + "select id from functional.alltypesagg").error(selectError("nodb.alltypes")); + + // Union from non-existent tables. + authorize("select id from functional.notbl union all " + + "select id from functional.alltypesagg").error(selectError("functional.notbl")); + } + + @Test + public void testInsert() throws ImpalaException { + // Basic insert into a table. + authorize("insert into functional.zipcode_incomes(id) values('123')") + .ok(onServer(TPrivilegeLevel.ALL)) + .ok(onServer(TPrivilegeLevel.INSERT)) + .ok(onDatabase("functional", TPrivilegeLevel.ALL)) + .ok(onDatabase("functional", TPrivilegeLevel.INSERT)) + .ok(onTable("functional", "zipcode_incomes", TPrivilegeLevel.ALL)) + .ok(onTable("functional", "zipcode_incomes", TPrivilegeLevel.INSERT)) + .error(insertError("functional.zipcode_incomes")) + .error(insertError("functional.zipcode_incomes"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.INSERT))) + .error(insertError("functional.zipcode_incomes"), onDatabase("functional", + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.INSERT))) + .error(insertError("functional.zipcode_incomes"), onTable("functional", + "zipcode_incomes", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.INSERT))); + + for (AuthzTest test: new AuthzTest[]{ + // With clause with insert. + authorize("with t as (select * from functional.alltypestiny) " + + "insert into functional.alltypes partition(month, year) " + + "select * from t"), + // Insert with select on a target table. + authorize("insert into functional.alltypes partition(month, year) " + + "select * from functional.alltypestiny where id < 100")}) { + test.ok(onServer(TPrivilegeLevel.ALL)) + .ok(onServer(TPrivilegeLevel.INSERT, TPrivilegeLevel.SELECT)) + .ok(onDatabase("functional", TPrivilegeLevel.ALL)) + .ok(onDatabase("functional", TPrivilegeLevel.INSERT, TPrivilegeLevel.SELECT)) + .ok(onTable("functional", "alltypes", TPrivilegeLevel.ALL), + onTable("functional", "alltypestiny", TPrivilegeLevel.ALL)) + .ok(onTable("functional", "alltypes", TPrivilegeLevel.INSERT), + onTable("functional", "alltypestiny", TPrivilegeLevel.SELECT)) + .ok(onTable("functional", "alltypes", TPrivilegeLevel.INSERT), + onColumn("functional", "alltypestiny", ALLTYPES_COLUMNS, + TPrivilegeLevel.SELECT)) + .error(selectError("functional.alltypestiny")) + .error(selectError("functional.alltypestiny"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.INSERT, TPrivilegeLevel.SELECT))) + .error(selectError("functional.alltypestiny"), onDatabase("functional", + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.INSERT, + TPrivilegeLevel.SELECT))) + .error(insertError("functional.alltypes"), onTable("functional", + "alltypestiny", TPrivilegeLevel.SELECT), onTable("functional", + "alltypes", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.INSERT))) + .error(selectError("functional.alltypestiny"), onTable("functional", + "alltypestiny", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)), + onTable("functional", "alltypes", TPrivilegeLevel.INSERT)); + } + + // Insert with select on a target view. + // Column-level privileges on views are not currently supported. + authorize("insert into functional.alltypes partition(month, year) " + + "select * from functional.alltypes_view where id < 100") + .ok(onServer(TPrivilegeLevel.ALL)) + .ok(onServer(TPrivilegeLevel.INSERT, TPrivilegeLevel.SELECT)) + .ok(onDatabase("functional", TPrivilegeLevel.ALL)) + .ok(onDatabase("functional", TPrivilegeLevel.INSERT, TPrivilegeLevel.SELECT)) + .ok(onTable("functional", "alltypes", TPrivilegeLevel.ALL), + onTable("functional", "alltypes_view", TPrivilegeLevel.ALL)) + .ok(onTable("functional", "alltypes", TPrivilegeLevel.INSERT), + onTable("functional", "alltypes_view", TPrivilegeLevel.SELECT)) + .error(selectError("functional.alltypes_view")) + .error(selectError("functional.alltypes_view"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.INSERT, TPrivilegeLevel.SELECT))) + .error(selectError("functional.alltypes_view"), onDatabase("functional", + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.INSERT, + TPrivilegeLevel.SELECT))) + .error(insertError("functional.alltypes"), onTable("functional", + "alltypes_view", TPrivilegeLevel.SELECT), onTable("functional", + "alltypes", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.INSERT))) + .error(selectError("functional.alltypes_view"), onTable("functional", + "alltypes_view", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)), + onTable("functional", "alltypes", TPrivilegeLevel.INSERT)); + + // Insert with inline view. + authorize("insert into functional.alltypes partition(month, year) " + + "select b.* from functional.alltypesagg a join (select * from " + + "functional.alltypestiny) b on (a.int_col = b.int_col)") + .ok(onServer(TPrivilegeLevel.ALL)) + .ok(onServer(TPrivilegeLevel.INSERT, TPrivilegeLevel.SELECT)) + .ok(onDatabase("functional", TPrivilegeLevel.ALL)) + .ok(onDatabase("functional", TPrivilegeLevel.INSERT, TPrivilegeLevel.SELECT)) + .ok(onTable("functional", "alltypes", TPrivilegeLevel.ALL), + onTable("functional", "alltypesagg", TPrivilegeLevel.ALL), + onTable("functional", "alltypestiny", TPrivilegeLevel.ALL)) + .ok(onTable("functional", "alltypes", TPrivilegeLevel.INSERT), + onTable("functional", "alltypesagg", TPrivilegeLevel.SELECT), + onTable("functional", "alltypestiny", TPrivilegeLevel.SELECT)) + .error(selectError("functional.alltypesagg")) + .error(selectError("functional.alltypesagg"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.INSERT, TPrivilegeLevel.SELECT))) + .error(selectError("functional.alltypesagg"), onDatabase("functional", allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.INSERT, TPrivilegeLevel.SELECT))) + .error(insertError("functional.alltypes"), onTable("functional", + "alltypesagg", TPrivilegeLevel.SELECT), onTable("functional", + "alltypestiny", TPrivilegeLevel.SELECT), onTable("functional", + "alltypes", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.INSERT))) + .error(selectError("functional.alltypesagg"), onTable("functional", + "alltypesagg", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)), + onTable("functional", "alltypestiny", TPrivilegeLevel.SELECT), + onTable("functional", "alltypes", TPrivilegeLevel.INSERT)) + .error(selectError("functional.alltypestiny"), onTable("functional", + "alltypesagg", TPrivilegeLevel.SELECT), onTable("functional", + "alltypestiny", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)), + onTable("functional", "alltypes", TPrivilegeLevel.INSERT)); + + // Inserting into a view is not allowed. + authorize("insert into functional.alltypes_view(id) values(123)") + .error(insertError("functional.alltypes_view")); + + // Inserting into a non-existent database. + authorize("insert into nodb.alltypes(id) values(1)") + .error(insertError("nodb.alltypes")); + + // Inserting into a non-existent table. + authorize("insert into functional.notbl(id) values(1)") + .error(insertError("functional.notbl")); + } + + @Test + public void testUseDb() throws ImpalaException { + AuthzTest test = authorize("use functional"); + for (TPrivilegeLevel privilege: TPrivilegeLevel.values()) { + test.ok(onServer(privilege)) + .ok(onDatabase("functional", privilege)) + .ok(onTable("functional", "alltypes", privilege)) + .ok(onColumn("functional", "alltypes", "id", privilege)); + } + test.error(accessError("functional.*.*")); + + // Accessing default database should always be allowed. + authorize("use default").ok(); + + // Accessing system database should always be allowed. + authorize("use _impala_builtins").ok(); + + // Use a non-existent database. + authorize("use nodb").error(accessError("nodb.*.*")); + } + + @Test + public void testTruncate() throws ImpalaException { + // Truncate a table. + authorize("truncate table functional.alltypes") + .ok(onServer(TPrivilegeLevel.ALL)) + .ok(onServer(TPrivilegeLevel.INSERT)) + .ok(onDatabase("functional", TPrivilegeLevel.ALL)) + .ok(onDatabase("functional", TPrivilegeLevel.INSERT)) + .ok(onTable("functional", "alltypes", TPrivilegeLevel.ALL)) + .ok(onTable("functional", "alltypes", TPrivilegeLevel.INSERT)) + .error(insertError("functional.alltypes")) + .error(insertError("functional.alltypes"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.INSERT))) + .error(insertError("functional.alltypes"), onDatabase("functional", allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.INSERT))) + .error(insertError("functional.alltypes"), onTable("functional", "alltypes", + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.INSERT))); + + // Truncate a non-existent database. + authorize("truncate table nodb.alltypes") + .error(insertError("nodb.alltypes")); + + // Truncate a non-existent table. + authorize("truncate table functional.notbl") + .error(insertError("functional.notbl")); + + // Truncating a view is not supported. + authorize("truncate table functional.alltypes_view") + .error(insertError("functional.alltypes_view")); + } + + @Test + public void testLoad() throws ImpalaException { + // Load into a table. + authorize("load data inpath 'hdfs://localhost:20500/test-warehouse/tpch.lineitem' " + + "into table functional.alltypes partition(month=10, year=2009)") + .ok(onServer(TPrivilegeLevel.ALL)) + .ok(onDatabase("functional", TPrivilegeLevel.ALL), + onUri("hdfs://localhost:20500/test-warehouse/tpch.lineitem", + TPrivilegeLevel.ALL)) + .ok(onDatabase("functional", TPrivilegeLevel.INSERT), + onUri("hdfs://localhost:20500/test-warehouse/tpch.lineitem", + TPrivilegeLevel.ALL)) + .ok(onTable("functional", "alltypes", TPrivilegeLevel.ALL), + onUri("hdfs://localhost:20500/test-warehouse/tpch.lineitem", + TPrivilegeLevel.ALL)) + .ok(onTable("functional", "alltypes", TPrivilegeLevel.INSERT), + onUri("hdfs://localhost:20500/test-warehouse/tpch.lineitem", + TPrivilegeLevel.ALL)) + .error(insertError("functional.alltypes")) + .error(accessError("hdfs://localhost:20500/test-warehouse/tpch.lineitem"), + onDatabase("functional", TPrivilegeLevel.INSERT)) + .error(accessError("hdfs://localhost:20500/test-warehouse/tpch.lineitem"), + onTable("functional", "alltypes", TPrivilegeLevel.INSERT)) + .error(insertError("functional.alltypes"), + onUri("hdfs://localhost:20500/test-warehouse/tpch.lineitem", + TPrivilegeLevel.ALL)); + + // Load from non-existent URI. + authorize("load data inpath 'hdfs://localhost:20500/test-warehouse/nouri' " + + "into table functional.alltypes partition(month=10, year=2009)") + .error(insertError("functional.alltypes")) + .error(accessError("hdfs://localhost:20500/test-warehouse/nouri"), + onDatabase("functional", TPrivilegeLevel.INSERT)) + .error(accessError("hdfs://localhost:20500/test-warehouse/nouri"), + onTable("functional", "alltypes", TPrivilegeLevel.INSERT)); + + // Load into non-existent database. + authorize("load data inpath 'hdfs://localhost:20500/test-warehouse/tpch.lineitem' " + + "into table nodb.alltypes partition(month=10, year=2009)") + .error(insertError("nodb.alltypes")) + .error(insertError("nodb.alltypes"), onUri( + "hdfs://localhost:20500/test-warehouse/tpch.nouri", TPrivilegeLevel.ALL)); + + // Load into non-existent table. + authorize("load data inpath 'hdfs://localhost:20500/test-warehouse/tpch.lineitem' " + + "into table functional.notbl partition(month=10, year=2009)") + .error(insertError("functional.notbl")) + .error(insertError("functional.notbl"), onUri( + "hdfs://localhost:20500/test-warehouse/tpch.nouri", TPrivilegeLevel.ALL)); + + // Load into a view is not supported. + authorize("load data inpath 'hdfs://localhost:20500/test-warehouse/tpch.lineitem' " + + "into table functional.alltypes_view") + .error(insertError("functional.alltypes_view")); + } + + @Test + public void testResetMetadata() throws ImpalaException { + // Invalidate metadata on server. + authorize("invalidate metadata") + .ok(onServer(TPrivilegeLevel.ALL)) + .ok(onServer(TPrivilegeLevel.REFRESH)) + .error(refreshError("server")); + + // Invalidate metadata/refresh on a table / view + for(String name: new String[] {"alltypes", "alltypes_view"}) { + for (AuthzTest test: new AuthzTest[]{ + authorize("invalidate metadata functional." + name), + authorize("refresh functional." + name)}) { + test.ok(onServer(TPrivilegeLevel.ALL)) + .ok(onServer(TPrivilegeLevel.REFRESH)) + .ok(onDatabase("functional", TPrivilegeLevel.ALL)) + .ok(onDatabase("functional", TPrivilegeLevel.REFRESH)) + .ok(onTable("functional", name, TPrivilegeLevel.ALL)) + .ok(onTable("functional", name, TPrivilegeLevel.REFRESH)) + .error(refreshError("functional." + name)) + .error(refreshError("functional." + name), onDatabase("functional", allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.REFRESH))) + .error(refreshError("functional." + name), onTable("functional", name, + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.REFRESH))); + } + } + + authorize("refresh functions functional") + .ok(onServer(TPrivilegeLevel.REFRESH)) + .ok(onDatabase("functional", TPrivilegeLevel.ALL)) + .ok(onDatabase("functional", TPrivilegeLevel.REFRESH)) + .error(refreshError("functional")) + .error(refreshError("functional"), onServer(allExcept(TPrivilegeLevel.ALL, + TPrivilegeLevel.REFRESH))) + .error(refreshError("functional"), onDatabase("functional", allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.REFRESH))); + + // Reset metadata in non-existent database. + authorize("invalidate metadata nodb").error(refreshError("default.nodb")); + authorize("refresh nodb").error(refreshError("default.nodb")); + authorize("refresh functions nodb").error(refreshError("nodb")); + } + + @Test + public void testShow() throws ImpalaException { + // Show databases should always be allowed. + authorize("show databases").ok(); + + // Show tables. + AuthzTest test = authorize("show tables in functional"); + for (TPrivilegeLevel privilege: TPrivilegeLevel.values()) { + test.ok(onServer(privilege)) + .ok(onDatabase("functional", privilege)) + .ok(onTable("functional", "alltypes", privilege)); + } + test.error(accessError("functional.*.*")); + + // Show functions. + test = authorize("show functions in functional"); + for (TPrivilegeLevel privilege: viewMetadataPrivileges()) { + test.ok(onServer(privilege)) + .ok(onDatabase("functional", privilege)); + } + test.error(accessError("functional")); + + // Show tables in system database should always be allowed. + authorize("show tables in _impala_builtins").ok(); + + // Show tables for non-existent database. + authorize("show tables in nodb").error(accessError("nodb")); + + // Show partitions, table stats, and column stats + for (AuthzTest authzTest: new AuthzTest[]{ + authorize("show partitions functional.alltypes"), + authorize("show table stats functional.alltypes"), + authorize("show column stats functional.alltypes")}) { + for (TPrivilegeLevel privilege: viewMetadataPrivileges()) { + authzTest.ok(onServer(privilege)) + .ok(onDatabase("functional", privilege)) + .ok(onTable("functional", "alltypes", privilege)) + .error(accessError("functional.alltypes"), onColumn("functional", "alltypes", + "id", TPrivilegeLevel.SELECT)); + } + authzTest.error(accessError("functional")); + } + + // Show range partitions.dimtbl + for (TPrivilegeLevel privilege: viewMetadataPrivileges()) { + authorize("show range partitions functional_kudu.dimtbl") + .ok(onServer(privilege)) + .ok(onDatabase("functional_kudu", privilege)) + .ok(onTable("functional_kudu", "dimtbl", privilege)) + .error(accessError("functional_kudu.dimtbl"), onColumn("functional_kudu", + "dimtbl", "id", TPrivilegeLevel.SELECT)) + .error(accessError("functional_kudu")); + } + + // Show files. + for (AuthzTest authzTest: new AuthzTest[]{ + authorize("show files in functional.alltypes"), + authorize("show files in functional.alltypes partition(month=10, year=2010)")}) { + for (TPrivilegeLevel privilege: viewMetadataPrivileges()) { + authzTest.ok(onServer(privilege)) + .ok(onDatabase("functional", privilege)) + .ok(onTable("functional", "alltypes", privilege)); + } + authzTest.error(accessError("functional")); + } + + // Show current roles should always be allowed. + authorize("show current roles").ok(); + + // Show roles should always be allowed. + authorize("show roles").ok(); + + // Show role grant group should always be allowed. + authorize(String.format("show role grant group %s", USER.getName())).ok(); + + // Show grant role should always be allowed. + authorize(String.format("show grant role authz_test_role")).ok(); + + // Show create table. + test = authorize("show create table functional.alltypes"); + for (TPrivilegeLevel privilege: viewMetadataPrivileges()) { + test.ok(onServer(privilege)) + .ok(onDatabase("functional", privilege)) + .ok(onTable("functional", "alltypes", privilege)); + } + test.error(accessError("functional")); + // Show create table on non-existent database. + authorize("show create table nodb.alltypes").error(accessError("nodb.alltypes")); + // Show create table on non-existent table. + authorize("show create table functional.notbl") + .error(accessError("functional.notbl")); + + // Show create view. + test = authorize("show create view functional.alltypes_view"); + for (TPrivilegeLevel privilege: viewMetadataPrivileges()) { + test.ok(onServer(privilege, TPrivilegeLevel.SELECT)) + .ok(onDatabase("functional", privilege, TPrivilegeLevel.SELECT)) + .ok(onTable("functional", "alltypes_view", privilege), + onTable("functional", "alltypes", TPrivilegeLevel.SELECT)); + } + test.error(accessError("functional")); + // Show create view on non-existent database. + authorize("show create view nodb.alltypes").error(accessError("nodb.alltypes")); + // Show create view on non-existent table. + authorize("show create view functional.notbl").error(accessError("functional.notbl")); + + // Show create function. + ScalarFunction fn = addFunction("functional", "f"); + try { + test = authorize("show create function functional.f"); + for (TPrivilegeLevel privilege: viewMetadataPrivileges()) { + test.ok(onServer(privilege)) + .ok(onDatabase("functional", privilege)); + } + test.error(accessError("functional")); + // Show create function on non-existent database. + authorize("show create function nodb.f").error(accessError("nodb")); + // Show create function on non-existent function. + authorize("show create function functional.nofn").error(accessError("functional")); + } finally { + removeFunction(fn); + } + // Show create function in system database should always be allowed. + authorize("show create function _impala_builtins.pi").ok(); + + // Show data sources should always be allowed. + authorize("show data sources").ok(); + } + + /** + * Test describe output of Databases and tables. + * From https://issues.apache.org/jira/browse/IMPALA-6479 + * Column level select privileges should limit output. + */ + @Test + public void testDescribe() throws ImpalaException { + // Describe database. + AuthzTest authzTest = authorize("describe database functional"); + for (TPrivilegeLevel privilege: viewMetadataPrivileges()) { + authzTest.ok(onServer(privilege)) + .ok(onDatabase("functional", privilege)); + } + authzTest.error(accessError("functional")) + .error(accessError("functional"), onServer(allExcept(viewMetadataPrivileges()))) + .error(accessError("functional"), onDatabase("functional", + allExcept(viewMetadataPrivileges()))); + + // Describe on non-existent database. + authorize("describe database nodb").error(accessError("nodb")); + + // Describe table. + TTableName tableName = new TTableName("functional", "alltypes"); + TDescribeOutputStyle style = TDescribeOutputStyle.MINIMAL; + authzTest = authorize("describe functional.alltypes"); + for (TPrivilegeLevel privilege: new TPrivilegeLevel[]{ + TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT}) { + authzTest.okDescribe(tableName, style, ALLTYPES_COLUMNS, null, onServer(privilege)) + .okDescribe(tableName, style, ALLTYPES_COLUMNS, null, onDatabase("functional", + privilege)) + .okDescribe(tableName, style, ALLTYPES_COLUMNS, null, onTable("functional", + "alltypes", privilege)); + } + authzTest.okDescribe(tableName, style, null, ALLTYPES_COLUMNS, onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))) + .okDescribe(tableName, style, null, ALLTYPES_COLUMNS, onDatabase("functional", + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))) + .okDescribe(tableName, style, null, ALLTYPES_COLUMNS, onTable("functional", + "alltypes", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))) + // In this test, since we only have column level privileges on "id", then + // only the "id" column should show and the others should not. + .okDescribe(tableName, style, new String[]{"id"}, ALLTYPES_COLUMNS_WITHOUT_ID, + onColumn("functional", "alltypes", "id", TPrivilegeLevel.SELECT)) + .error(accessError("functional.alltypes")); + + // Describe table extended. + tableName = new TTableName("functional", "alltypes"); + style = TDescribeOutputStyle.EXTENDED; + String[] locationString = new String[]{"Location:"}; + String[] checkStrings = (String[]) ArrayUtils.addAll(ALLTYPES_COLUMNS, + locationString); + authzTest = authorize("describe functional.alltypes"); + for (TPrivilegeLevel privilege: new TPrivilegeLevel[]{ + TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT}) { + authzTest.okDescribe(tableName, style, checkStrings, null, onServer(privilege)) + .okDescribe(tableName, style, checkStrings, null, onDatabase("functional", + privilege)) + .okDescribe(tableName, style, checkStrings, null, onTable("functional", + "alltypes", privilege)); + } + authzTest.okDescribe(tableName, style, locationString, ALLTYPES_COLUMNS, + onServer(allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))) + .okDescribe(tableName, style, locationString, ALLTYPES_COLUMNS, + onDatabase("functional", allExcept(TPrivilegeLevel.ALL, + TPrivilegeLevel.SELECT))) + .okDescribe(tableName, style, locationString, ALLTYPES_COLUMNS, + onTable("functional", "alltypes", allExcept(TPrivilegeLevel.ALL, + TPrivilegeLevel.SELECT))) + // Location should not appear with only column level auth. + .okDescribe(tableName, style, new String[]{"id"}, + (String[]) ArrayUtils.addAll(ALLTYPES_COLUMNS_WITHOUT_ID, + new String[]{"Location:"}), onColumn("functional", "alltypes", "id", + TPrivilegeLevel.SELECT)) + .error(accessError("functional.alltypes")); + + // Describe view. + tableName = new TTableName("functional", "alltypes_view"); + style = TDescribeOutputStyle.MINIMAL; + authzTest = authorize("describe functional.alltypes_view"); + for (TPrivilegeLevel privilege: new TPrivilegeLevel[]{ + TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT}) { + authzTest.okDescribe(tableName, style, ALLTYPES_COLUMNS, null, onServer(privilege)) + .okDescribe(tableName, style, ALLTYPES_COLUMNS, null, onDatabase("functional", + privilege)) + .okDescribe(tableName, style, ALLTYPES_COLUMNS, null, onTable("functional", + "alltypes_view", privilege)); + } + authzTest.okDescribe(tableName, style, null, ALLTYPES_COLUMNS, onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))) + .okDescribe(tableName, style, null, ALLTYPES_COLUMNS, onDatabase("functional", + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))) + .okDescribe(tableName, style, null, ALLTYPES_COLUMNS, onTable("functional", + "alltypes_view", TPrivilegeLevel.INSERT)) + .error(accessError("functional.alltypes_view")); + + // Describe view extended. + tableName = new TTableName("functional", "alltypes_view"); + style = TDescribeOutputStyle.EXTENDED; + // Views have extra output to explicitly check + String[] viewStrings = new String[]{"View Original Text:", "View Expanded Text:"}; + checkStrings = (String[]) ArrayUtils.addAll(ALLTYPES_COLUMNS, viewStrings); + authzTest = authorize("describe functional.alltypes_view"); + for (TPrivilegeLevel privilege: new TPrivilegeLevel[]{ + TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT}) { + authzTest.okDescribe(tableName, style, checkStrings, null, onServer(privilege)) + .okDescribe(tableName, style, checkStrings, null, onDatabase("functional", + privilege)) + .okDescribe(tableName, style, checkStrings, null, onTable("functional", + "alltypes_view", privilege)); + } + authzTest.okDescribe(tableName, style, null, ALLTYPES_COLUMNS, onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))) + .okDescribe(tableName, style, null, ALLTYPES_COLUMNS, onDatabase("functional", + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))) + .okDescribe(tableName, style, viewStrings, ALLTYPES_COLUMNS, onTable("functional", + "alltypes_view", TPrivilegeLevel.INSERT)) + .error(accessError("functional.alltypes_view")); + + // Describe specific column on a table. + authzTest = authorize("describe functional.allcomplextypes.int_struct_col"); + for (TPrivilegeLevel privilege: TPrivilegeLevel.values()) { + authzTest.ok(onServer(privilege)) + .ok(onDatabase("functional", privilege)) + .ok(onTable("functional", "allcomplextypes", privilege)); + } + authzTest.ok(onColumn("functional", "allcomplextypes", "int_struct_col", + TPrivilegeLevel.SELECT)) + .error(accessError("functional.allcomplextypes")); + + for (AuthzTest test: new AuthzTest[]{ + // User has access to a different column. + authorize("describe functional.allcomplextypes.int_struct_col"), + // Insufficient privileges on complex type column, accessing member + authorize("describe functional.allcomplextypes.complex_struct_col.f2"), + // Insufficient privileges on non-complex type column, accessing member + authorize("describe functional.allcomplextypes.nested_struct_col.f1")}) { + test.error(accessError("functional.allcomplextypes"), onColumn("functional", + "allcomplextypes", "id", TPrivilegeLevel.SELECT)); + } + } + + @Test + public void testStats() throws ImpalaException { + for (String statsType: new String[]{"compute", "drop"}) { + authorize(String.format("%s stats functional.alltypes", statsType)) + .ok(onServer(TPrivilegeLevel.ALL)) + .ok(onServer(TPrivilegeLevel.ALTER)) + .ok(onDatabase("functional", TPrivilegeLevel.ALL)) + .ok(onDatabase("functional", TPrivilegeLevel.ALTER)) + .ok(onTable("functional", "alltypes", TPrivilegeLevel.ALL)) + .ok(onTable("functional", "alltypes", TPrivilegeLevel.ALTER)) + .error(alterError("functional.alltypes")) + .error(alterError("functional.alltypes"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.ALTER))) + .error(alterError("functional.alltypes"), onDatabase("functional", + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.ALTER))) + .error(alterError("functional.alltypes"), onTable("functional", "alltypes", + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.ALTER))); + + // Database does not exist. + authorize(String.format("%s stats nodb.notbl", statsType)) + .error(alterError("nodb.notbl")) + .error(alterError("nodb.notbl"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.ALTER))) + .error(alterError("default.nodb"), onDatabase("nodb", allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.ALL))); + + // Table does not exist. + authorize(String.format("%s stats functional.notbl", statsType)) + .error(alterError("functional.notbl")) + .error(alterError("functional.notbl"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.ALTER))) + .error(alterError("default.functional"), onDatabase("functional", allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.ALL))); + } + } + + @Test + public void testCreateDatabase() throws ImpalaException { + for (AuthzTest test: new AuthzTest[]{ + authorize("create database newdb"), + authorize("create database if not exists newdb")}) { + test.ok(onServer(TPrivilegeLevel.ALL)) + .ok(onServer(TPrivilegeLevel.CREATE)) + .error(createError("newdb")) + .error(createError("newdb"), onServer(allExcept(TPrivilegeLevel.ALL, + TPrivilegeLevel.CREATE))); + } + + // Create a database with a specific location. + authorize("create database newdb location " + + "'hdfs://localhost:20500/test-warehouse/new_location'") + .ok(onServer(TPrivilegeLevel.ALL)) + .ok(onServer(TPrivilegeLevel.CREATE), onUri( + "hdfs://localhost:20500/test-warehouse/new_location", TPrivilegeLevel.ALL)) + .error(createError("newdb")) + .error(createError("newdb"), onServer(allExcept(TPrivilegeLevel.ALL, + TPrivilegeLevel.CREATE)), onUri( + "hdfs://localhost:20500/test-warehouse/new_location", TPrivilegeLevel.ALL)) + .error(accessError("hdfs://localhost:20500/test-warehouse/new_location"), + onServer(TPrivilegeLevel.CREATE)); + + // Database already exists. + for (AuthzTest test: new AuthzTest[]{ + authorize("create database functional"), + authorize("create database if not exists functional")}) { + test.error(createError("functional")) + .error(createError("functional"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE))); + } + + authorize("create database if not exists _impala_builtins") + .error(systemDbError(), onServer(TPrivilegeLevel.ALL)); + } + + @Test + public void testCreateTable() throws ImpalaException { + for (AuthzTest test: new AuthzTest[]{ + authorize("create table functional.new_table(i int)"), + authorize("create external table functional.new_table(i int)")}) { + test.ok(onServer(TPrivilegeLevel.ALL)) + .ok(onServer(TPrivilegeLevel.CREATE)) + .ok(onDatabase("functional", TPrivilegeLevel.ALL)) + .ok(onDatabase("functional", TPrivilegeLevel.CREATE)) + .error(createError("functional.new_table"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE))) + .error(createError("functional.new_table"), onDatabase("functional", allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE))); + } + + // Create table like. + authorize("create table functional.new_table like functional.alltypes") + .ok(onServer(TPrivilegeLevel.ALL)) + .ok(onServer(join(viewMetadataPrivileges(), TPrivilegeLevel.CREATE))) + .ok(onDatabase("functional", TPrivilegeLevel.ALL)) + .ok(onDatabase("functional", join(viewMetadataPrivileges(), + TPrivilegeLevel.CREATE))) + .ok(onDatabase("functional"), onDatabase("functional", TPrivilegeLevel.CREATE), + onTable("functional", "alltypes", viewMetadataPrivileges())) + .error(accessError("functional.alltypes")) + .error(accessError("functional.alltypes"), onServer(allExcept( + join(viewMetadataPrivileges(), TPrivilegeLevel.CREATE)))) + .error(createError("functional.new_table"), onDatabase("functional", allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE, TPrivilegeLevel.SELECT))) + .error(createError("functional.new_table"), onDatabase("functional", allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE)), onTable( + "functional", "alltypes", viewMetadataPrivileges())) + .error(accessError("functional.alltypes"), onDatabase("functional", + TPrivilegeLevel.CREATE), onTable("functional", "alltypes", allExcept( + viewMetadataPrivileges()))); + + // Table already exists. + for (AuthzTest test : new AuthzTest[]{ + authorize("create table functional.alltypes(i int)"), + authorize("create table if not exists functional.alltypes(i int)")}) { + test.error(createError("functional.alltypes")) + .error(createError("functional.alltypes"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE))) + .error(createError("functional.alltypes"), onDatabase("functional", allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE))); + } + + // CTAS. + authorize("create table functional.new_table as " + + "select int_col from functional.alltypes") + .ok(onServer(TPrivilegeLevel.ALL)) + .ok(onServer(TPrivilegeLevel.CREATE, TPrivilegeLevel.INSERT, + TPrivilegeLevel.SELECT)) + .ok(onDatabase("functional", TPrivilegeLevel.ALL)) + .ok(onDatabase("functional", TPrivilegeLevel.CREATE, TPrivilegeLevel.INSERT, + TPrivilegeLevel.SELECT)) + .ok(onDatabase("functional"), onDatabase("functional", TPrivilegeLevel.CREATE, + TPrivilegeLevel.INSERT), onTable("functional", "alltypes", + TPrivilegeLevel.SELECT)) + .ok(onDatabase("functional"), onDatabase("functional", TPrivilegeLevel.CREATE, + TPrivilegeLevel.INSERT), onColumn("functional", "alltypes", "int_col", + TPrivilegeLevel.ALL)) + .error(createError("functional.new_table")) + .error(createError("functional.new_table"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE, TPrivilegeLevel.INSERT, + TPrivilegeLevel.SELECT))) + .error(createError("functional.new_table"), onDatabase("functional", allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE, TPrivilegeLevel.INSERT, + TPrivilegeLevel.SELECT))) + .error(createError("functional.new_table"), onDatabase("functional", allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE, TPrivilegeLevel.INSERT)), + onTable("functional", "alltypes", TPrivilegeLevel.SELECT)) + .error(selectError("functional.alltypes"), onDatabase("functional", + TPrivilegeLevel.CREATE, TPrivilegeLevel.INSERT), onTable("functional", + "alltypes", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))); + + // Table with a specific location. + authorize("create table functional.new_table(i int) location " + + "'hdfs://localhost:20500/test-warehouse/new_table'") + .ok(onServer(TPrivilegeLevel.ALL)) + .ok(onServer(TPrivilegeLevel.CREATE), + onUri("hdfs://localhost:20500/test-warehouse/new_table", TPrivilegeLevel.ALL)) + .error(createError("functional.new_table"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE))) + .error(createError("functional.new_table"), onDatabase("functional", allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE)), onUri( + "hdfs://localhost:20500/test-warehouse/new_table", TPrivilegeLevel.ALL)) + .error(accessError("hdfs://localhost:20500/test-warehouse/new_table"), + onDatabase("functional", TPrivilegeLevel.CREATE)); + + // External table with URI location. + authorize("create external table functional.new_table(i int) location " + + "'hdfs://localhost:20500/test-warehouse/UPPER_CASE/test'") + .ok(onServer(TPrivilegeLevel.ALL)) + .ok(onServer(TPrivilegeLevel.CREATE), + onUri("hdfs://localhost:20500/test-warehouse/UPPER_CASE/test", + TPrivilegeLevel.ALL)) + // Wrong case letters on URI. + .error(accessError("hdfs://localhost:20500/test-warehouse/UPPER_CASE/test"), + onServer(TPrivilegeLevel.CREATE), + onUri("hdfs://localhost:20500/test-warehouse/upper_case/test", + TPrivilegeLevel.ALL)) + .error(createError("functional.new_table"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE))) + .error(createError("functional.new_table"), onDatabase("functional", allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE)), onUri( + "hdfs://localhost:20500/test-warehouse/UPPER_CASE/test", TPrivilegeLevel.ALL)) + .error(accessError("hdfs://localhost:20500/test-warehouse/UPPER_CASE/test"), + onDatabase("functional", TPrivilegeLevel.CREATE)); + + authorize("create table functional.new_table like parquet " + + "'hdfs://localhost:20500/test-warehouse/schemas/alltypestiny.parquet'") + .ok(onServer(TPrivilegeLevel.ALL)) + .ok(onServer(TPrivilegeLevel.CREATE), + onUri("hdfs://localhost:20500/test-warehouse/schemas/alltypestiny.parquet", + TPrivilegeLevel.ALL)) + .error(accessError( + "hdfs://localhost:20500/test-warehouse/schemas/alltypestiny.parquet"), + onServer(allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE))) + .error(createError("functional.new_table"), onDatabase("functional", allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE)), onUri( + "hdfs://localhost:20500/test-warehouse/schemas/alltypestiny.parquet", + TPrivilegeLevel.ALL)) + .error(accessError( + "hdfs://localhost:20500/test-warehouse/schemas/alltypestiny.parquet"), + onDatabase("functional", TPrivilegeLevel.CREATE)); + + authorize("create table if not exists _impala_builtins.new_table(i int)") + .error(systemDbError(), onServer(TPrivilegeLevel.ALL)); + + // IMPALA-4000: Only users with ALL privileges on SERVER may create external Kudu + // tables. + authorize("create external table functional.kudu_tbl stored as kudu " + + "tblproperties ('kudu.master_addresses'='127.0.0.1', 'kudu.table_name'='tbl')") + .ok(onServer(TPrivilegeLevel.ALL)) + .error(createError("functional.kudu_tbl")) + .error(accessError("server1"), onServer(allExcept(TPrivilegeLevel.ALL))) + .error(accessError("server1"), onDatabase("functional", TPrivilegeLevel.ALL)); + + // IMPALA-4000: ALL privileges on SERVER are not required to create managed tables. + authorize("create table functional.kudu_tbl (i int, j int, primary key (i))" + + " partition by hash (i) partitions 9 stored as kudu") + .ok(onServer(TPrivilegeLevel.ALL)) + .ok(onDatabase("functional", TPrivilegeLevel.ALL)) + .ok(onDatabase("functional", TPrivilegeLevel.CREATE)) + .error(createError("functional.kudu_tbl")) + .error(createError("functional.kudu_tbl"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE))) + .error(createError("functional.kudu_tbl"), onDatabase("functional", allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE))); + + // IMPALA-6451: CTAS for Kudu tables on non-external tables and without + // TBLPROPERTIES ('kudu.master_addresses') should not require ALL privileges + // on SERVER. + // The statement below causes the SQL statement to be rewritten. + authorize("create table functional.kudu_tbl primary key (bigint_col) " + + "stored as kudu as " + + "select bigint_col, string_col, current_timestamp() as ins_date " + + "from functional.alltypes " + + "where exists (select 1 from functional.alltypes)") + .ok(onServer(TPrivilegeLevel.ALL)) + .ok(onDatabase("functional", TPrivilegeLevel.CREATE, TPrivilegeLevel.INSERT, + TPrivilegeLevel.SELECT)) + .error(createError("functional.kudu_tbl")) + .error(createError("functional.kudu_tbl"), onServer(allExcept(TPrivilegeLevel.ALL, + TPrivilegeLevel.CREATE, TPrivilegeLevel.INSERT, TPrivilegeLevel.SELECT))) + .error(createError("functional.kudu_tbl"), onDatabase("functional", allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE, TPrivilegeLevel.INSERT, + TPrivilegeLevel.SELECT))); + + // Database does not exist. + authorize("create table nodb.new_table(i int)") + .error(createError("nodb.new_table")) + .error(createError("nodb.new_table"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE))) + .error(createError("nodb.new_table"), onDatabase("functional", allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE))); + } + + @Test + public void testCreateView() throws ImpalaException { + for (AuthzTest test: new AuthzTest[]{ + authorize("create view functional.new_view as " + + "select int_col from functional.alltypes"), + authorize("create view functional.new_view(a) as " + + "select int_col from functional.alltypes")}) { + test.ok(onServer(TPrivilegeLevel.ALL)) + .ok(onServer(TPrivilegeLevel.CREATE, TPrivilegeLevel.SELECT)) + .ok(onDatabase("functional", TPrivilegeLevel.ALL)) + .ok(onDatabase("functional", TPrivilegeLevel.CREATE, TPrivilegeLevel.SELECT)) + .ok(onDatabase("functional", TPrivilegeLevel.CREATE), onTable( + "functional", "alltypes", TPrivilegeLevel.SELECT)) + .ok(onDatabase("functional", TPrivilegeLevel.CREATE), onColumn( + "functional", "alltypes", "int_col", TPrivilegeLevel.ALL)) + .error(selectError("functional.alltypes")) + .error(selectError("functional.alltypes"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE, TPrivilegeLevel.SELECT))) + .error(createError("functional.new_view"), onDatabase("functional", allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE))) + .error(selectError("functional.alltypes"), onDatabase("functional", allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))) + .error(selectError("functional.alltypes"), onTable("functional", "alltypes", + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))); + } + + // View with constant select. + authorize("create view functional.new_view as select 1") + .ok(onServer(TPrivilegeLevel.ALL)) + .ok(onServer(TPrivilegeLevel.CREATE)) + .ok(onDatabase("functional", TPrivilegeLevel.ALL)) + .ok(onDatabase("functional", TPrivilegeLevel.CREATE)) + .error(createError("functional.new_view")) + .error(createError("functional.new_view"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE, TPrivilegeLevel.SELECT))) + .error(createError("functional.new_view"), onDatabase("functional", allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE))); + + // View already exists. + for (AuthzTest test: new AuthzTest[]{ + authorize("create view functional.alltypes_view as " + + "select int_col from functional.alltypes"), + authorize("create view if not exists functional.alltypes_view as " + + "select int_col from functional.alltypes")}) { + test.error(selectError("functional.alltypes")) + .error(selectError("functional.alltypes"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE, TPrivilegeLevel.SELECT))) + .error(createError("functional.alltypes_view"), onDatabase("functional", allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE))) + .error(selectError("functional.alltypes"), onDatabase("functional", allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))) + .error(selectError("functional.alltypes"), onTable("functional", "alltypes", + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT))); + } + + authorize("create view if not exists _impala_builtins.new_view as select 1") + .error(systemDbError(), onServer(TPrivilegeLevel.ALL)); + + // Database does not exist. + authorize("create view nodb.new_view as select 1") + .error(createError("nodb.new_view")) + .error(createError("nodb.new_view"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE))) + .error(createError("nodb.new_view"), onDatabase("functional", allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.CREATE))); + } + + @Test + public void testDropDatabase() throws ImpalaException { + for (AuthzTest test: new AuthzTest[]{ + authorize("drop database functional"), + authorize("drop database functional cascade"), + authorize("drop database functional restrict")}) { + test.ok(onServer(TPrivilegeLevel.ALL)) + .ok(onServer(TPrivilegeLevel.DROP)) + .ok(onDatabase("functional", TPrivilegeLevel.ALL)) + .ok(onDatabase("functional", TPrivilegeLevel.DROP)) + .error(dropError("functional")) + .error(dropError("functional"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.DROP))) + .error(dropError("functional"), onDatabase("functional", + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.DROP))); + } + + // Database does not exist. + for (AuthzTest test: new AuthzTest[]{ + authorize("drop database nodb"), + authorize("drop database nodb cascade"), + authorize("drop database nodb restrict")}) { + test.error(dropError("nodb")) + .error(dropError("nodb"), onServer( + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.DROP))); + } + + // Database does not exist but with if exists clause. + for (AuthzTest test: new AuthzTest[]{ + authorize("drop database if exists nodb"), + authorize("drop database if exists nodb cascade"), + authorize("drop database if exists nodb restrict")}) { + test.ok(onServer(TPrivilegeLevel.ALL)) + .ok(onServer(TPrivilegeLevel.DROP)) + .error(dropError("nodb")) + .error(dropError("nodb"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.DROP))); + } + + // Dropping system database is not allowed even if with ALL privilege on server. + authorize("drop database _impala_builtins") + .error(systemDbError(), onServer(TPrivilegeLevel.ALL)); + } + + @Test + public void testDropTable() throws ImpalaException { + authorize("drop table functional.alltypes") + .ok(onServer(TPrivilegeLevel.ALL)) + .ok(onServer(TPrivilegeLevel.DROP)) + .ok(onDatabase("functional", TPrivilegeLevel.ALL)) + .ok(onDatabase("functional", TPrivilegeLevel.DROP)) + .ok(onTable("functional", "alltypes", TPrivilegeLevel.ALL)) + .ok(onTable("functional", "alltypes", TPrivilegeLevel.DROP)) + .error(dropError("functional.alltypes")) + .error(dropError("functional.alltypes"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.DROP))) + .error(dropError("functional.alltypes"), onDatabase("functional", + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.DROP))) + .error(dropError("functional.alltypes"), onTable("functional", "alltypes", + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.DROP))); + + // Database/Table does not exist. + authorize("drop table nodb.notbl") + .error(dropError("nodb.notbl")) + .error(dropError("nodb.notbl"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.DROP))) + .error(dropError("nodb.notbl"), onDatabase("functional", + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.DROP))); + + // Table does not exist. + authorize("drop table functional.notbl") + .error(dropError("functional.notbl")) + .error(dropError("functional.notbl"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.DROP))) + .error(dropError("functional.notbl"), onDatabase("functional", + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.DROP))); + + // Table does not exist but with if exists clause. + authorize("drop table if exists functional.notbl") + .ok(onServer(TPrivilegeLevel.ALL)) + .ok(onServer(TPrivilegeLevel.DROP)) + .ok(onDatabase("functional", TPrivilegeLevel.ALL)) + .ok(onDatabase("functional", TPrivilegeLevel.DROP)) + .error(dropError("functional.notbl")) + .error(dropError("functional.notbl"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.DROP))) + .error(dropError("functional.notbl"), onDatabase("functional", + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.DROP))); + + // Dropping any tables in the system database is not allowed even with ALL privilege + // on server. + authorize("drop table _impala_builtins.tbl") + .error(systemDbError(), onServer(TPrivilegeLevel.ALL)); + } + + @Test + public void testDropView() throws ImpalaException { + authorize("drop view functional.alltypes_view") + .ok(onServer(TPrivilegeLevel.ALL)) + .ok(onServer(TPrivilegeLevel.DROP)) + .ok(onDatabase("functional", TPrivilegeLevel.ALL)) + .ok(onDatabase("functional", TPrivilegeLevel.DROP)) + .ok(onTable("functional", "alltypes_view", TPrivilegeLevel.ALL)) + .ok(onTable("functional", "alltypes_view", TPrivilegeLevel.DROP)) + .error(dropError("functional.alltypes_view")) + .error(dropError("functional.alltypes_view"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.DROP))) + .error(dropError("functional.alltypes_view"), onDatabase("functional", + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.DROP))) + .error(dropError("functional.alltypes_view"), onTable("functional", + "alltypes_view", allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.DROP))); + + // Database does not exist. + authorize("drop view nodb.noview") + .error(dropError("nodb.noview")) + .error(dropError("nodb.noview"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.DROP))) + .error(dropError("nodb.noview"), onDatabase("functional", + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.DROP))); + + // View does not exist. + authorize("drop table functional.noview") + .error(dropError("functional.noview")) + .error(dropError("functional.noview"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.DROP))) + .error(dropError("functional.noview"), onDatabase("functional", + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.DROP))); + + // View does not exist but with if exists clause. + authorize("drop table if exists functional.noview") + .ok(onServer(TPrivilegeLevel.ALL)) + .ok(onServer(TPrivilegeLevel.DROP)) + .ok(onDatabase("functional", TPrivilegeLevel.ALL)) + .ok(onDatabase("functional", TPrivilegeLevel.DROP)) + .error(dropError("functional.noview")) + .error(dropError("functional.noview"), onServer(allExcept( + TPrivilegeLevel.ALL, TPrivilegeLevel.DROP))) + .error(dropError("functional.noview"), onDatabase("functional", + allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.DROP))); + + // Dropping any views in the system database is not allowed even with ALL privilege + // on server. + authorize("drop table _impala_builtins.v") + .error(systemDbError(), onServer(TPrivilegeLevel.ALL)); + } + + // Convert TDescribeResult to list of strings. + private static List<String> resultToStringList(TDescribeResult result) { + List<String> list = new ArrayList<>(); + for (TResultRow row: result.getResults()) { + for (TColumnValue col: row.getColVals()) { + list.add(col.getString_val() == null ? "NULL": col.getString_val().trim()); + } + } + return list; + } + + private static String selectError(String object) { + return "User '%s' does not have privileges to execute 'SELECT' on: " + object; + } + + private static String insertError(String object) { + return "User '%s' does not have privileges to execute 'INSERT' on: " + object; + } + + private static String createError(String object) { + return "User '%s' does not have privileges to execute 'CREATE' on: " + object; + } + + private static String alterError(String object) { + return "User '%s' does not have privileges to execute 'ALTER' on: " + object; + } + + private static String dropError(String object) { + return "User '%s' does not have privileges to execute 'DROP' on: " + object; + } + + private static String accessError(String object) { + return "User '%s' does not have privileges to access: " + object; + } + + private static String refreshError(String object) { + return "User '%s' does not have privileges to execute " + + "'INVALIDATE METADATA/REFRESH' on: " + object; + } + + private static String systemDbError() { + return "Cannot modify system database."; + } + + private ScalarFunction addFunction(String db, String fnName) { + ScalarFunction fn = ScalarFunction.createForTesting(db, fnName, + new ArrayList<Type>(), Type.INT, "/dummy", "dummy.class", null, + null, TFunctionBinaryType.NATIVE); + authzCatalog_.addFunction(fn); + return fn; + } + + private void removeFunction(ScalarFunction fn) { + authzCatalog_.removeFunction(fn); + } + + private TPrivilegeLevel[] join(TPrivilegeLevel[] level1, TPrivilegeLevel... level2) { + TPrivilegeLevel[] levels = new TPrivilegeLevel[level1.length + level2.length]; + int index = 0; + for (TPrivilegeLevel level: level1) { + levels[index++] = level; + } + for (TPrivilegeLevel level: level2) { + levels[index++] = level; + } + return levels; + } + + private TPrivilegeLevel[] viewMetadataPrivileges() { + return new TPrivilegeLevel[]{TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT, + TPrivilegeLevel.INSERT, TPrivilegeLevel.REFRESH}; + } + + private static TPrivilegeLevel[] allExcept(TPrivilegeLevel... excludedPrivLevels) { + HashSet<TPrivilegeLevel> excludedSet = Sets.newHashSet(excludedPrivLevels); + List<TPrivilegeLevel> privLevels = new ArrayList<>(); + for (TPrivilegeLevel level: TPrivilegeLevel.values()) { + if (!excludedSet.contains(level)) { + privLevels.add(level); + } + } + return privLevels.toArray(new TPrivilegeLevel[0]); + } + + private class AuthzTest { + private final AnalysisContext context_; + private final String stmt_; + private final String role_ = "authz_test_role"; + + public AuthzTest(String stmt) { + this(null, stmt); + } + + public AuthzTest(AnalysisContext context, String stmt) { + Preconditions.checkNotNull(stmt); + context_ = context; + stmt_ = stmt; + } + + private void createRole(TPrivilege[]... privileges) throws ImpalaException { + Role role = authzCatalog_.addRole(role_); + authzCatalog_.addRoleGrantGroup(role_, USER.getName()); + for (TPrivilege[] privs: privileges) { + for (TPrivilege privilege: privs) { + privilege.setRole_id(role.getId()); + authzCatalog_.addRolePrivilege(role_, privilege); + } + } + } + + private void dropRole() throws ImpalaException { + authzCatalog_.removeRole(role_); + } + + /** + * This method runs with the specified privileges. + * + * A new temporary role will be created and assigned to the specified privileges + * into the new role. The new role will be dropped once this method finishes. + */ + public AuthzTest ok(TPrivilege[]... privileges) throws ImpalaException { + try { + createRole(privileges); + if (context_ != null) { + authzOk(context_, stmt_); + } else { + authzOk(stmt_); + } + } catch (AuthorizationException ae) { + // Because the same test can be called from multiple statements + // it is useful to know which statement caused the exception. + throw new AuthorizationException(stmt_ + ": " + ae.getMessage(), ae); + } finally { + dropRole(); + } + return this; + } + + /** + * This method runs with the specified privileges and checks describe output. + * + * A new temporary role will be created and assigned to the specified privileges + * into the new role. The new role will be dropped once this method finishes. + */ + public AuthzTest okDescribe(TTableName table, TDescribeOutputStyle style, + String[] requiredStrings, String[] excludedStrings, TPrivilege[]... privileges) + throws ImpalaException { + try { + createRole(privileges); + if (context_ != null) { + authzOk(context_, stmt_); + } else { + authzOk(stmt_); + } + List<String> result = resultToStringList(authzFrontend_.describeTable(table, + style, USER)); + if (requiredStrings != null) { + for (String str: requiredStrings) { + assertTrue(String.format("\"%s\" is not in the describe output.\n" + + "Expected : %s\n" + + "Actual : %s", str, Arrays.toString(requiredStrings), result), + result.contains(str)); + } + } + if (excludedStrings != null) { + for (String str: excludedStrings) { + assertTrue(String.format("\"%s\" should not be in the describe output.", str), + !result.contains(str)); + } + } + } finally { + dropRole(); + } + return this; + } + + /** + * This method runs with the specified privileges. + * + * A new temporary role will be created and assigned to the specified privileges + * into the new role. The new
<TRUNCATED>
