IMPALA-6802 (part 1): Clean up authorization tests
The first patch of this patch is to introduce a new mechanism of testing
authorization that tests authorization at every hierarchy. This patch
rewrites the authorization tests for select statements.
Testing:
- Added new authorization tests
- Ran all front-end tests
Cherry-picks: not for 2.x
Change-Id: I9cd5713607c423f644451af5ebb3494d3a728e3b
Reviewed-on: http://gerrit.cloudera.org:8080/10135
Reviewed-by: Alex Behm <alex.behm@cloudera.com>
Tested-by: Impala Public Jenkins <impala-public-jenkins@cloudera.com>
diff --git a/fe/src/test/java/org/apache/impala/analysis/AuthorizationTestV2.java b/fe/src/test/java/org/apache/impala/analysis/AuthorizationTestV2.java
new file mode 100644
index 0000000..a46196a
--- /dev/null
+++ b/fe/src/test/java/org/apache/impala/analysis/AuthorizationTestV2.java
@@ -0,0 +1,638 @@
+// 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.ImmutableList;
+import com.google.common.collect.Sets;
+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.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.TPrivilege;
+import org.apache.impala.thrift.TPrivilegeLevel;
+import org.apache.impala.thrift.TPrivilegeScope;
+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.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());
+ }
+ }
+
+ @Test
+ public void testPrivilegeRequests() throws ImpalaException {
+ // Select *
+ Set<String> expectedPrivileges = 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"
+ );
+ verifyPrivilegeReqs("select * from functional.alltypes", expectedPrivileges);
+ verifyPrivilegeReqs("select alltypes.* from functional.alltypes", expectedPrivileges);
+ verifyPrivilegeReqs(createAnalysisCtx("functional"), "select * from alltypes",
+ expectedPrivileges);
+ verifyPrivilegeReqs(createAnalysisCtx("functional"),
+ "select alltypes.* from alltypes", expectedPrivileges);
+ verifyPrivilegeReqs("select a.* from functional.alltypes a", expectedPrivileges);
+
+ // Select a specific column.
+ expectedPrivileges = Sets.newHashSet(
+ "functional.alltypes",
+ "functional.alltypes.id"
+ );
+ verifyPrivilegeReqs("select id from functional.alltypes", expectedPrivileges);
+ verifyPrivilegeReqs("select alltypes.id from functional.alltypes",
+ expectedPrivileges);
+ verifyPrivilegeReqs(createAnalysisCtx("functional"),
+ "select alltypes.id from alltypes", expectedPrivileges);
+ verifyPrivilegeReqs(createAnalysisCtx("functional"), "select id from alltypes",
+ expectedPrivileges);
+ verifyPrivilegeReqs("select alltypes.id from functional.alltypes",
+ expectedPrivileges);
+ verifyPrivilegeReqs("select a.id from functional.alltypes a", expectedPrivileges);
+ }
+
+ @Test
+ public void testSelect() throws ImpalaException {
+ // Select a specific column on a table.
+ authorize("select id 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))
+ .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)))
+ .error(selectError("functional.alltypes"), onColumn("functional",
+ "alltypes", "id", 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 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 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", new String[]{"id", "bool_col",
+ "tinyint_col", "smallint_col", "int_col", "bigint_col", "float_col",
+ "double_col", "date_string_col", "string_col", "timestamp_col", "year",
+ "month"}, 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)))
+ .error(selectError("functional.alltypes"), onColumn("functional", "alltypes",
+ new String[]{"id", "bool_col", "tinyint_col", "smallint_col", "int_col",
+ "bigint_col", "float_col", "double_col", "date_string_col", "string_col",
+ "timestamp_col", "year", "month"}, 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)))
+ .error(selectError("functional.alltypes"), onColumn("functional", "alltypes",
+ "id", 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)))
+ .error(selectError("functional.allcomplextypes"), onColumn("functional",
+ "allcomplextypes", new String[]{"id", "int_struct_col"}, 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)))
+ .error(selectError("functional.allcomplextypes"), onColumn("functional",
+ "allcomplextypes", new String[]{"struct_array_col", "int_map_col"},
+ allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.SELECT)));
+
+ // Select with cross join.
+ authorize("select * from functional.alltypes a cross join " +
+ "functional.alltypessmall b")
+ .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", new String[]{"id", "bool_col",
+ "tinyint_col", "smallint_col", "int_col", "bigint_col", "float_col",
+ "double_col", "date_string_col", "string_col", "timestamp_col", "year",
+ "month"}, TPrivilegeLevel.SELECT),
+ onColumn("functional", "alltypessmall", new String[]{"id", "bool_col",
+ "tinyint_col", "smallint_col", "int_col", "bigint_col", "float_col",
+ "double_col", "date_string_col", "string_col", "timestamp_col", "year",
+ "month"}, 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.alltypes"), onColumn("functional", "alltypes",
+ new String[]{"id", "bool_col", "tinyint_col", "smallint_col", "int_col",
+ "bigint_col", "float_col", "double_col", "date_string_col", "string_col",
+ "timestamp_col", "year", "month"}, allExcept(TPrivilegeLevel.ALL,
+ TPrivilegeLevel.SELECT)), onColumn("functional", "alltypessmall",
+ new String[]{"id", "bool_col", "tinyint_col", "smallint_col", "int_col",
+ "bigint_col", "float_col", "double_col", "date_string_col", "string_col",
+ "timestamp_col", "year", "month"}, allExcept(TPrivilegeLevel.ALL,
+ TPrivilegeLevel.SELECT)));
+ }
+
+ private static String selectError(String object) {
+ return "User '%s' does not have privileges to execute 'SELECT' on: " + object;
+ }
+
+ 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_);
+ }
+ } 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_);
+ ImmutableList<PrivilegeRequest> privilegeReqs =
+ analysisResult.getAnalyzer().getPrivilegeReqs();
+
+ assertEquals(expectedPrivilegeNames.size(), privilegeReqs.size());
+
+ for (PrivilegeRequest privilegeReq : privilegeReqs) {
+ assertTrue(String.format("Privilege on %s should not exist.",
+ privilegeReq.getName()),
+ expectedPrivilegeNames.contains(privilegeReq.getName()));
+ }
+ }
+}
diff --git a/fe/src/test/java/org/apache/impala/testutil/ImpaladTestCatalog.java b/fe/src/test/java/org/apache/impala/testutil/ImpaladTestCatalog.java
index 32b5ee7..cdf1d1b 100644
--- a/fe/src/test/java/org/apache/impala/testutil/ImpaladTestCatalog.java
+++ b/fe/src/test/java/org/apache/impala/testutil/ImpaladTestCatalog.java
@@ -17,8 +17,7 @@
package org.apache.impala.testutil;
-import java.util.Set;
-
+import com.google.common.base.Preconditions;
import org.apache.impala.analysis.TableName;
import org.apache.impala.authorization.AuthorizationConfig;
import org.apache.impala.catalog.CatalogException;
@@ -27,10 +26,14 @@
import org.apache.impala.catalog.HdfsCachePool;
import org.apache.impala.catalog.HdfsTable;
import org.apache.impala.catalog.ImpaladCatalog;
+import org.apache.impala.catalog.Role;
+import org.apache.impala.catalog.RolePrivilege;
import org.apache.impala.catalog.Table;
+import org.apache.impala.thrift.TPrivilege;
import org.apache.impala.util.PatternMatcher;
-import com.google.common.base.Preconditions;
+import java.util.HashSet;
+import java.util.Set;
/**
* Mock catalog used for running FE tests that allows lazy-loading of tables without a
@@ -114,4 +117,19 @@
@Override
public void waitForCatalogUpdate(long timeoutMs) {
}
+
+ public Role addRole(String roleName) {
+ return srcCatalog_.addRole(roleName, new HashSet<String>());
+ }
+
+ public Role addRoleGrantGroup(String roleName, String groupName) throws CatalogException {
+ return srcCatalog_.addRoleGrantGroup(roleName, groupName);
+ }
+
+ public RolePrivilege addRolePrivilege(String roleName, TPrivilege privilege)
+ throws CatalogException {
+ return srcCatalog_.addRolePrivilege(roleName, privilege);
+ }
+
+ public void removeRole(String roleName) { srcCatalog_.removeRole(roleName); }
}