SENTRY-2409: ALTER TABLE SET OWNER does not allow to change the table if using only the table name
diff --git a/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/HiveAuthzBindingHook.java b/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/HiveAuthzBindingHook.java
index 6731d1a..a4002b7 100644
--- a/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/HiveAuthzBindingHook.java
+++ b/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/HiveAuthzBindingHook.java
@@ -276,6 +276,18 @@
           ASTNode newTableNode = (ASTNode)childASTNode.getChild(0);
           currOutDB = extractDatabase(newTableNode);
         }
+
+        // only get DB and table name from TOK_TABNAME for "ALTER TABLE SET OWNER" command
+        // otherwise, it is possible to mistake output table name as input table name. Ideally,
+        // the root token type of the command should be "TOK_ALTERTABLE_OWNER", not "TOK_ALTERTABLE"
+        String command = context.getCommand();
+        if (command != null && command.toLowerCase().contains("set owner") &&
+            "TOK_TABNAME".equals(childASTNode.getText())) {
+
+          extractDbTableNameFromTOKTABLE(childASTNode);
+          currDB = currOutDB;
+          currTab = currOutTab;
+        }
       }
 
       break;
diff --git a/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/authz/HiveAuthzPrivilegesMap.java b/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/authz/HiveAuthzPrivilegesMap.java
index 385ca98..1aaa9b3 100644
--- a/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/authz/HiveAuthzPrivilegesMap.java
+++ b/sentry-binding/sentry-binding-hive/src/main/java/org/apache/sentry/binding/hive/authz/HiveAuthzPrivilegesMap.java
@@ -120,7 +120,7 @@
 
     /* TODO: once HIVE-18762 is in the hiver version integrated with Sentry, uncomment this block
     HiveAuthzPrivileges alterTableSetOwnerPrivilege = new HiveAuthzPrivileges.AuthzPrivilegeBuilder().
-        addInputObjectPriviledge(AuthorizableType.Table, EnumSet.of(DBModelAction.ALL)).
+        addOutputObjectPriviledge(AuthorizableType.Table, EnumSet.of(DBModelAction.ALL)).
         setOperationScope(HiveOperationScope.TABLE).
         setOperationType(HiveOperationType.DDL).
         setGrantOption(true).
diff --git a/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/dbprovider/TestOwnerPrivileges.java b/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/dbprovider/TestOwnerPrivileges.java
index 55a79ee..880fa94 100644
--- a/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/dbprovider/TestOwnerPrivileges.java
+++ b/sentry-tests/sentry-tests-hive/src/test/java/org/apache/sentry/tests/e2e/dbprovider/TestOwnerPrivileges.java
@@ -35,6 +35,7 @@
 
 import org.apache.commons.lang.StringUtils;
 import org.apache.hadoop.fs.permission.FsAction;
+import org.apache.hive.service.cli.HiveSQLException;
 import org.apache.sentry.tests.e2e.hdfs.TestHDFSIntegrationBase;
 import org.apache.sentry.tests.e2e.hive.StaticUserGroup;
 import org.apache.sentry.service.common.ServiceConstants.SentryPrincipalType;
@@ -640,6 +641,170 @@
   }
 
   /**
+   * Verify that the owner privilege is updated when the ownership is changed when DB name
+   * is not explicitly specified
+   *
+   * @throws Exception
+   */
+  @Ignore("Enable the test once HIVE-18762 is in the hiver version integrated with Sentry")
+  @Test
+  public void testAlterTableWithoutDB() throws Exception {
+    dbNames = new String[]{DB1};
+    String allWithGrantRole = "allWithGrant_role";
+    String ownerRole = "owner_role";
+    roles = new String[]{"admin_role", "create_db1", "owner_role"};
+
+    // create required roles
+    setupUserRoles(roles, statementAdmin);
+
+    // create test DB
+    statementAdmin.execute("DROP DATABASE IF EXISTS " + DB1 + " CASCADE");
+    statementAdmin.execute("CREATE DATABASE " + DB1);
+
+    // setup privileges for USER1
+    statementAdmin.execute("GRANT CREATE ON DATABASE " + DB1 + " TO ROLE create_db1");
+    statementAdmin.execute("USE " + DB1);
+
+    // USER1 create table
+    Connection connectionUSER1_1 = hiveServer2.createConnection(USER1_1, USER1_1);
+    Statement statementUSER1_1 = connectionUSER1_1.createStatement();
+    statementUSER1_1.execute("USE " + DB1);
+    statementUSER1_1.execute("CREATE TABLE " + tableName1
+        + " (under_col int comment 'the under column')");
+
+    // verify privileges created for new table
+    verifyTableOwnerPrivilegeExistForPrincipal(statementUSER1_1, SentryPrincipalType.USER, Lists.newArrayList(USER1_1),
+        DB1, tableName1, 1);
+
+    Connection connectionUSER2_1 = hiveServer2.createConnection(USER2_1, USER2_1);
+    Statement statementUSER2_1 = connectionUSER2_1.createStatement();
+
+    try {
+      // create role that has all with grant on the table
+      statementAdmin.execute("create role " + allWithGrantRole);
+      statementAdmin.execute("grant role " + allWithGrantRole + " to group " + USERGROUP2);
+      statementAdmin.execute("grant all on table " + DB1 + "." + tableName1 + " to role " +
+          allWithGrantRole + " with grant option");
+      statementUSER2_1.execute("USE " + DB1);
+
+      // user2_1 having all with grant on this table and can issue command: alter table set owner
+      // alter table set owner to a role
+      statementUSER2_1
+          .execute("ALTER TABLE " + tableName1 + " SET OWNER ROLE " + ownerRole);
+
+      // verify privileges is transferred to role owner_role, which is associated with USERGROUP1,
+      // therefore to USER1_1
+      verifyTableOwnerPrivilegeExistForPrincipal(statementAdmin, SentryPrincipalType.ROLE,
+          Lists.newArrayList(ownerRole),
+          DB1, tableName1, 1);
+
+      // alter table set owner to user USER1_1 and verify privileges is transferred to USER USER1_1
+      statementUSER2_1
+          .execute("ALTER TABLE " + tableName1 + " SET OWNER USER " + USER1_1);
+      verifyTableOwnerPrivilegeExistForPrincipal(statementAdmin, SentryPrincipalType.USER,
+          Lists.newArrayList(USER1_1), DB1, tableName1, 1);
+    } finally {
+      statementAdmin.execute("drop role " + allWithGrantRole);
+
+      statementAdmin.close();
+      connection.close();
+
+      statementUSER1_1.close();
+      connectionUSER1_1.close();
+
+      statementUSER2_1.close();
+      connectionUSER2_1.close();
+    }
+  }
+
+
+  /**
+   * Verify that the owner privilege is not updated for user who does not have all with grant option
+   * when DB name is not explicitly specified
+   *
+   * @throws Exception
+   */
+  @Ignore("Enable the test once HIVE-18762 is in the hiver version integrated with Sentry")
+  @Test
+  public void testAlterTableNegativeWithoutDB() throws Exception {
+    dbNames = new String[]{DB1};
+    String allWithOutGrantRole = "allWithOutGrant_role";
+    String ownerRole = "owner_role";
+    roles = new String[]{"admin_role", "create_db1", "owner_role"};
+
+    // create required roles
+    setupUserRoles(roles, statementAdmin);
+
+    // create test DB
+    statementAdmin.execute("DROP DATABASE IF EXISTS " + DB1 + " CASCADE");
+    statementAdmin.execute("CREATE DATABASE " + DB1);
+
+    // setup privileges for USER1
+    statementAdmin.execute("GRANT CREATE ON DATABASE " + DB1 + " TO ROLE create_db1");
+    statementAdmin.execute("USE " + DB1);
+
+    // USER1 create table
+    Connection connectionUSER1_1 = hiveServer2.createConnection(USER1_1, USER1_1);
+    Statement statementUSER1_1 = connectionUSER1_1.createStatement();
+    statementUSER1_1.execute("USE " + DB1);
+    statementUSER1_1.execute("CREATE TABLE " + tableName1
+        + " (under_col int comment 'the under column')");
+
+    // verify privileges created for new table
+    verifyTableOwnerPrivilegeExistForPrincipal(statementUSER1_1, SentryPrincipalType.USER, Lists.newArrayList(USER1_1),
+        DB1, tableName1, 1);
+
+    Connection connectionUSER2_1 = hiveServer2.createConnection(USER2_1, USER2_1);
+    Statement statementUSER2_1 = connectionUSER2_1.createStatement();
+
+    try {
+      // create role that has all with grant on the table
+      statementAdmin.execute("create role " + allWithOutGrantRole);
+      statementAdmin.execute("grant role " + allWithOutGrantRole + " to group " + USERGROUP2);
+      statementAdmin.execute("grant all on table " + DB1 + "." + tableName1 + " to role " +
+          allWithOutGrantRole);
+      statementUSER2_1.execute("USE " + DB1);
+
+      // user2_1 having all without grant on this table and can not issue command:
+      // alter table set owner to a role
+      try {
+        statementUSER2_1
+            .execute("ALTER TABLE " + tableName1 + " SET OWNER ROLE " + ownerRole);
+        Assert.fail("User without grant permission should not be allowed to change the owner");
+      } catch (HiveSQLException ex) {
+        String exMessage = ex.getMessage();
+        Assert.assertTrue(
+            "Expect required privileges: Server=server1->Db=db_1->Table=tb_1->action=*->grantOption=true; not in Exception message: " + exMessage,
+            exMessage.contains("The required privileges: Server=server1->Db=db_1->Table=tb_1->action=*->grantOption=true;"));
+      }
+
+      // user2_1 having all without grant on this table and can not issue command:
+      // alter table set owner to user USER1_1
+      try {
+        statementUSER2_1
+            .execute("ALTER TABLE " + tableName1 + " SET OWNER USER " + USER1_1);
+        Assert.fail("User without grant permission should not be allowed to change the owner");
+      } catch (HiveSQLException ex) {
+        String exMessage = ex.getMessage();
+        Assert.assertTrue(
+            "Expect required privileges: Server=server1->Db=db_1->Table=tb_1->action=*->grantOption=true; not in Exception message: " + exMessage,
+            exMessage.contains("The required privileges: Server=server1->Db=db_1->Table=tb_1->action=*->grantOption=true;"));
+      }
+    } finally {
+      statementAdmin.execute("drop role " + allWithOutGrantRole);
+
+      statementAdmin.close();
+      connection.close();
+
+      statementUSER1_1.close();
+      connectionUSER1_1.close();
+
+      statementUSER2_1.close();
+      connectionUSER2_1.close();
+    }
+  }
+
+  /**
    * Verify that the user who can call alter table set owner on this table
    *
    * @throws Exception
@@ -692,11 +857,15 @@
     }
 
     // admin issues alter table set owner
-    try {
-      statementAdmin.execute("ALTER TABLE " + DB1 + "." + tableName1 + " SET OWNER ROLE " + ownerRole);
-      Assert.fail("Expect altering table set owner to fail for admin");
-    } catch (Exception ex) {
-      // admin does not have grant option, so cannot issue this command
+    if (!ownerPrivilegeGrantEnabled) {
+      try {
+        statementAdmin
+            .execute("ALTER TABLE " + DB1 + "." + tableName1 + " SET OWNER ROLE " + ownerRole);
+        Assert.fail("Expect altering table set owner to fail for admin");
+      } catch (Exception ex) {
+        // admin is owner of the db. It does not have grant option if owner does not have
+        // grant option, so cannot issue this command
+      }
     }
 
     Connection connectionUSER2_1 = hiveServer2.createConnection(USER2_1, USER2_1);
@@ -800,7 +969,7 @@
   }
 
   // Create test roles
-  protected void setupUserRoles(String[] roles, Statement statement) throws Exception {
+  protected void setupUserRoles(String[] roles, Statement statementAdmin) throws Exception {
     Set<String> userRoles = Sets.newHashSet(roles);
     userRoles.remove("admin_role");