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

<TRUNCATED>

Reply via email to