This is an automated email from the ASF dual-hosted git repository.
stigahuang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/impala.git
The following commit(s) were added to refs/heads/master by this push:
new b3b8e2103 IMPALA-12578: Pass owner user of database and table to
Ranger in GRANT/REVOKE
b3b8e2103 is described below
commit b3b8e21038f952b83287d321a6e68c4d5e8cfebd
Author: Fang-Yu Rao <[email protected]>
AuthorDate: Mon Dec 11 16:01:10 2023 -0800
IMPALA-12578: Pass owner user of database and table to Ranger in
GRANT/REVOKE
After RANGER-1200, Ranger allows the owner user of a resource to
grant/revoke a privilege to/from a grantee/revokee, which requires the
client of the Ranger server to provide the ownership information in the
requests for granting and revoking accesses.
Before this patch, Impala did not provide its Ranger plug-in with the
owner user of resource in the GRANT and REVOKE statements and thus the
owner user of a resource was not able to grant/revoke a privilege
to/from other principals. This patch passes to the Ranger server the
owner user of resource in the GRANT and REVOKE statements when the
resource is a database, a table, or a column. This way the owner user
does not have to be explicitly granted additional privileges on the
resource to execute the GRANT and REVOKE statements for the
aforementioned resource types.
For user-defined functions, we will deal with this resource type in
IMPALA-12685 in that it depends on IMPALA-11743 where we will have to
make Impala load from Hive MetaStore the owner user of a user-defined
function.
The patch also fixes a potential bug in getOwnerUser() of Db, LocalDb,
Table, and LocalTable. Specifically, before this patch, when
determining the owner user of a database or a table, Impala returned
the owner name without verifying the corresponding principal type is
indeed a user. This was problematic because the principal type could be
a group or a role. In addition, we note that Ranger assumes implicitly
that the provided owner is a user. This could be seen from the
definition of GrantRevokeRequest. Before Ranger adds an additional
field in GrantRevokeRequest to distinguish an owner user from an owner
group, Impala will not be able to support allowing a user in an owner
group to grant or revoke privileges on the resources owned by the owner
group.
Testing:
- Added an end-to-end test to verify that the owner user of a resource
is able to execute the GRANT/REVOKE statements without being granted
additional privileges if the resource is a database, a table, or a
column.
Change-Id: Ibac5335c65a860963ef0ccd890a926af80585ef3
Reviewed-on: http://gerrit.cloudera.org:8080/20916
Tested-by: Impala Public Jenkins <[email protected]>
Reviewed-by: Fang-Yu Rao <[email protected]>
---
common/thrift/JniCatalog.thrift | 3 +
.../impala/analysis/GrantRevokePrivStmt.java | 4 +
.../org/apache/impala/analysis/PrivilegeSpec.java | 25 +-
.../ranger/RangerCatalogdAuthorizationManager.java | 25 +-
fe/src/main/java/org/apache/impala/catalog/Db.java | 4 +-
.../main/java/org/apache/impala/catalog/Table.java | 3 +-
.../org/apache/impala/catalog/local/LocalDb.java | 4 +-
.../apache/impala/catalog/local/LocalTable.java | 3 +-
.../authorization/AuthorizationTestBase.java | 6 +-
tests/authorization/test_ranger.py | 321 +++++++++++++++++++++
10 files changed, 375 insertions(+), 23 deletions(-)
diff --git a/common/thrift/JniCatalog.thrift b/common/thrift/JniCatalog.thrift
index 879bb610a..9f2e8139e 100755
--- a/common/thrift/JniCatalog.thrift
+++ b/common/thrift/JniCatalog.thrift
@@ -737,6 +737,9 @@ struct TGrantRevokePrivParams {
// The type of principal
5: required CatalogObjects.TPrincipalType principal_type
+
+ // The name of the resource owner.
+ 6: optional string owner_name
}
// Parameters of DROP DATABASE commands
diff --git
a/fe/src/main/java/org/apache/impala/analysis/GrantRevokePrivStmt.java
b/fe/src/main/java/org/apache/impala/analysis/GrantRevokePrivStmt.java
index 6372fe704..06ae0ea03 100644
--- a/fe/src/main/java/org/apache/impala/analysis/GrantRevokePrivStmt.java
+++ b/fe/src/main/java/org/apache/impala/analysis/GrantRevokePrivStmt.java
@@ -65,6 +65,10 @@ public class GrantRevokePrivStmt extends AuthorizationStmt {
params.setPrincipal_type(principalType_);
params.setHas_grant_opt(hasGrantOpt_);
params.setPrivileges(privileges);
+ String ownerName = privilegeSpec_.getOwnerName();
+ if (ownerName != null) {
+ params.setOwner_name(ownerName);
+ }
return params;
}
diff --git a/fe/src/main/java/org/apache/impala/analysis/PrivilegeSpec.java
b/fe/src/main/java/org/apache/impala/analysis/PrivilegeSpec.java
index 5fda09710..aab0948c5 100644
--- a/fe/src/main/java/org/apache/impala/analysis/PrivilegeSpec.java
+++ b/fe/src/main/java/org/apache/impala/analysis/PrivilegeSpec.java
@@ -56,6 +56,7 @@ public class PrivilegeSpec extends StmtNode {
// Set/modified during analysis
private String dbName_;
private String serverName_;
+ private String ownerName_;
private PrivilegeSpec(TPrivilegeLevel privilegeLevel, TPrivilegeScope scope,
String serverName, String dbName, TableName tableName, HdfsUri uri,
@@ -232,14 +233,7 @@ public class PrivilegeSpec extends StmtNode {
case SERVER:
break;
case DATABASE:
- Preconditions.checkState(!Strings.isNullOrEmpty(dbName_));
- try {
- analyzer.getDb(dbName_, true);
- } catch (AnalysisException e) {
- throw new AnalysisException(String.format("Error setting/showing
privileges " +
- "for database '%s'. Verify that the database exists and that you
have " +
- "permissions to issue a GRANT/REVOKE/SHOW GRANT statement.",
dbName_));
- }
+ analyzeTargetDatabase(analyzer);
break;
case URI:
Preconditions.checkNotNull(uri_);
@@ -325,6 +319,7 @@ public class PrivilegeSpec extends StmtNode {
dbName_ = analyzer.getTargetDbName(tableName_);
Preconditions.checkNotNull(dbName_);
table = analyzer.getTable(dbName_, tableName_.getTbl(), /* must_exist */
true);
+ ownerName_ = table.getOwnerUser();
} catch (TableLoadingException e) {
throw new AnalysisException(e.getMessage(), e);
} catch (AnalysisException e) {
@@ -336,6 +331,18 @@ public class PrivilegeSpec extends StmtNode {
return table;
}
+ private void analyzeTargetDatabase(Analyzer analyzer) throws
AnalysisException {
+ Preconditions.checkState(!Strings.isNullOrEmpty(dbName_));
+ try {
+ FeDb db = analyzer.getDb(dbName_, /* throwIfDoesNotExist */ true);
+ ownerName_ = db.getOwnerUser();
+ } catch (AnalysisException e) {
+ throw new AnalysisException(String.format("Error setting/showing
privileges " +
+ "for database '%s'. Verify that the database exists and that you
have " +
+ "permissions to issue a GRANT/REVOKE/SHOW GRANT statement.",
dbName_));
+ }
+ }
+
private void analyzeUdf(Analyzer analyzer) throws AnalysisException {
Preconditions.checkState(scope_ == TPrivilegeScope.USER_DEFINED_FN);
@@ -373,4 +380,6 @@ public class PrivilegeSpec extends StmtNode {
public String getDbName() { return dbName_; }
public String getServerName() { return serverName_; }
+
+ public String getOwnerName() { return ownerName_; }
}
diff --git
a/fe/src/main/java/org/apache/impala/authorization/ranger/RangerCatalogdAuthorizationManager.java
b/fe/src/main/java/org/apache/impala/authorization/ranger/RangerCatalogdAuthorizationManager.java
index 22adcad1f..809f2928b 100644
---
a/fe/src/main/java/org/apache/impala/authorization/ranger/RangerCatalogdAuthorizationManager.java
+++
b/fe/src/main/java/org/apache/impala/authorization/ranger/RangerCatalogdAuthorizationManager.java
@@ -226,7 +226,8 @@ public class RangerCatalogdAuthorizationManager implements
AuthorizationManager
new User(header.getRequesting_user()).getShortName(), /*isGrant*/ true,
/*user*/ null, Collections.emptyList(),
Collections.singletonList(params.getPrincipal_name()),
- plugin_.get().getClusterName(), header.getClient_ip(),
params.getPrivileges());
+ plugin_.get().getClusterName(), header.getClient_ip(),
params.getPrivileges(),
+ params.getOwner_name());
grantPrivilege(requests, header.getRedacted_sql_stmt(),
header.getClient_ip());
// Update the authorization refresh marker so that the Impalads can
refresh their
@@ -241,7 +242,8 @@ public class RangerCatalogdAuthorizationManager implements
AuthorizationManager
new User(header.getRequesting_user()).getShortName(), /*isGrant*/
false,
/*user*/ null, Collections.emptyList(),
Collections.singletonList(params.getPrincipal_name()),
- plugin_.get().getClusterName(), header.getClient_ip(),
params.getPrivileges());
+ plugin_.get().getClusterName(), header.getClient_ip(),
params.getPrivileges(),
+ params.getOwner_name());
revokePrivilege(requests, header.getRedacted_sql_stmt(),
header.getClient_ip());
// Update the authorization refresh marker so that the Impalads can
refresh their
@@ -255,7 +257,8 @@ public class RangerCatalogdAuthorizationManager implements
AuthorizationManager
List<GrantRevokeRequest> requests = createGrantRevokeRequests(
new User(header.getRequesting_user()).getShortName(), /*isGrant*/ true,
params.getPrincipal_name(), Collections.emptyList(),
Collections.emptyList(),
- plugin_.get().getClusterName(), header.getClient_ip(),
params.getPrivileges());
+ plugin_.get().getClusterName(), header.getClient_ip(),
params.getPrivileges(),
+ params.getOwner_name());
grantPrivilege(requests, header.getRedacted_sql_stmt(),
header.getClient_ip());
refreshAuthorization(response);
@@ -267,7 +270,8 @@ public class RangerCatalogdAuthorizationManager implements
AuthorizationManager
List<GrantRevokeRequest> requests = createGrantRevokeRequests(
new User(header.getRequesting_user()).getShortName(), /*isGrant*/
false,
params.getPrincipal_name(), Collections.emptyList(),
Collections.emptyList(),
- plugin_.get().getClusterName(), header.getClient_ip(),
params.getPrivileges());
+ plugin_.get().getClusterName(), header.getClient_ip(),
params.getPrivileges(),
+ params.getOwner_name());
revokePrivilege(requests, header.getRedacted_sql_stmt(),
header.getClient_ip());
refreshAuthorization(response);
@@ -280,7 +284,7 @@ public class RangerCatalogdAuthorizationManager implements
AuthorizationManager
new User(header.getRequesting_user()).getShortName(), /*isGrant*/ true,
/*user*/ null, Collections.singletonList(params.getPrincipal_name()),
Collections.emptyList(), plugin_.get().getClusterName(),
header.getClient_ip(),
- params.getPrivileges());
+ params.getPrivileges(), params.getOwner_name());
grantPrivilege(requests, header.getRedacted_sql_stmt(),
header.getClient_ip());
refreshAuthorization(response);
@@ -293,7 +297,7 @@ public class RangerCatalogdAuthorizationManager implements
AuthorizationManager
new User(header.getRequesting_user()).getShortName(), /*isGrant*/
false,
/*user*/ null, Collections.singletonList(params.getPrincipal_name()),
Collections.emptyList(), plugin_.get().getClusterName(),
header.getClient_ip(),
- params.getPrivileges());
+ params.getPrivileges(), params.getOwner_name());
revokePrivilege(requests, header.getRedacted_sql_stmt(),
header.getClient_ip());
// Update the authorization refresh marker so that the Impalads can
refresh their
@@ -376,13 +380,15 @@ public class RangerCatalogdAuthorizationManager
implements AuthorizationManager
public static List<GrantRevokeRequest> createGrantRevokeRequests(String
grantor,
boolean isGrant, String user, List<String> groups, List<String> roles,
- String clusterName, String clientIp, List<TPrivilege> privileges) {
+ String clusterName, String clientIp, List<TPrivilege> privileges,
+ String resourceOwner) {
List<GrantRevokeRequest> requests = new ArrayList<>();
for (TPrivilege p: privileges) {
Function<Map<String, String>, GrantRevokeRequest> createRequest =
(resource) ->
createGrantRevokeRequest(grantor, user, groups, roles, clusterName,
- p.has_grant_opt, isGrant, p.privilege_level, resource, clientIp);
+ p.has_grant_opt, isGrant, p.privilege_level, resource,
resourceOwner,
+ clientIp);
// Ranger Impala service definition defines 4 resources:
// [DB -> Table -> Column]
@@ -419,7 +425,7 @@ public class RangerCatalogdAuthorizationManager implements
AuthorizationManager
private static GrantRevokeRequest createGrantRevokeRequest(String grantor,
String user,
List<String> groups, List<String> roles, String clusterName, boolean
withGrantOpt,
boolean isGrant, TPrivilegeLevel level, Map<String, String> resource,
- String clientIp) {
+ String resourceOwner, String clientIp) {
GrantRevokeRequest request = new GrantRevokeRequest();
request.setGrantor(grantor);
// In a Kerberized environment, we also need to call setGrantorGroups() to
provide
@@ -434,6 +440,7 @@ public class RangerCatalogdAuthorizationManager implements
AuthorizationManager
request.setReplaceExistingPermissions(Boolean.FALSE);
request.setClusterName(clusterName);
request.setResource(resource);
+ if (resourceOwner != null) request.setOwnerUser(resourceOwner);
request.setClientIPAddress(clientIp);
// For revoke grant option, omit the privilege
diff --git a/fe/src/main/java/org/apache/impala/catalog/Db.java
b/fe/src/main/java/org/apache/impala/catalog/Db.java
index f767ba80a..919a71e3f 100644
--- a/fe/src/main/java/org/apache/impala/catalog/Db.java
+++ b/fe/src/main/java/org/apache/impala/catalog/Db.java
@@ -29,6 +29,7 @@ import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
import org.apache.hadoop.hive.metastore.api.Database;
+import org.apache.hadoop.hive.metastore.api.PrincipalType;
import org.apache.impala.analysis.ColumnDef;
import org.apache.impala.analysis.KuduPartitionParam;
import org.apache.impala.catalog.events.InFlightEvents;
@@ -619,7 +620,8 @@ public class Db extends CatalogObjectImpl implements FeDb {
@Override // FeDb
public String getOwnerUser() {
org.apache.hadoop.hive.metastore.api.Database db = getMetaStoreDb();
- return db == null ? null : db.getOwnerName();
+ return db == null ? null :
+ (db.getOwnerType() == PrincipalType.USER ? db.getOwnerName() : null);
}
/**
diff --git a/fe/src/main/java/org/apache/impala/catalog/Table.java
b/fe/src/main/java/org/apache/impala/catalog/Table.java
index 0c641505b..dfe4bc87f 100644
--- a/fe/src/main/java/org/apache/impala/catalog/Table.java
+++ b/fe/src/main/java/org/apache/impala/catalog/Table.java
@@ -35,6 +35,7 @@ import org.apache.hadoop.hive.metastore.TableType;
import org.apache.hadoop.hive.metastore.api.ColumnStatisticsData;
import org.apache.hadoop.hive.metastore.api.ColumnStatisticsObj;
import org.apache.hadoop.hive.metastore.api.FieldSchema;
+import org.apache.hadoop.hive.metastore.api.PrincipalType;
import org.apache.impala.analysis.TableName;
import org.apache.impala.catalog.events.InFlightEvents;
import org.apache.impala.catalog.monitor.CatalogMonitor;
@@ -925,7 +926,7 @@ public abstract class Table extends CatalogObjectImpl
implements FeTable {
LOG.warn("Owner of {} is unknown due to table is unloaded",
getFullName());
return null;
}
- return msTable_.getOwner();
+ return msTable_.getOwnerType() == PrincipalType.USER ? msTable_.getOwner()
: null;
}
public void setMetaStoreTable(org.apache.hadoop.hive.metastore.api.Table
msTbl) {
diff --git a/fe/src/main/java/org/apache/impala/catalog/local/LocalDb.java
b/fe/src/main/java/org/apache/impala/catalog/local/LocalDb.java
index a12e4aa9e..8669af064 100644
--- a/fe/src/main/java/org/apache/impala/catalog/local/LocalDb.java
+++ b/fe/src/main/java/org/apache/impala/catalog/local/LocalDb.java
@@ -26,6 +26,7 @@ import java.util.Map;
import java.util.Set;
import org.apache.hadoop.hive.metastore.api.Database;
+import org.apache.hadoop.hive.metastore.api.PrincipalType;
import org.apache.hadoop.hive.metastore.api.Table;
import org.apache.impala.analysis.ColumnDef;
import org.apache.impala.analysis.KuduPartitionParam;
@@ -325,6 +326,7 @@ public class LocalDb implements FeDb {
@Override // FeDb
public String getOwnerUser() {
Database db = getMetaStoreDb();
- return db == null? null : db.getOwnerName();
+ return db == null ? null :
+ (db.getOwnerType() == PrincipalType.USER ? db.getOwnerName() : null);
}
}
diff --git a/fe/src/main/java/org/apache/impala/catalog/local/LocalTable.java
b/fe/src/main/java/org/apache/impala/catalog/local/LocalTable.java
index 30c1270c1..15e1bda6a 100644
--- a/fe/src/main/java/org/apache/impala/catalog/local/LocalTable.java
+++ b/fe/src/main/java/org/apache/impala/catalog/local/LocalTable.java
@@ -27,6 +27,7 @@ import com.google.common.annotations.VisibleForTesting;
import org.apache.hadoop.hive.common.ValidWriteIdList;
import org.apache.hadoop.hive.metastore.TableType;
import org.apache.hadoop.hive.metastore.api.ColumnStatisticsObj;
+import org.apache.hadoop.hive.metastore.api.PrincipalType;
import org.apache.hadoop.hive.metastore.api.Table;
import org.apache.impala.analysis.TableName;
import org.apache.impala.catalog.ArrayType;
@@ -230,7 +231,7 @@ abstract class LocalTable implements FeTable {
LOG.warn("Owner of {} is unknown due to msTable is unloaded",
getFullName());
return null;
}
- return msTable_.getOwner();
+ return msTable_.getOwnerType() == PrincipalType.USER ? msTable_.getOwner()
: null;
}
@Override
diff --git
a/fe/src/test/java/org/apache/impala/authorization/AuthorizationTestBase.java
b/fe/src/test/java/org/apache/impala/authorization/AuthorizationTestBase.java
index 3e551227a..364628456 100644
---
a/fe/src/test/java/org/apache/impala/authorization/AuthorizationTestBase.java
+++
b/fe/src/test/java/org/apache/impala/authorization/AuthorizationTestBase.java
@@ -207,7 +207,8 @@ public abstract class AuthorizationTestBase extends
FrontendTestBase {
// owner of the resource.
getName(),
Collections.emptyList(), Collections.emptyList(),
- rangerImpalaPlugin_.getClusterName(), "127.0.0.1", privileges);
+ rangerImpalaPlugin_.getClusterName(), "127.0.0.1", privileges,
+ /*resourceOwner*/ null);
}
}
@@ -221,7 +222,8 @@ public abstract class AuthorizationTestBase extends
FrontendTestBase {
// owner of the resource.
(as_owner_ ? OWNER_GROUPS : GROUPS), Collections.emptyList(),
// groups,
- rangerImpalaPlugin_.getClusterName(), "127.0.0.1", privileges);
+ rangerImpalaPlugin_.getClusterName(), "127.0.0.1", privileges,
+ /*resourceOwner*/ null);
}
}
diff --git a/tests/authorization/test_ranger.py
b/tests/authorization/test_ranger.py
index d681e77aa..73b5f2a05 100644
--- a/tests/authorization/test_ranger.py
+++ b/tests/authorization/test_ranger.py
@@ -38,6 +38,11 @@ from tests.util.filesystem_utils import WAREHOUSE_PREFIX,
WAREHOUSE
from tests.util.iceberg_util import get_snapshots
ADMIN = "admin"
+OWNER_USER = getuser()
+NON_OWNER = "non_owner"
+ERROR_GRANT = "User doesn't have necessary permission to grant access"
+ERROR_REVOKE = "User doesn't have necessary permission to revoke access"
+
RANGER_AUTH = ("admin", "admin")
RANGER_HOST = "http://localhost:6080"
REST_HEADERS = {"Content-Type": "application/json", "Accept":
"application/json"}
@@ -1119,6 +1124,322 @@ class TestRanger(CustomClusterTestSuite):
pytest.xfail("getTableIfCached() faulty behavior, known issue")
self._test_ownership()
+ @pytest.mark.execute_serially
+ @CustomClusterTestSuite.with_args(
+ impalad_args=IMPALAD_ARGS, catalogd_args=CATALOGD_ARGS)
+ def test_grant_revoke_by_owner_legacy_catalog(self, unique_name):
+ self._test_grant_revoke_by_owner(unique_name)
+
+ @pytest.mark.execute_serially
+ @CustomClusterTestSuite.with_args(impalad_args=LOCAL_CATALOG_IMPALAD_ARGS,
+ catalogd_args=LOCAL_CATALOG_CATALOGD_ARGS)
+ def test_grant_revoke_by_owner_local_catalog(self, unique_name):
+ self._test_grant_revoke_by_owner(unique_name)
+
+ def _test_grant_revoke_by_owner(self, unique_name):
+ non_owner_user = NON_OWNER
+ non_owner_group = NON_OWNER
+ grantee_role = "grantee_role"
+ resource_owner_role = OWNER_USER
+ admin_client = self.create_impala_client()
+ unique_database = unique_name + "_db"
+ table_name = "tbl"
+ column_names = ["a", "b"]
+ udf_uri = "/test-warehouse/impala-hive-udfs.jar"
+ udf_name = "identity"
+ test_data = [("USER", non_owner_user), ("GROUP", non_owner_group),
+ ("ROLE", grantee_role)]
+ privileges = ["alter", "drop", "create", "insert", "select", "refresh"]
+
+ try:
+ admin_client.execute("create role {}".format(grantee_role), user=ADMIN)
+ admin_client.execute("create role {}".format(resource_owner_role),
user=ADMIN)
+ admin_client.execute("grant create on server to user
{0}".format(OWNER_USER),
+ user=ADMIN)
+ # A user has to be granted the ALL privilege on the URI of the UDF as
well to be
+ # able to create a UDF.
+ admin_client.execute("grant all on uri '{0}{1}' to user {2}"
+ .format(os.getenv("FILESYSTEM_PREFIX"), udf_uri, OWNER_USER),
user=ADMIN)
+ self._run_query_as_user("create database {0}".format(unique_database),
OWNER_USER,
+ True)
+ self._run_query_as_user("create table {0}.{1} ({2} int, {3} string)"
+ .format(unique_database, table_name, column_names[0],
column_names[1]),
+ OWNER_USER, True)
+ self._run_query_as_user("create function {0}.{1} "
+ "location '{2}{3}' symbol='org.apache.impala.TestUdf'"
+ .format(unique_database, udf_name, os.getenv("FILESYSTEM_PREFIX"),
udf_uri),
+ OWNER_USER, True)
+
+ for data in test_data:
+ grantee_type = data[0]
+ grantee = data[1]
+ for privilege in privileges:
+ # Case 1: when the resource is a database.
+ self._test_grant_revoke_by_owner_on_database(privilege,
unique_database,
+ grantee_type, grantee, resource_owner_role)
+
+ # Case 2: when the resource is a table.
+ self._test_grant_revoke_by_owner_on_table(privilege, unique_database,
+ table_name, grantee_type, grantee, resource_owner_role)
+
+ # Case 3: when the resource is a column.
+ self._test_grant_revoke_by_owner_on_column(privilege, column_names,
+ unique_database, table_name, grantee_type, grantee,
resource_owner_role)
+
+ # Case 4: when the resource is a UDF.
+ self._test_grant_revoke_by_owner_on_udf(privilege, unique_database,
udf_name,
+ grantee_type, grantee)
+ finally:
+ admin_client.execute("drop database if exists {0}
cascade".format(unique_database),
+ user=ADMIN)
+ admin_client.execute("drop role {}".format(grantee_role), user=ADMIN)
+ admin_client.execute("drop role {}".format(resource_owner_role),
user=ADMIN)
+ admin_client.execute("revoke create on server from user
{0}".format(OWNER_USER),
+ user=ADMIN)
+ admin_client.execute("revoke all on uri '{0}{1}' from user {2}"
+ .format(os.getenv("FILESYSTEM_PREFIX"), udf_uri, OWNER_USER),
user=ADMIN)
+
+ def _test_grant_revoke_by_owner_on_database(self, privilege, unique_database,
+ grantee_type, grantee, resource_owner_role):
+ grant_database_stmt = "grant {0} on database {1} to {2} {3}"
+ revoke_database_stmt = "revoke {0} on database {1} from {2} {3}"
+ show_grant_database_stmt = "show grant {0} {1} on database {2}"
+ set_database_owner_user_stmt = "alter database {0} set owner user {1}"
+ set_database_owner_group_stmt = "alter database {0} set owner group {1}"
+ set_database_owner_role_stmt = "alter database {0} set owner role {1}"
+ resource_owner_group = OWNER_USER
+ admin_client = self.create_impala_client()
+
+ try:
+ self._run_query_as_user(grant_database_stmt
+ .format(privilege, unique_database, grantee_type, grantee),
OWNER_USER,
+ True)
+ result = admin_client.execute(show_grant_database_stmt
+ .format(grantee_type, grantee, unique_database), user=ADMIN)
+ TestRanger._check_privileges(result, [
+ [grantee_type, grantee, unique_database, "", "", "", "", "", "*",
+ privilege, "false"],
+ [grantee_type, grantee, unique_database, "*", "*", "", "", "", "",
+ privilege, "false"]])
+
+ self._run_query_as_user(revoke_database_stmt
+ .format(privilege, unique_database, grantee_type, grantee),
OWNER_USER,
+ True)
+ result = admin_client.execute(show_grant_database_stmt
+ .format(grantee_type, grantee, unique_database), user=ADMIN)
+ TestRanger._check_privileges(result, [])
+
+ # Set the owner of the database to a group that has the same name as
'OWNER_USER'
+ # and verify that the user 'OWNER_USER' is not able to grant or revoke a
privilege
+ # on the database.
+ # We run the statement in Hive because currently
+ # ALTER DATABASE SET OWNER GROUP is not supported in Impala. Note that we
+ # need to force Impala to reload the metadata of database since the
change
+ # is made through Hive.
+ self.run_stmt_in_hive(set_database_owner_group_stmt
+ .format(unique_database, resource_owner_group))
+ admin_client.execute("invalidate metadata", user=ADMIN)
+
+ result = self._run_query_as_user(grant_database_stmt
+ .format(privilege, unique_database, grantee_type, grantee),
OWNER_USER,
+ False)
+ assert ERROR_GRANT in str(result)
+ result = self._run_query_as_user(revoke_database_stmt
+ .format(privilege, unique_database, grantee_type, grantee),
OWNER_USER,
+ False)
+ assert ERROR_REVOKE in str(result)
+
+ # Set the owner of the database to a role that has the same name as
'OWNER_USER'
+ # and verify that the user 'OWNER_USER' is not able to grant or revoke a
+ # privilege on the database.
+ admin_client.execute(set_database_owner_role_stmt
+ .format(unique_database, resource_owner_role), user=ADMIN)
+
+ result = self._run_query_as_user(grant_database_stmt
+ .format(privilege, unique_database, grantee_type, grantee),
OWNER_USER,
+ False)
+ assert ERROR_GRANT in str(result)
+ result = self._run_query_as_user(revoke_database_stmt
+ .format(privilege, unique_database, grantee_type, grantee),
OWNER_USER,
+ False)
+ assert ERROR_REVOKE in str(result)
+ # Change the database owner back to the user 'OWNER_USER'.
+ admin_client.execute(set_database_owner_user_stmt
+ .format(unique_database, OWNER_USER), user=ADMIN)
+ finally:
+ # Revoke the privilege that was granted by 'OWNER_USER' in case any of
the
+ # REVOKE statements submitted by 'owner_user' failed to prevent this test
+ # from interfering with other tests.
+ admin_client.execute(revoke_database_stmt
+ .format(privilege, unique_database, grantee_type, grantee),
user=ADMIN)
+
+ def _test_grant_revoke_by_owner_on_table(self, privilege, unique_database,
table_name,
+ grantee_type, grantee, resource_owner_role):
+ # The CREATE privilege on a table is not supported.
+ if privilege == "create":
+ return
+ grant_table_stmt = "grant {0} on table {1}.{2} to {3} {4}"
+ revoke_table_stmt = "revoke {0} on table {1}.{2} from {3} {4}"
+ show_grant_table_stmt = "show grant {0} {1} on table {2}.{3}"
+ resource_owner_group = OWNER_USER
+ admin_client = self.create_impala_client()
+ set_table_owner_user_stmt = "alter table {0}.{1} set owner user {2}"
+ set_table_owner_group_stmt = "alter table {0}.{1} set owner group {2}"
+ set_table_owner_role_stmt = "alter table {0}.{1} set owner role {2}"
+
+ try:
+ self._run_query_as_user(grant_table_stmt
+ .format(privilege, unique_database, table_name, grantee_type,
grantee),
+ OWNER_USER, True)
+ result = admin_client.execute(show_grant_table_stmt
+ .format(grantee_type, grantee, unique_database, table_name),
+ user=ADMIN)
+ TestRanger._check_privileges(result, [
+ [grantee_type, grantee, unique_database, table_name, "*", "", "", "",
+ "", privilege, "false"]])
+
+ self._run_query_as_user(revoke_table_stmt
+ .format(privilege, unique_database, table_name, grantee_type,
grantee),
+ OWNER_USER, True)
+ result = admin_client.execute(show_grant_table_stmt
+ .format(grantee_type, grantee, unique_database, table_name),
+ user=ADMIN)
+ TestRanger._check_privileges(result, [])
+
+ # Set the owner of the table to a group that has the same name as
+ # 'OWNER_USER' and verify that the user 'OWNER_USER' is not able to
grant or
+ # revoke a privilege on the table.
+ # We run the statement in Hive because currently
+ # ALTER TABLE SET OWNER GROUP is not supported in Impala. Note that we
+ # need to force Impala to reload the metadata of table since the change
+ # is made through Hive.
+ self.run_stmt_in_hive(set_table_owner_group_stmt
+ .format(unique_database, table_name, resource_owner_group))
+ admin_client.execute("refresh {0}.{1}".format(unique_database,
table_name),
+ user=ADMIN)
+
+ result = self._run_query_as_user(grant_table_stmt
+ .format(privilege, unique_database, table_name, grantee_type,
grantee),
+ OWNER_USER, False)
+ assert ERROR_GRANT in str(result)
+ result = self._run_query_as_user(revoke_table_stmt
+ .format(privilege, unique_database, table_name, grantee_type,
grantee),
+ OWNER_USER, False)
+ assert ERROR_REVOKE in str(result)
+
+ # Set the owner of the table to a role that has the same name as
+ # 'OWNER_USER' and verify that the user 'OWNER_USER' is not able to
grant or
+ # revoke a privilege on the table.
+ admin_client.execute(set_table_owner_role_stmt
+ .format(unique_database, table_name, resource_owner_role),
user=ADMIN)
+
+ result = self._run_query_as_user(grant_table_stmt
+ .format(privilege, unique_database, table_name, grantee_type,
grantee),
+ OWNER_USER, False)
+ assert ERROR_GRANT in str(result)
+ result = self._run_query_as_user(revoke_table_stmt
+ .format(privilege, unique_database, table_name, grantee_type,
grantee),
+ OWNER_USER, False)
+ assert ERROR_REVOKE in str(result)
+ # Change the table owner back to the user 'OWNER_USER'.
+ admin_client.execute(set_table_owner_user_stmt
+ .format(unique_database, table_name, OWNER_USER), user=ADMIN)
+ finally:
+ # Revoke the privilege that was granted by 'OWNER_USER' in case any of
the
+ # REVOKE statements submitted by 'OWNER_USER' failed to prevent this test
+ # from interfering with other tests.
+ admin_client.execute(revoke_table_stmt
+ .format(privilege, unique_database, table_name, grantee_type,
grantee),
+ user=ADMIN)
+
+ def _test_grant_revoke_by_owner_on_column(self, privilege, column_names,
+ unique_database, table_name, grantee_type, grantee, resource_owner_role):
+ # For a column, only the SELECT privilege is allowed.
+ if privilege != "select":
+ return
+ grant_column_stmt = "grant {0} ({1}) on table {2}.{3} to {4} {5}"
+ revoke_column_stmt = "revoke {0} ({1}) on table {2}.{3} from {4} {5}"
+ show_grant_column_stmt = "show grant {0} {1} on column {2}.{3}.{4}"
+ resource_owner_group = OWNER_USER
+ admin_client = self.create_impala_client()
+ set_table_owner_user_stmt = "alter table {0}.{1} set owner user {2}"
+ set_table_owner_group_stmt = "alter table {0}.{1} set owner group {2}"
+ set_table_owner_role_stmt = "alter table {0}.{1} set owner role {2}"
+
+ try:
+ self._run_query_as_user(grant_column_stmt
+ .format(privilege, column_names[0], unique_database, table_name,
+ grantee_type, grantee), OWNER_USER, True)
+ result = admin_client.execute(show_grant_column_stmt
+ .format(grantee_type, grantee, unique_database, table_name,
+ column_names[0]), user=ADMIN)
+ TestRanger._check_privileges(result, [
+ [grantee_type, grantee, unique_database, table_name, column_names[0],
+ "", "", "", "", privilege, "false"]])
+
+ self._run_query_as_user(revoke_column_stmt
+ .format(privilege, column_names[0], unique_database, table_name,
+ grantee_type, grantee), OWNER_USER, True)
+ result = admin_client.execute(show_grant_column_stmt
+ .format(grantee_type, grantee, unique_database, table_name,
+ column_names[0]), user=ADMIN)
+ TestRanger._check_privileges(result, [])
+
+ # Set the owner of the table to a group that has the same name as
'OWNER_USER'
+ # and verify that the user 'OWNER_USER' is not able to grant or revoke a
+ # privilege on a column in the table.
+ self.run_stmt_in_hive(set_table_owner_group_stmt
+ .format(unique_database, table_name, resource_owner_group))
+ admin_client.execute("refresh {0}.{1}".format(unique_database,
table_name),
+ user=ADMIN)
+
+ result = self._run_query_as_user(grant_column_stmt
+ .format(privilege, column_names[0], unique_database, table_name,
+ grantee_type, grantee), OWNER_USER, False)
+ assert ERROR_GRANT in str(result)
+ result = self._run_query_as_user(revoke_column_stmt
+ .format(privilege, column_names[0], unique_database, table_name,
+ grantee_type, grantee), OWNER_USER, False)
+ assert ERROR_REVOKE in str(result)
+
+ # Set the owner of the table to a role that has the same name as
'OWNER_USER' and
+ # verify that the user 'OWNER_USER' is not able to grant or revoke a
privilege on
+ # a column in the table.
+ admin_client.execute(set_table_owner_role_stmt
+ .format(unique_database, table_name, resource_owner_role),
user=ADMIN)
+
+ result = self._run_query_as_user(grant_column_stmt
+ .format(privilege, column_names[0], unique_database, table_name,
+ grantee_type, grantee), OWNER_USER, False)
+ assert ERROR_GRANT in str(result)
+ result = self._run_query_as_user(revoke_column_stmt
+ .format(privilege, column_names[0], unique_database, table_name,
+ grantee_type, grantee), OWNER_USER, False)
+ assert ERROR_REVOKE in str(result)
+ # Change the table owner back to the user 'owner_user'.
+ admin_client.execute(set_table_owner_user_stmt
+ .format(unique_database, table_name, OWNER_USER), user=ADMIN)
+ finally:
+ # Revoke the privilege that was granted by 'OWNER_USER' in case any of
the
+ # REVOKE statements submitted by 'OWNER_USER' failed to prevent this test
+ # from interfering with other tests.
+ admin_client.execute(revoke_column_stmt
+ .format(privilege, column_names[0], unique_database, table_name,
+ grantee_type, grantee), user=ADMIN)
+
+ def _test_grant_revoke_by_owner_on_udf(self, privilege, unique_database,
udf_name,
+ grantee_type, grantee):
+ # Due to IMPALA-11743 and IMPALA-12685, the owner of a UDF could not grant
+ # or revoke the SELECT privilege.
+ result = self._run_query_as_user("grant {0} on user_defined_fn "
+ "{1}.{2} to {3} {4}".format(privilege, unique_database, udf_name,
+ grantee_type, grantee), OWNER_USER, False)
+ assert ERROR_GRANT in str(result)
+ result = self._run_query_as_user("revoke {0} on user_defined_fn "
+ "{1}.{2} from {3} {4}".format(privilege, unique_database, udf_name,
+ grantee_type, grantee), OWNER_USER, False)
+ assert ERROR_REVOKE in str(result)
+
@pytest.mark.execute_serially
@CustomClusterTestSuite.with_args(
impalad_args=IMPALAD_ARGS, catalogd_args=CATALOGD_ARGS)