| // 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.Assert; |
| 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; |
| |
| public class AuthorizationTestV2 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 AuthorizationTestV2() { |
| 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); |
| } |
| |
| @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 |
| /** |
| * Test describe output of Databases and tables. |
| * From https://issues.apache.org/jira/browse/IMPALA-6479 |
| * Column level select privileges should limit output. |
| */ |
| 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)); |
| } |
| } |
| |
| // 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 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 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[] 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 role will be dropped once this method finishes. |
| */ |
| public AuthzTest error(String expectedError, TPrivilege[]... privileges) |
| throws ImpalaException { |
| try { |
| createRole(privileges); |
| if (context_ != null) { |
| authzError(context_, stmt_, expectedError); |
| } else { |
| authzError(stmt_, expectedError); |
| } |
| } finally { |
| dropRole(); |
| } |
| return this; |
| } |
| } |
| |
| private AuthzTest authorize(String stmt) { |
| return new AuthzTest(stmt); |
| } |
| |
| private AuthzTest authorize(AnalysisContext ctx, String stmt) { |
| return new AuthzTest(ctx, stmt); |
| } |
| |
| private TPrivilege[] onServer(TPrivilegeLevel... levels) { |
| TPrivilege[] privileges = new TPrivilege[levels.length]; |
| for (int i = 0; i < levels.length; i++) { |
| privileges[i] = new TPrivilege("", levels[i], TPrivilegeScope.SERVER, false); |
| privileges[i].setServer_name(SENTRY_SERVER); |
| privileges[i].setPrivilege_name(RolePrivilege.buildRolePrivilegeName( |
| privileges[i])); |
| } |
| return privileges; |
| } |
| |
| private TPrivilege[] onDatabase(String db, TPrivilegeLevel... levels) { |
| TPrivilege[] privileges = new TPrivilege[levels.length]; |
| for (int i = 0; i < levels.length; i++) { |
| privileges[i] = new TPrivilege("", levels[i], TPrivilegeScope.DATABASE, false); |
| privileges[i].setServer_name(SENTRY_SERVER); |
| privileges[i].setDb_name(db); |
| privileges[i].setPrivilege_name(RolePrivilege.buildRolePrivilegeName( |
| privileges[i])); |
| } |
| return privileges; |
| } |
| |
| private TPrivilege[] onTable(String db, String table, TPrivilegeLevel... levels) { |
| TPrivilege[] privileges = new TPrivilege[levels.length]; |
| for (int i = 0; i < levels.length; i++) { |
| privileges[i] = new TPrivilege("", levels[i], TPrivilegeScope.TABLE, false); |
| privileges[i].setServer_name(SENTRY_SERVER); |
| privileges[i].setDb_name(db); |
| privileges[i].setTable_name(table); |
| privileges[i].setPrivilege_name(RolePrivilege.buildRolePrivilegeName( |
| privileges[i])); |
| } |
| return privileges; |
| } |
| |
| private TPrivilege[] onColumn(String db, String table, String column, |
| TPrivilegeLevel... levels) { |
| return onColumn(db, table, new String[]{column}, levels); |
| } |
| |
| private TPrivilege[] onColumn(String db, String table, String[] columns, |
| TPrivilegeLevel... levels) { |
| int size = columns.length * levels.length; |
| TPrivilege[] privileges = new TPrivilege[size]; |
| int idx = 0; |
| for (int i = 0; i < levels.length; i++) { |
| for (String column: columns) { |
| privileges[idx] = new TPrivilege("", levels[i], TPrivilegeScope.COLUMN, false); |
| privileges[idx].setServer_name(SENTRY_SERVER); |
| privileges[idx].setDb_name(db); |
| privileges[idx].setTable_name(table); |
| privileges[idx].setColumn_name(column); |
| privileges[idx].setPrivilege_name(RolePrivilege.buildRolePrivilegeName( |
| privileges[idx])); |
| idx++; |
| } |
| } |
| return privileges; |
| } |
| |
| private TPrivilege[] onUri(String uri, TPrivilegeLevel... levels) { |
| TPrivilege[] privileges = new TPrivilege[levels.length]; |
| for (int i = 0; i < levels.length; i++) { |
| privileges[i] = new TPrivilege("", levels[i], TPrivilegeScope.URI, false); |
| privileges[i].setServer_name(SENTRY_SERVER); |
| privileges[i].setUri(uri); |
| privileges[i].setPrivilege_name(RolePrivilege.buildRolePrivilegeName( |
| privileges[i])); |
| } |
| return privileges; |
| } |
| |
| private void authzOk(String stmt) throws ImpalaException { |
| authzOk(analysisContext_, stmt); |
| } |
| |
| private void authzOk(AnalysisContext context, String stmt) throws ImpalaException { |
| authzOk(authzFrontend_, context, stmt); |
| } |
| |
| private void authzOk(Frontend fe, AnalysisContext context, String stmt) |
| throws ImpalaException { |
| parseAndAnalyze(stmt, context, fe); |
| } |
| |
| /** |
| * Verifies that a given statement fails authorization and the expected error |
| * string matches. |
| */ |
| private void authzError(String stmt, String expectedError, Matcher matcher) |
| throws ImpalaException { |
| authzError(analysisContext_, stmt, expectedError, matcher); |
| } |
| |
| private void authzError(String stmt, String expectedError) |
| throws ImpalaException { |
| authzError(analysisContext_, stmt, expectedError, startsWith()); |
| } |
| |
| private void authzError(AnalysisContext ctx, String stmt, String expectedError, |
| Matcher matcher) throws ImpalaException { |
| authzError(authzFrontend_, ctx, stmt, expectedError, matcher); |
| } |
| |
| private void authzError(AnalysisContext ctx, String stmt, String expectedError) |
| throws ImpalaException { |
| authzError(authzFrontend_, ctx, stmt, expectedError, startsWith()); |
| } |
| |
| private interface Matcher { |
| boolean match(String actual, String expected); |
| } |
| |
| private static Matcher exact() { |
| return new Matcher() { |
| @Override |
| public boolean match(String actual, String expected) { |
| return actual.equals(expected); |
| } |
| }; |
| } |
| |
| private static Matcher startsWith() { |
| return new Matcher() { |
| @Override |
| public boolean match(String actual, String expected) { |
| return actual.startsWith(expected); |
| } |
| }; |
| } |
| |
| private void authzError(Frontend fe, AnalysisContext ctx, |
| String stmt, String expectedErrorString, Matcher matcher) |
| throws ImpalaException { |
| Preconditions.checkNotNull(expectedErrorString); |
| try { |
| parseAndAnalyze(stmt, ctx, fe); |
| } catch (AuthorizationException e) { |
| // Insert the username into the error. |
| expectedErrorString = String.format(expectedErrorString, ctx.getUser()); |
| String errorString = e.getMessage(); |
| assertTrue( |
| "got error:\n" + errorString + "\nexpected:\n" + expectedErrorString, |
| matcher.match(errorString, expectedErrorString)); |
| return; |
| } |
| fail("Stmt didn't result in authorization error: " + stmt); |
| } |
| |
| private void verifyPrivilegeReqs(String stmt, Set<String> expectedPrivilegeNames) |
| throws ImpalaException { |
| verifyPrivilegeReqs(createAnalysisCtx(), stmt, expectedPrivilegeNames); |
| } |
| |
| private void verifyPrivilegeReqs(AnalysisContext ctx, String stmt, |
| Set<String> expectedPrivilegeNames) throws ImpalaException { |
| AnalysisResult analysisResult = parseAndAnalyze(stmt, ctx, frontend_); |
| Set<String> actualPrivilegeNames = Sets.newHashSet(); |
| for (PrivilegeRequest privReq: analysisResult.getAnalyzer().getPrivilegeReqs()) { |
| actualPrivilegeNames.add(privReq.getName()); |
| } |
| assertEquals(expectedPrivilegeNames, actualPrivilegeNames); |
| } |
| } |