mirror of
https://github.com/apache/impala.git
synced 2025-12-19 18:12:08 -05:00
IMPALA-9708: Remove Sentry support
Impala 4 decided to drop Sentry support in favor of Ranger. This removes Sentry support and related tests. It retires startup flags related to Sentry and does the first round of removing obsolete code. This does not adjust documentation to remove references to Sentry, and other dead code will be removed separately. Some issues came up when implementing this. Here is a summary of how this patch resolves them: 1. authorization_provider currently defaults to "sentry", but "ranger" requires extra parameters to be set. This changes the default value of authorization_provider to "", which translates internally to the noop policy that does no authorization. 2. These flags are Sentry specific and are now retired: - authorization_policy_provider_class - sentry_catalog_polling_frequency_s - sentry_config 3. The authorization_factory_class may be obsolete now that there is only one authorization policy, but this leaves it in place. 4. Sentry is the last component using CDH_COMPONENTS_HOME, so that is removed. There are still Maven dependencies coming from the CDH_BUILD_NUMBER repository, so that is not removed. 5. To make the transition easier, testdata/bin/kill-sentry-service.sh is not removed and it is still called from testdata/bin/kill-all.sh. Testing: - Core job passes Change-Id: I8e99c15936d6d250cf258e3a1dcba11d3eb4661e Reviewed-on: http://gerrit.cloudera.org:8080/15833 Reviewed-by: Joe McDonnell <joemcdonnell@cloudera.com> Tested-by: Impala Public Jenkins <impala-public-jenkins@cloudera.com>
This commit is contained in:
@@ -4,7 +4,7 @@ Beware that it may become stale over time as the project evolves.
|
||||
# Detailed Build Notes
|
||||
|
||||
Impala can be built with pre-built components or components downloaded from S3.
|
||||
The components needed to build Impala are Apache Hadoop, Hive, HBase, and Sentry.
|
||||
The components needed to build Impala are Apache Hadoop, Hive, and HBase.
|
||||
If you need to manually override the locations or versions of these components, you
|
||||
can do so through the environment variables and scripts listed below.
|
||||
|
||||
@@ -27,8 +27,8 @@ can do so through the environment variables and scripts listed below.
|
||||
| IMPALA_HOME | | Top level Impala directory |
|
||||
| IMPALA_TOOLCHAIN | "${IMPALA_HOME}/toolchain" | Native toolchain directory (for compilers, libraries, etc.) |
|
||||
| SKIP_TOOLCHAIN_BOOTSTRAP | "false" | Skips downloading the toolchain any python dependencies if "true" |
|
||||
| CDH_BUILD_NUMBER | | Identifier to indicate the CDH build number
|
||||
| CDH_COMPONENTS_HOME | "${IMPALA_HOME}/toolchain/cdh_components-${CDH_BUILD_NUMBER}" | Location of the CDH components within the toolchain. |
|
||||
| CDP_BUILD_NUMBER | | Identifier to indicate the CDP build number
|
||||
| CDP_COMPONENTS_HOME | "${IMPALA_HOME}/toolchain/cdp_components-${CDP_BUILD_NUMBER}" | Location of the CDP components within the toolchain. |
|
||||
| CDH_MAJOR_VERSION | "7" | Identifier used to uniqueify paths for potentially incompatible component builds. |
|
||||
| IMPALA_CONFIG_SOURCED | "1" | Set by ${IMPALA_HOME}/bin/impala-config.sh (internal use) |
|
||||
| JAVA_HOME | "/usr/lib/jvm/${JAVA_VERSION}" | Used to locate Java |
|
||||
@@ -64,6 +64,5 @@ can do so through the environment variables and scripts listed below.
|
||||
| HADOOP_LIB_DIR | "${HADOOP_HOME}/lib" | For 'libhdfs.a' or 'libhdfs.so' |
|
||||
| HIVE_HOME | "${CDP_COMPONENTS_HOME}/{hive-${IMPALA_HIVE_VERSION}/" | |
|
||||
| HBASE_HOME | "${CDP_COMPONENTS_HOME}/hbase-${IMPALA_HBASE_VERSION}/" | |
|
||||
| SENTRY_HOME | "${CDP_COMPONENTS_HOME}/sentry-${IMPALA_SENTRY_VERSION}/" | Used to setup test data |
|
||||
| THRIFT_HOME | "${IMPALA_TOOLCHAIN}/thrift-${IMPALA_THRIFT_VERSION}" | |
|
||||
|
||||
|
||||
@@ -213,17 +213,6 @@ class CatalogServiceThriftIf : public CatalogServiceIf {
|
||||
VLOG_RPC << "PrioritizeLoad(): response=" << ThriftDebugString(resp);
|
||||
}
|
||||
|
||||
void SentryAdminCheck(TSentryAdminCheckResponse& resp,
|
||||
const TSentryAdminCheckRequest& req) override {
|
||||
VLOG_RPC << "SentryAdminCheck(): request=" << ThriftDebugString(req);
|
||||
Status status = catalog_server_->catalog()->SentryAdminCheck(req, &resp);
|
||||
if (!status.ok()) LOG(ERROR) << status.GetDetail();
|
||||
TStatus thrift_status;
|
||||
status.ToThrift(&thrift_status);
|
||||
resp.__set_status(thrift_status);
|
||||
VLOG_RPC << "SentryAdminCheck(): response=" << ThriftDebugString(resp);
|
||||
}
|
||||
|
||||
void UpdateTableUsage(TUpdateTableUsageResponse& resp,
|
||||
const TUpdateTableUsageRequest& req) override {
|
||||
VLOG_RPC << "UpdateTableUsage(): request=" << ThriftDebugString(req);
|
||||
|
||||
@@ -103,14 +103,6 @@ class CatalogServiceClientWrapper : public CatalogServiceClient {
|
||||
recv_GetPartitionStats(_return);
|
||||
}
|
||||
|
||||
void SentryAdminCheck(TSentryAdminCheckResponse& _return,
|
||||
const TSentryAdminCheckRequest& req, bool* send_done) {
|
||||
DCHECK(!*send_done);
|
||||
send_SentryAdminCheck(req);
|
||||
*send_done = true;
|
||||
recv_SentryAdminCheck(_return);
|
||||
}
|
||||
|
||||
void UpdateTableUsage(TUpdateTableUsageResponse& _return,
|
||||
const TUpdateTableUsageRequest& req, bool* send_done) {
|
||||
DCHECK(!*send_done);
|
||||
|
||||
@@ -45,11 +45,6 @@ DEFINE_int32(max_nonhdfs_partitions_parallel_load, 20,
|
||||
DEFINE_int32(initial_hms_cnxn_timeout_s, 120,
|
||||
"Number of seconds catalogd will wait to establish an initial connection to the HMS "
|
||||
"before exiting.");
|
||||
DEFINE_int64(sentry_catalog_polling_frequency_s, 60,
|
||||
"Frequency (in seconds) at which the the catalogd polls the sentry service to update "
|
||||
"any policy changes.");
|
||||
DEFINE_string(sentry_config, "", "Local path to a sentry-site.xml configuration "
|
||||
"file. If set, authorization will be enabled.");
|
||||
|
||||
Catalog::Catalog() {
|
||||
JniMethodDescriptor methods[] = {
|
||||
@@ -61,7 +56,6 @@ Catalog::Catalog() {
|
||||
{"getTableMetrics", "([B)Ljava/lang/String;", &get_table_metrics_id_},
|
||||
{"getDbs", "([B)[B", &get_dbs_id_},
|
||||
{"getFunctions", "([B)[B", &get_functions_id_},
|
||||
{"checkUserSentryAdmin", "([B)[B", &sentry_admin_check_id_},
|
||||
{"getCatalogObject", "([B)[B", &get_catalog_object_id_},
|
||||
{"getPartialCatalogObject", "([B)[B", &get_partial_catalog_object_id_},
|
||||
{"getCatalogDelta", "([B)[B", &get_catalog_delta_id_},
|
||||
@@ -189,11 +183,6 @@ Status Catalog::GetPartitionStats(
|
||||
return JniUtil::CallJniMethod(catalog_, get_partition_stats_id_, req, resp);
|
||||
}
|
||||
|
||||
Status Catalog::SentryAdminCheck(const TSentryAdminCheckRequest& req,
|
||||
TSentryAdminCheckResponse* resp) {
|
||||
return JniUtil::CallJniMethod(catalog_, sentry_admin_check_id_, req, resp);
|
||||
}
|
||||
|
||||
Status Catalog::UpdateTableUsage(const TUpdateTableUsageRequest& req) {
|
||||
return JniUtil::CallJniMethod(catalog_, update_table_usage_id_, req);
|
||||
}
|
||||
|
||||
@@ -128,12 +128,6 @@ class Catalog {
|
||||
Status GetPartitionStats(
|
||||
const TGetPartitionStatsRequest& req, TGetPartitionStatsResponse* resp);
|
||||
|
||||
/// Checks whether the requesting user has admin privileges on the Sentry Service and
|
||||
/// returns OK if they do. Returns a bad status if there was an error executing the
|
||||
/// request.
|
||||
Status SentryAdminCheck(const TSentryAdminCheckRequest& req,
|
||||
TSentryAdminCheckResponse* resp);
|
||||
|
||||
/// Update recently used table names and their use counts in an impalad since the last
|
||||
/// report.
|
||||
Status UpdateTableUsage(const TUpdateTableUsageRequest& req);
|
||||
@@ -160,7 +154,6 @@ class Catalog {
|
||||
jmethodID get_functions_id_; // JniCatalog.getFunctions()
|
||||
jmethodID get_partition_stats_id_; // JniCatalog.getPartitionStats()
|
||||
jmethodID prioritize_load_id_; // JniCatalog.prioritizeLoad()
|
||||
jmethodID sentry_admin_check_id_; // JniCatalog.checkUserSentryAdmin()
|
||||
jmethodID catalog_ctor_;
|
||||
jmethodID update_table_usage_id_;
|
||||
};
|
||||
|
||||
@@ -367,6 +367,7 @@ DEFINE_bool(enable_column_masking, true,
|
||||
REMOVED_FLAG(abfs_read_chunk_size);
|
||||
REMOVED_FLAG(adls_read_chunk_size);
|
||||
REMOVED_FLAG(authorization_policy_file);
|
||||
REMOVED_FLAG(authorization_policy_provider_class);
|
||||
REMOVED_FLAG(be_service_threads);
|
||||
REMOVED_FLAG(cgroup_hierarchy_path);
|
||||
REMOVED_FLAG(coordinator_rpc_threads);
|
||||
@@ -399,6 +400,8 @@ REMOVED_FLAG(rm_default_cpu_vcores);
|
||||
REMOVED_FLAG(rm_default_memory);
|
||||
REMOVED_FLAG(rpc_cnxn_attempts);
|
||||
REMOVED_FLAG(rpc_cnxn_retry_interval_ms);
|
||||
REMOVED_FLAG(sentry_catalog_polling_frequency_s);
|
||||
REMOVED_FLAG(sentry_config);
|
||||
REMOVED_FLAG(skip_lzo_version_check);
|
||||
REMOVED_FLAG(staging_cgroup);
|
||||
REMOVED_FLAG(status_report_interval);
|
||||
|
||||
@@ -370,21 +370,6 @@ Status CatalogOpExecutor::GetPartitionStats(
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status CatalogOpExecutor::SentryAdminCheck(const TSentryAdminCheckRequest& req,
|
||||
TSentryAdminCheckResponse* result) {
|
||||
const TNetworkAddress& address =
|
||||
MakeNetworkAddress(FLAGS_catalog_service_host, FLAGS_catalog_service_port);
|
||||
int attempt = 0; // Used for debug action only.
|
||||
CatalogServiceConnection::RpcStatus rpc_status =
|
||||
CatalogServiceConnection::DoRpcWithRetry(env_->catalogd_client_cache(), address,
|
||||
&CatalogServiceClientWrapper::SentryAdminCheck, req,
|
||||
FLAGS_catalog_client_connection_num_retries,
|
||||
FLAGS_catalog_client_rpc_retry_interval_ms,
|
||||
[&attempt]() { return CatalogRpcDebugFn(&attempt); }, result);
|
||||
RETURN_IF_ERROR(rpc_status.status);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status CatalogOpExecutor::UpdateTableUsage(const TUpdateTableUsageRequest& req,
|
||||
TUpdateTableUsageResponse* resp) {
|
||||
const TNetworkAddress& address =
|
||||
|
||||
@@ -79,12 +79,6 @@ class CatalogOpExecutor {
|
||||
Status UpdateTableUsage(const TUpdateTableUsageRequest& req,
|
||||
TUpdateTableUsageResponse* resp);
|
||||
|
||||
/// Makes an RPC to the CatalogServer to verify whether the specified user has
|
||||
/// privileges / to access the Sentry Policy Service. Returns OK if the RPC was
|
||||
/// successful, otherwise a bad status will be returned.
|
||||
Status SentryAdminCheck(const TSentryAdminCheckRequest& req,
|
||||
TSentryAdminCheckResponse* resp);
|
||||
|
||||
/// Set in Exec(), returns a pointer to the TDdlExecResponse of the DDL execution.
|
||||
/// If called before Exec(), this will return NULL. Only set if the
|
||||
/// TCatalogOpType is DDL.
|
||||
|
||||
@@ -576,30 +576,6 @@ Java_org_apache_impala_service_FeSupport_NativeUpdateTableUsage(
|
||||
return result_bytes;
|
||||
}
|
||||
|
||||
// Calls the catalog server to to check if the given user is a Sentry admin.
|
||||
extern "C"
|
||||
JNIEXPORT jbyteArray JNICALL
|
||||
Java_org_apache_impala_service_FeSupport_NativeSentryAdminCheck(
|
||||
JNIEnv* env, jclass caller_class, jbyteArray thrift_struct) {
|
||||
TSentryAdminCheckRequest request;
|
||||
THROW_IF_ERROR_RET(DeserializeThriftMsg(env, thrift_struct, &request), env,
|
||||
JniUtil::internal_exc_class(), nullptr);
|
||||
|
||||
CatalogOpExecutor catalog_op_executor(ExecEnv::GetInstance(), nullptr, nullptr);
|
||||
TSentryAdminCheckResponse result;
|
||||
Status status = catalog_op_executor.SentryAdminCheck(request, &result);
|
||||
if (!status.ok()) {
|
||||
LOG(ERROR) << status.GetDetail();
|
||||
status.AddDetail("Error making an RPC call to Catalog server.");
|
||||
status.SetTStatus(&result);
|
||||
}
|
||||
|
||||
jbyteArray result_bytes = nullptr;
|
||||
THROW_IF_ERROR_RET(SerializeThriftMsg(env, &result, &result_bytes), env,
|
||||
JniUtil::internal_exc_class(), result_bytes);
|
||||
return result_bytes;
|
||||
}
|
||||
|
||||
// Calls in to the catalog server to request partial information about a
|
||||
// catalog object.
|
||||
extern "C"
|
||||
@@ -748,11 +724,6 @@ static JNINativeMethod native_methods[] = {
|
||||
const_cast<char*>("([B)[B"),
|
||||
(void*)::Java_org_apache_impala_service_FeSupport_NativeUpdateTableUsage
|
||||
},
|
||||
{
|
||||
const_cast<char*>("NativeSentryAdminCheck"),
|
||||
const_cast<char*>("([B)[B"),
|
||||
(void*)::Java_org_apache_impala_service_FeSupport_NativeSentryAdminCheck
|
||||
},
|
||||
{
|
||||
const_cast<char*>("NativeParseQueryOptions"),
|
||||
const_cast<char*>("(Ljava/lang/String;[B)[B"),
|
||||
|
||||
@@ -39,9 +39,10 @@ using namespace impala;
|
||||
// Authorization related flags. Must be set to valid values to properly configure
|
||||
// authorization.
|
||||
DEFINE_string(authorization_provider,
|
||||
"sentry",
|
||||
"",
|
||||
"Specifies the type of internally-provided authorization provider to use. "
|
||||
"['ranger', 'sentry' (default)]");
|
||||
"Defaults to unset, which disables authorization. To enable authorization, "
|
||||
"set to one of the following: ['ranger']");
|
||||
DEFINE_string(authorization_factory_class,
|
||||
"",
|
||||
"Specifies the class name that implements the authorization provider. "
|
||||
@@ -53,9 +54,6 @@ DEFINE_string(ranger_app_id, "",
|
||||
"required when authorization with Ranger is enabled.");
|
||||
DEFINE_string(server_name, "", "The name to use for securing this impalad "
|
||||
"server during authorization. Set to enable authorization.");
|
||||
DEFINE_string(authorization_policy_provider_class,
|
||||
"org.apache.sentry.provider.common.HadoopGroupResourceAuthorizationProvider",
|
||||
"Advanced: The authorization policy provider class name for Sentry.");
|
||||
DEFINE_string(authorized_proxy_user_config, "",
|
||||
"Specifies the set of authorized proxy users (users who can delegate to other "
|
||||
"users during authorization) and whom they are allowed to delegate. "
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
#include "common/names.h"
|
||||
|
||||
DEFINE_bool(force_lowercase_usernames, false, "If true, all principals and usernames are"
|
||||
" mapped to lowercase shortnames before being passed to any components (Sentry, "
|
||||
" mapped to lowercase shortnames before being passed to any components (Ranger, "
|
||||
"admission control) for authorization");
|
||||
|
||||
using boost::algorithm::is_any_of;
|
||||
|
||||
@@ -38,19 +38,16 @@ DECLARE_int32(max_hdfs_partitions_parallel_load);
|
||||
DECLARE_int32(max_nonhdfs_partitions_parallel_load);
|
||||
DECLARE_int32(initial_hms_cnxn_timeout_s);
|
||||
DECLARE_int32(kudu_operation_timeout_ms);
|
||||
DECLARE_int64(sentry_catalog_polling_frequency_s);
|
||||
DECLARE_int64(inc_stats_size_limit_bytes);
|
||||
DECLARE_string(principal);
|
||||
DECLARE_string(lineage_event_log_dir);
|
||||
DECLARE_string(principal);
|
||||
DECLARE_string(local_library_dir);
|
||||
DECLARE_string(server_name);
|
||||
DECLARE_string(authorization_policy_provider_class);
|
||||
DECLARE_string(authorized_proxy_group_config);
|
||||
DECLARE_string(catalog_topic_mode);
|
||||
DECLARE_string(kudu_master_hosts);
|
||||
DECLARE_string(reserved_words_version);
|
||||
DECLARE_string(sentry_config);
|
||||
DECLARE_double(max_filter_error_rate);
|
||||
DECLARE_int64(min_buffer_size);
|
||||
DECLARE_bool(disable_catalog_data_ops_debug_only);
|
||||
@@ -100,9 +97,6 @@ Status GetThriftBackendGflags(JNIEnv* jni_env, jbyteArray* cfg_bytes) {
|
||||
cfg.__set_local_catalog_cache_expiration_s(
|
||||
FLAGS_local_catalog_cache_expiration_s);
|
||||
cfg.__set_server_name(FLAGS_server_name);
|
||||
cfg.__set_sentry_config(FLAGS_sentry_config);
|
||||
cfg.__set_authorization_policy_provider_class(
|
||||
FLAGS_authorization_policy_provider_class);
|
||||
cfg.__set_kudu_master_hosts(FLAGS_kudu_master_hosts);
|
||||
cfg.__set_read_size(FLAGS_read_size);
|
||||
cfg.__set_num_metadata_loading_threads(FLAGS_num_metadata_loading_threads);
|
||||
@@ -110,7 +104,6 @@ Status GetThriftBackendGflags(JNIEnv* jni_env, jbyteArray* cfg_bytes) {
|
||||
cfg.__set_max_nonhdfs_partitions_parallel_load(
|
||||
FLAGS_max_nonhdfs_partitions_parallel_load);
|
||||
cfg.__set_initial_hms_cnxn_timeout_s(FLAGS_initial_hms_cnxn_timeout_s);
|
||||
cfg.__set_sentry_config(FLAGS_sentry_config);
|
||||
// auth_to_local rules are read if --load_auth_to_local_rules is set to true
|
||||
// and impala is kerberized.
|
||||
cfg.__set_load_auth_to_local_rules(FLAGS_load_auth_to_local_rules);
|
||||
@@ -122,7 +115,6 @@ Status GetThriftBackendGflags(JNIEnv* jni_env, jbyteArray* cfg_bytes) {
|
||||
cfg.__set_lineage_event_log_dir(FLAGS_lineage_event_log_dir);
|
||||
cfg.__set_local_library_path(FLAGS_local_library_dir);
|
||||
cfg.__set_kudu_operation_timeout_ms(FLAGS_kudu_operation_timeout_ms);
|
||||
cfg.__set_sentry_catalog_polling_frequency_s(FLAGS_sentry_catalog_polling_frequency_s);
|
||||
if (FLAGS_reserved_words_version == "2.11.0") {
|
||||
cfg.__set_reserved_words_version(TReservedWordsVersion::IMPALA_2_11);
|
||||
} else {
|
||||
|
||||
@@ -28,16 +28,14 @@
|
||||
# override this to share a toolchain directory between multiple checkouts of Impala
|
||||
# or to use a cached copy to avoid downloading a new one.
|
||||
# IMPALA_TOOLCHAIN_HOST - The host to use for downloading the artifacts
|
||||
# CDH_COMPONENTS_HOME - Directory to store CDH Hadoop component artifacts
|
||||
# CDP_COMPONENTS_HOME - Directory to store CDP Hadoop component artifacts
|
||||
# CDH_BUILD_NUMBER - CDH Hadoop components are built with consistent versions so that
|
||||
# CDP_BUILD_NUMBER - CDP Hadoop components are built with consistent versions so that
|
||||
# Hadoop, Hive, Kudu, etc are all built with versions that are compatible with each
|
||||
# other. The way to specify a single consistent set of components is via a build
|
||||
# number. This determines the location in s3 to get the artifacts.
|
||||
# CDP_BUILD_NUMBER - The CDP equivalent of a CDH_BUILD_NUMBER.
|
||||
# DOWNLOAD_CDH_COMPONENTS - When set to true, this script will also download and extract
|
||||
# the CDH/CDP Hadoop components (i.e. Hadoop, Hive, HBase, Sentry, Ranger, etc) into
|
||||
# CDH_COMPONENTS_HOME/CDP_COMPONENTS_HOME as appropriate.
|
||||
# the CDP Hadoop components (i.e. Hadoop, Hive, HBase, Ranger, etc) into
|
||||
# CDP_COMPONENTS_HOME as appropriate.
|
||||
# KUDU_IS_SUPPORTED - If KUDU_IS_SUPPORTED is false, Kudu is disabled and we download
|
||||
# the toolchain Kudu and use the symbols to compile a non-functional stub library so
|
||||
# that Impala has something to link against.
|
||||
@@ -69,7 +67,7 @@ from collections import namedtuple
|
||||
from string import Template
|
||||
|
||||
# Maps return values from 'lsb_release -irs' to the corresponding OS labels for both the
|
||||
# toolchain and the CDH components.
|
||||
# toolchain and the CDP components.
|
||||
OsMapping = namedtuple('OsMapping', ['lsb_release', 'toolchain', 'cdh'])
|
||||
OS_MAPPING = [
|
||||
OsMapping("centos5", "ec2-package-centos-5", None),
|
||||
@@ -294,32 +292,10 @@ class ToolchainPackage(EnvVersionedPackage):
|
||||
f.write(self.archive_basename)
|
||||
|
||||
|
||||
class CdhComponent(EnvVersionedPackage):
|
||||
def __init__(self, name, explicit_version=None, archive_basename_tmpl=None,
|
||||
unpack_directory_tmpl=None):
|
||||
# Compute the CDH base URL (based on the IMPALA_TOOLCHAIN_HOST and CDH_BUILD_NUMBER)
|
||||
if "IMPALA_TOOLCHAIN_HOST" not in os.environ or "CDH_BUILD_NUMBER" not in os.environ:
|
||||
logging.error("Impala environment not set up correctly, make sure "
|
||||
"impala-config.sh is sourced.")
|
||||
sys.exit(1)
|
||||
template_subs = {"toolchain_host": os.environ["IMPALA_TOOLCHAIN_HOST"],
|
||||
"cdh_build_number": os.environ["CDH_BUILD_NUMBER"]}
|
||||
url_prefix_tmpl = "https://${toolchain_host}/build/cdh_components/" + \
|
||||
"${cdh_build_number}/tarballs/"
|
||||
|
||||
# Get the output base directory from CDH_COMPONENTS_HOME
|
||||
destination_basedir = os.environ["CDH_COMPONENTS_HOME"]
|
||||
super(CdhComponent, self).__init__(name, url_prefix_tmpl, destination_basedir,
|
||||
explicit_version=explicit_version,
|
||||
archive_basename_tmpl=archive_basename_tmpl,
|
||||
unpack_directory_tmpl=unpack_directory_tmpl,
|
||||
template_subs_in=template_subs)
|
||||
|
||||
|
||||
class CdpComponent(EnvVersionedPackage):
|
||||
def __init__(self, name, explicit_version=None, archive_basename_tmpl=None,
|
||||
unpack_directory_tmpl=None, makedir=False):
|
||||
# Compute the CDH base URL (based on the IMPALA_TOOLCHAIN_HOST and CDP_BUILD_NUMBER)
|
||||
# Compute the CDP base URL (based on the IMPALA_TOOLCHAIN_HOST and CDP_BUILD_NUMBER)
|
||||
if "IMPALA_TOOLCHAIN_HOST" not in os.environ or "CDP_BUILD_NUMBER" not in os.environ:
|
||||
logging.error("Impala environment not set up correctly, make sure "
|
||||
"impala-config.sh is sourced.")
|
||||
@@ -592,8 +568,6 @@ def get_hadoop_downloads():
|
||||
tez = CdpComponent("tez", archive_basename_tmpl="tez-${version}-minimal",
|
||||
makedir=True)
|
||||
cluster_components.extend([hadoop, hbase, hive, hive_src, tez])
|
||||
# Sentry is always CDH
|
||||
cluster_components.append(CdhComponent("sentry"))
|
||||
# Ranger is always CDP
|
||||
cluster_components.append(CdpComponent("ranger",
|
||||
archive_basename_tmpl="ranger-${version}-admin"))
|
||||
@@ -620,16 +594,12 @@ def main():
|
||||
compute what packages need to be downloaded. Packages are only downloaded if they are
|
||||
not already present in $IMPALA_TOOLCHAIN. There are two main categories of packages.
|
||||
Toolchain packages are native packages built using the native toolchain. These are
|
||||
always downloaded. Hadoop component packages are the CDH or CDP builds of Hadoop
|
||||
always downloaded. Hadoop component packages are the CDP builds of Hadoop
|
||||
components such as Hadoop, Hive, HBase, etc. Hadoop component packages are organized
|
||||
as a consistent set of compatible version via a build number (i.e. CDH_BUILD_NUMBER
|
||||
and CDP_BUILD_NUMBER). Hadoop component packages are only downloaded if
|
||||
$DOWNLOAD_CDH_COMPONENTS is true. CDH Hadoop packages are downloaded into
|
||||
$CDH_COMPONENTS_HOME. CDP Hadoop packages are downloaded into $CDP_COMPONENTS_HOME.
|
||||
as a consistent set of compatible version via a build number (i.e. CDP_BUILD_NUMBER)
|
||||
Hadoop component packages are only downloaded if $DOWNLOAD_CDH_COMPONENTS is true.
|
||||
The versions used for Hadoop components come from the CDP versions based on the
|
||||
$CDP_BUILD_NUMBER.
|
||||
The exceptions is:
|
||||
- sentry (always downloaded from $IMPALA_TOOLCHAIN_HOST for a given $CDH_BUILD_NUMBER)
|
||||
$CDP_BUILD_NUMBER. CDP Hadoop packages are downloaded into $CDP_COMPONENTS_HOME.
|
||||
If Kudu is not supported on this platform (or KUDU_IS_SUPPORTED=false), then this
|
||||
builds a Kudu stub to allow for compilation without Kudu support.
|
||||
"""
|
||||
@@ -652,7 +622,6 @@ def main():
|
||||
downloads += get_toolchain_downloads()
|
||||
kudu_download = None
|
||||
if os.getenv("DOWNLOAD_CDH_COMPONENTS", "false") == "true":
|
||||
create_directory_from_env_var("CDH_COMPONENTS_HOME")
|
||||
create_directory_from_env_var("CDP_COMPONENTS_HOME")
|
||||
downloads += get_kudu_downloads(use_kudu_stub)
|
||||
downloads += get_hadoop_downloads()
|
||||
|
||||
@@ -65,7 +65,6 @@ function generate_config {
|
||||
}
|
||||
|
||||
CREATE_METASTORE=0
|
||||
CREATE_SENTRY_POLICY_DB=0
|
||||
CREATE_RANGER_POLICY_DB=0
|
||||
|
||||
# parse command line options
|
||||
@@ -75,15 +74,11 @@ do
|
||||
-create_metastore)
|
||||
CREATE_METASTORE=1
|
||||
;;
|
||||
-create_sentry_policy_db)
|
||||
CREATE_SENTRY_POLICY_DB=1
|
||||
;;
|
||||
-create_ranger_policy_db)
|
||||
CREATE_RANGER_POLICY_DB=1
|
||||
;;
|
||||
-help|*)
|
||||
echo "[-create_metastore] : If true, creates a new metastore."
|
||||
echo "[-create_sentry_policy_db] : If true, creates a new sentry policy db."
|
||||
echo "[-create_ranger_policy_db] : If true, creates a new Ranger policy db."
|
||||
exit 1
|
||||
;;
|
||||
@@ -122,7 +117,6 @@ RANGER_TEST_CONF_DIR="${IMPALA_HOME}/testdata/cluster/ranger"
|
||||
echo "Config dir: ${CONFIG_DIR}"
|
||||
echo "Current user: ${CURRENT_USER}"
|
||||
echo "Metastore DB: ${METASTORE_DB}"
|
||||
echo "Sentry DB : ${SENTRY_POLICY_DB}"
|
||||
echo "Ranger DB : ${RANGER_POLICY_DB}"
|
||||
|
||||
pushd ${CONFIG_DIR}
|
||||
@@ -175,12 +169,6 @@ postgres 1>${IMPALA_CLUSTER_LOGS_DIR}/schematool.log 2>&1
|
||||
| psql -q -U hiveuser -d ${METASTORE_DB}
|
||||
fi
|
||||
|
||||
if [ $CREATE_SENTRY_POLICY_DB -eq 1 ]; then
|
||||
echo "Creating Sentry Policy Server DB"
|
||||
dropdb -U hiveuser $SENTRY_POLICY_DB 2> /dev/null || true
|
||||
createdb -U hiveuser $SENTRY_POLICY_DB
|
||||
fi
|
||||
|
||||
if [ $CREATE_RANGER_POLICY_DB -eq 1 ]; then
|
||||
echo "Creating Ranger Policy Server DB"
|
||||
dropdb -U hiveuser "${RANGER_POLICY_DB}" 2> /dev/null || true
|
||||
@@ -215,13 +203,6 @@ fi
|
||||
generate_config log4j.properties.template log4j.properties
|
||||
generate_config hbase-site.xml.template hbase-site.xml
|
||||
|
||||
$IMPALA_HOME/bin/generate_xml_config.py sentry-site.xml.py sentry-site.xml
|
||||
for SENTRY_VARIANT in oo oo_nogrant no_oo ; do
|
||||
export SENTRY_VARIANT
|
||||
$IMPALA_HOME/bin/generate_xml_config.py sentry-site.xml.py \
|
||||
sentry-site_${SENTRY_VARIANT}.xml
|
||||
done
|
||||
|
||||
if [[ "${IMPALA_KERBERIZE}" = "true" ]]; then
|
||||
generate_config hbase-jaas-server.conf.template hbase-jaas-server.conf
|
||||
generate_config hbase-jaas-client.conf.template hbase-jaas-client.conf
|
||||
|
||||
@@ -171,7 +171,6 @@ export IMPALA_TOOLCHAIN_HOST
|
||||
export CDH_BUILD_NUMBER=1814051
|
||||
export CDH_MAVEN_REPOSITORY=\
|
||||
"https://${IMPALA_TOOLCHAIN_HOST}/build/cdh_components/${CDH_BUILD_NUMBER}/maven"
|
||||
export CDH_SENTRY_VERSION=2.1.0-cdh6.x-SNAPSHOT
|
||||
|
||||
export CDP_BUILD_NUMBER=2523282
|
||||
export CDP_MAVEN_REPOSITORY=\
|
||||
@@ -179,10 +178,10 @@ export CDP_MAVEN_REPOSITORY=\
|
||||
export CDP_HADOOP_VERSION=3.1.1.7.1.1.0-380
|
||||
export CDP_HBASE_VERSION=2.2.3.7.1.1.0-380
|
||||
export CDP_HIVE_VERSION=3.1.3000.7.1.1.0-380
|
||||
export CDP_RANGER_VERSION=2.0.0.7.1.1.0-380
|
||||
export CDP_TEZ_VERSION=0.9.1.7.1.1.0-380
|
||||
export CDP_KNOX_VERSION=1.3.0.7.1.1.0-380
|
||||
export CDP_OZONE_VERSION=0.4.0.7.1.1.0-380
|
||||
export CDP_RANGER_VERSION=2.0.0.7.1.1.0-380
|
||||
export CDP_TEZ_VERSION=0.9.1.7.1.1.0-380
|
||||
|
||||
export IMPALA_PARQUET_VERSION=1.10.99-cdh6.x-SNAPSHOT
|
||||
export IMPALA_AVRO_JAVA_VERSION=1.8.2-cdh6.x-SNAPSHOT
|
||||
@@ -198,7 +197,6 @@ unset IMPALA_HIVE_URL
|
||||
unset IMPALA_KUDU_URL
|
||||
unset IMPALA_KUDU_VERSION
|
||||
unset IMPALA_KUDU_JAVA_VERSION
|
||||
unset IMPALA_SENTRY_URL
|
||||
|
||||
export IMPALA_KERBERIZE=false
|
||||
|
||||
@@ -213,41 +211,29 @@ if [ -f "$IMPALA_HOME/bin/impala-config-local.sh" ]; then
|
||||
. "$IMPALA_HOME/bin/impala-config-local.sh"
|
||||
fi
|
||||
|
||||
export CDH_SENTRY_URL=${CDH_SENTRY_URL-}
|
||||
|
||||
export CDP_HIVE_URL=${CDP_HIVE_URL-}
|
||||
export CDP_HIVE_SOURCE_URL=${CDP_HIVE_SOURCE_URL-}
|
||||
export CDP_HADOOP_URL=${CDP_HADOOP_URL-}
|
||||
export CDP_HBASE_URL=${CDP_HBASE_URL-}
|
||||
export CDP_TEZ_URL=${CDP_TEZ_URL-}
|
||||
export CDP_HIVE_URL=${CDP_HIVE_URL-}
|
||||
export CDP_HIVE_SOURCE_URL=${CDP_HIVE_SOURCE_URL-}
|
||||
export CDP_RANGER_URL=${CDP_RANGER_URL-}
|
||||
export CDP_TEZ_URL=${CDP_TEZ_URL-}
|
||||
|
||||
export CDH_COMPONENTS_HOME="$IMPALA_TOOLCHAIN/cdh_components-$CDH_BUILD_NUMBER"
|
||||
export CDP_COMPONENTS_HOME="$IMPALA_TOOLCHAIN/cdp_components-$CDP_BUILD_NUMBER"
|
||||
export DISABLE_SENTRY=${DISABLE_SENTRY_OVERRIDE:-"true"}
|
||||
export CDH_MAJOR_VERSION=7
|
||||
export IMPALA_HADOOP_VERSION=${CDP_HADOOP_VERSION}
|
||||
export IMPALA_HADOOP_URL=${CDP_HADOOP_URL-}
|
||||
export HADOOP_HOME="$CDP_COMPONENTS_HOME/hadoop-${IMPALA_HADOOP_VERSION}/"
|
||||
export IMPALA_HBASE_VERSION=${CDP_HBASE_VERSION}
|
||||
export IMPALA_HBASE_URL=${CDP_HBASE_URL-}
|
||||
export IMPALA_HIVE_VERSION=${CDP_HIVE_VERSION}
|
||||
export IMPALA_HIVE_URL=${CDP_HIVE_URL-}
|
||||
export IMPALA_HIVE_SOURCE_URL=${CDP_HIVE_SOURCE_URL-}
|
||||
export IMPALA_HADOOP_VERSION=${CDP_HADOOP_VERSION}
|
||||
export IMPALA_HADOOP_URL=${CDP_HADOOP_URL-}
|
||||
export IMPALA_HBASE_VERSION=${CDP_HBASE_VERSION}
|
||||
export IMPALA_HBASE_URL=${CDP_HBASE_URL-}
|
||||
export IMPALA_TEZ_VERSION=${CDP_TEZ_VERSION}
|
||||
export IMPALA_TEZ_URL=${CDP_TEZ_URL-}
|
||||
export IMPALA_KNOX_VERSION=${CDP_KNOX_VERSION}
|
||||
export HADOOP_HOME="$CDP_COMPONENTS_HOME/hadoop-${IMPALA_HADOOP_VERSION}/"
|
||||
|
||||
# Ozone always uses the CDP version
|
||||
export IMPALA_OZONE_VERSION=${CDP_OZONE_VERSION}
|
||||
|
||||
# Ranger always uses the CDP version
|
||||
export IMPALA_RANGER_VERSION=${CDP_RANGER_VERSION}
|
||||
export IMPALA_RANGER_URL=${CDP_RANGER_URL-}
|
||||
|
||||
# Sentry always uses the CDH version
|
||||
export IMPALA_SENTRY_VERSION=${CDH_SENTRY_VERSION}
|
||||
export IMPALA_SENTRY_URL=${CDH_SENTRY_URL-}
|
||||
export IMPALA_TEZ_VERSION=${CDP_TEZ_VERSION}
|
||||
export IMPALA_TEZ_URL=${CDP_TEZ_URL-}
|
||||
|
||||
# Extract the first component of the hive version.
|
||||
# Allow overriding of Hive source location in case we want to build Impala without
|
||||
@@ -374,13 +360,6 @@ export METASTORE_DB=${METASTORE_DB-"$(cut -c-59 <<< HMS$ESCAPED_IMPALA_HOME)_cdp
|
||||
# Set the Hive binaries in the path
|
||||
export PATH="$HIVE_HOME/bin:$PATH"
|
||||
|
||||
export SENTRY_POLICY_DB=${SENTRY_POLICY_DB-$(cut -c-63 <<< SP$ESCAPED_IMPALA_HOME)}
|
||||
if [[ "${TARGET_FILESYSTEM}" == "s3" ]]; then
|
||||
# On S3, disable Sentry HDFS sync plugin.
|
||||
export SENTRY_PROCESSOR_FACTORIES="org.apache.sentry.api.service.thrift.SentryPolicyStoreProcessorFactory"
|
||||
else
|
||||
export SENTRY_PROCESSOR_FACTORIES="org.apache.sentry.api.service.thrift.SentryPolicyStoreProcessorFactory,org.apache.sentry.hdfs.SentryHDFSServiceProcessorFactory"
|
||||
fi
|
||||
RANGER_POLICY_DB=${RANGER_POLICY_DB-$(cut -c-63 <<< ranger$ESCAPED_IMPALA_HOME)}
|
||||
# The DB script in Ranger expects the database name to be in lower case.
|
||||
export RANGER_POLICY_DB=$(echo ${RANGER_POLICY_DB} | tr '[:upper:]' '[:lower:]')
|
||||
@@ -596,9 +575,6 @@ HADOOP_CLASSPATH="${HADOOP_CLASSPATH}:${HADOOP_HOME}/share/hadoop/tools/lib/*"
|
||||
|
||||
export PATH="$HADOOP_HOME/bin:$PATH"
|
||||
|
||||
export SENTRY_HOME="$CDH_COMPONENTS_HOME/sentry-${IMPALA_SENTRY_VERSION}"
|
||||
export SENTRY_CONF_DIR="$IMPALA_HOME/fe/src/test/resources"
|
||||
|
||||
export RANGER_HOME="${CDP_COMPONENTS_HOME}/ranger-${IMPALA_RANGER_VERSION}-admin"
|
||||
export RANGER_CONF_DIR="$IMPALA_HOME/fe/src/test/resources"
|
||||
|
||||
@@ -621,7 +597,7 @@ export HIVE_CONF_DIR="$IMPALA_FE_DIR/./src/test/resources"
|
||||
# Hive looks for jar files in a single directory from HIVE_AUX_JARS_PATH plus
|
||||
# any jars in AUX_CLASSPATH. (Or a list of jars in HIVE_AUX_JARS_PATH.)
|
||||
# The Postgres JDBC driver is downloaded by maven when building the frontend.
|
||||
# Export the location of Postgres JDBC driver so Sentry can pick it up.
|
||||
# Export the location of Postgres JDBC driver so Ranger can pick it up.
|
||||
export POSTGRES_JDBC_DRIVER="${IMPALA_FE_DIR}/target/dependency/postgresql-${IMPALA_POSTGRES_JDBC_DRIVER_VERSION}.jar"
|
||||
|
||||
export HIVE_AUX_JARS_PATH="$POSTGRES_JDBC_DRIVER"
|
||||
@@ -737,8 +713,6 @@ echo "HIVE_CONF_DIR = $HIVE_CONF_DIR"
|
||||
echo "HIVE_SRC_DIR = $HIVE_SRC_DIR"
|
||||
echo "HBASE_HOME = $HBASE_HOME"
|
||||
echo "HBASE_CONF_DIR = $HBASE_CONF_DIR"
|
||||
echo "SENTRY_HOME = $SENTRY_HOME"
|
||||
echo "SENTRY_CONF_DIR = $SENTRY_CONF_DIR"
|
||||
echo "RANGER_HOME = $RANGER_HOME"
|
||||
echo "RANGER_CONF_DIR = $RANGER_CONF_DIR "
|
||||
echo "THRIFT_HOME = $THRIFT_HOME"
|
||||
@@ -754,14 +728,12 @@ echo "DOWNLOAD_CDH_COMPONENTS = $DOWNLOAD_CDH_COMPONENTS"
|
||||
echo "IMPALA_MAVEN_OPTIONS = $IMPALA_MAVEN_OPTIONS"
|
||||
echo "IMPALA_TOOLCHAIN_HOST = $IMPALA_TOOLCHAIN_HOST"
|
||||
echo "CDH_BUILD_NUMBER = $CDH_BUILD_NUMBER"
|
||||
echo "CDH_COMPONENTS_HOME = $CDH_COMPONENTS_HOME"
|
||||
echo "CDP_BUILD_NUMBER = $CDP_BUILD_NUMBER"
|
||||
echo "CDP_COMPONENTS_HOME = $CDP_COMPONENTS_HOME"
|
||||
echo "IMPALA_HADOOP_VERSION = $IMPALA_HADOOP_VERSION"
|
||||
echo "IMPALA_HIVE_VERSION = $IMPALA_HIVE_VERSION"
|
||||
echo "IMPALA_HBASE_VERSION = $IMPALA_HBASE_VERSION"
|
||||
echo "IMPALA_HUDI_VERSION = $IMPALA_HUDI_VERSION"
|
||||
echo "IMPALA_SENTRY_VERSION = $IMPALA_SENTRY_VERSION"
|
||||
echo "IMPALA_KUDU_VERSION = $IMPALA_KUDU_VERSION"
|
||||
echo "IMPALA_KUDU_JAVA_VERSION= $IMPALA_KUDU_JAVA_VERSION"
|
||||
echo "IMPALA_RANGER_VERSION = $IMPALA_RANGER_VERSION"
|
||||
|
||||
18
buildall.sh
18
buildall.sh
@@ -58,7 +58,6 @@ TESTDATA_ACTION=0
|
||||
TESTS_ACTION=1
|
||||
FORMAT_CLUSTER=0
|
||||
FORMAT_METASTORE=0
|
||||
FORMAT_SENTRY_POLICY_DB=0
|
||||
FORMAT_RANGER_POLICY_DB=0
|
||||
NEED_MINICLUSTER=0
|
||||
START_IMPALA_CLUSTER=0
|
||||
@@ -107,7 +106,6 @@ do
|
||||
-format)
|
||||
FORMAT_CLUSTER=1
|
||||
FORMAT_METASTORE=1
|
||||
FORMAT_SENTRY_POLICY_DB=1
|
||||
FORMAT_RANGER_POLICY_DB=1
|
||||
;;
|
||||
-format_cluster)
|
||||
@@ -116,9 +114,6 @@ do
|
||||
-format_metastore)
|
||||
FORMAT_METASTORE=1
|
||||
;;
|
||||
-format_sentry_policy_db)
|
||||
FORMAT_SENTRY_POLICY_DB=1
|
||||
;;
|
||||
-format_ranger_policy_db)
|
||||
FORMAT_RANGER_POLICY_DB=1
|
||||
;;
|
||||
@@ -202,11 +197,10 @@ do
|
||||
echo "buildall.sh - Builds Impala and runs all tests."
|
||||
echo "[-noclean] : Omits cleaning all packages before building. Will not kill"\
|
||||
"running Hadoop services unless any -format* is True"
|
||||
echo "[-format] : Format the minicluster, metastore db, and sentry policy db"\
|
||||
echo "[-format] : Format the minicluster, metastore db, and ranger policy db"\
|
||||
"[Default: False]"
|
||||
echo "[-format_cluster] : Format the minicluster [Default: False]"
|
||||
echo "[-format_metastore] : Format the metastore db [Default: False]"
|
||||
echo "[-format_sentry_policy_db] : Format the Sentry policy db [Default: False]"
|
||||
echo "[-format_ranger_policy_db] : Format the Ranger policy db [Default: False]"
|
||||
echo "[-release_and_debug] : Build both release and debug binaries. Overrides "\
|
||||
"other build types [Default: false]"
|
||||
@@ -354,8 +348,7 @@ if [[ -z "$METASTORE_SNAPSHOT_FILE" && "${TARGET_FILESYSTEM}" != "hdfs" &&
|
||||
fi
|
||||
|
||||
if [[ $TESTS_ACTION -eq 1 || $TESTDATA_ACTION -eq 1 || $FORMAT_CLUSTER -eq 1 ||
|
||||
$FORMAT_METASTORE -eq 1 || $FORMAT_SENTRY_POLICY_DB -eq 1 ||
|
||||
$FORMAT_RANGER_POLICY_DB -eq 1 || -n "$SNAPSHOT_FILE" ||
|
||||
$FORMAT_METASTORE -eq 1 || $FORMAT_RANGER_POLICY_DB -eq 1 || -n "$SNAPSHOT_FILE" ||
|
||||
-n "$METASTORE_SNAPSHOT_FILE" ]]; then
|
||||
NEED_MINICLUSTER=1
|
||||
fi
|
||||
@@ -493,8 +486,7 @@ reconfigure_test_cluster() {
|
||||
"${IMPALA_HOME}/bin/start-impala-cluster.py" --kill --force
|
||||
|
||||
if [[ "$FORMAT_METASTORE" -eq 1 || "$FORMAT_CLUSTER" -eq 1 ||
|
||||
"$FORMAT_SENTRY_POLICY_DB" -eq 1 || "$FORMAT_RANGER_POLICY_DB" -eq 1 ||
|
||||
-n "$METASTORE_SNAPSHOT_FILE" ]]
|
||||
"$FORMAT_RANGER_POLICY_DB" -eq 1 || -n "$METASTORE_SNAPSHOT_FILE" ]]
|
||||
then
|
||||
# Kill any processes that may be accessing postgres metastore. To be safe, this is
|
||||
# done before we make any changes to the config files.
|
||||
@@ -502,10 +494,6 @@ reconfigure_test_cluster() {
|
||||
fi
|
||||
|
||||
local CREATE_TEST_CONFIG_ARGS=""
|
||||
if [[ "$FORMAT_SENTRY_POLICY_DB" -eq 1 ]]; then
|
||||
CREATE_TEST_CONFIG_ARGS+=" -create_sentry_policy_db"
|
||||
fi
|
||||
|
||||
if [[ "$FORMAT_RANGER_POLICY_DB" -eq 1 ]]; then
|
||||
CREATE_TEST_CONFIG_ARGS+=" -create_ranger_policy_db"
|
||||
fi
|
||||
|
||||
@@ -26,7 +26,7 @@ enum TReservedWordsVersion {
|
||||
// Used to pass gflags from backend to frontend, JniCatalog and JniFrontend
|
||||
// Attributes without comments correspond to gflags
|
||||
struct TBackendGflags {
|
||||
1: required string sentry_config
|
||||
// REMOVED: 1: required string sentry_config
|
||||
|
||||
2: required bool load_auth_to_local_rules
|
||||
|
||||
@@ -48,7 +48,7 @@ struct TBackendGflags {
|
||||
|
||||
11: required string server_name
|
||||
|
||||
12: required string authorization_policy_provider_class
|
||||
// REMOVED: 12: required string authorization_policy_provider_class
|
||||
|
||||
13: required string kudu_master_hosts
|
||||
|
||||
@@ -62,7 +62,7 @@ struct TBackendGflags {
|
||||
|
||||
18: required bool enable_stats_extrapolation
|
||||
|
||||
19: required i64 sentry_catalog_polling_frequency_s
|
||||
// REMOVED: 19: required i64 sentry_catalog_polling_frequency_s
|
||||
|
||||
20: required i32 max_hdfs_partitions_parallel_load
|
||||
|
||||
|
||||
@@ -507,23 +507,6 @@ struct TPrioritizeLoadResponse {
|
||||
1: required Status.TStatus status
|
||||
}
|
||||
|
||||
// Request to perform a privilege check with the Sentry Service to determine
|
||||
// if the requesting user is a Sentry Service admin.
|
||||
struct TSentryAdminCheckRequest {
|
||||
1: required CatalogServiceVersion protocol_version = CatalogServiceVersion.V1
|
||||
|
||||
// Common header included in all CatalogService requests.
|
||||
2: optional TCatalogServiceRequestHeader header
|
||||
}
|
||||
|
||||
struct TSentryAdminCheckResponse {
|
||||
// Returns OK if the operation was successful.
|
||||
1: optional Status.TStatus status
|
||||
|
||||
// Returns true if the user is a Sentry admin user.
|
||||
2: required bool is_admin
|
||||
}
|
||||
|
||||
struct TTableUsage {
|
||||
1: required CatalogObjects.TTableName table_name
|
||||
// count of usages since the last report
|
||||
@@ -566,13 +549,6 @@ service CatalogService {
|
||||
// TPrioritizeLoadRequest.
|
||||
TPrioritizeLoadResponse PrioritizeLoad(1: TPrioritizeLoadRequest req);
|
||||
|
||||
// Performs a check with the Sentry Service to determine if the requesting user
|
||||
// is configured as an admin on the Sentry Service. This API may be removed in
|
||||
// the future and external clients should not rely on using it.
|
||||
// TODO: When Sentry Service has a better mechanism to perform these changes this API
|
||||
// should be deprecated.
|
||||
TSentryAdminCheckResponse SentryAdminCheck(1: TSentryAdminCheckRequest req);
|
||||
|
||||
// Fetch partial information about some object in the catalog.
|
||||
TGetPartialCatalogObjectResponse GetPartialCatalogObject(
|
||||
1: TGetPartialCatalogObjectRequest req);
|
||||
|
||||
173
fe/pom.xml
173
fe/pom.xml
@@ -237,175 +237,6 @@ under the License.
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.sentry</groupId>
|
||||
<artifactId>sentry-core-common</artifactId>
|
||||
<version>${sentry.version}</version>
|
||||
<exclusions>
|
||||
<!-- https://issues.apache.org/jira/browse/IMPALA-9649 -->
|
||||
<exclusion>
|
||||
<groupId>org.apache.shiro</groupId>
|
||||
<artifactId>shiro-crypto-cipher</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.apache.shiro</groupId>
|
||||
<artifactId>shiro-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.sentry</groupId>
|
||||
<artifactId>sentry-core-model-db</artifactId>
|
||||
<version>${sentry.version}</version>
|
||||
<exclusions>
|
||||
<!-- https://issues.apache.org/jira/browse/HADOOP-14903 -->
|
||||
<exclusion>
|
||||
<groupId>net.minidev</groupId>
|
||||
<artifactId>json-smart</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.sentry</groupId>
|
||||
<artifactId>sentry-provider-common</artifactId>
|
||||
<version>${sentry.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.sentry</groupId>
|
||||
<artifactId>sentry-provider-db</artifactId>
|
||||
<version>${sentry.version}</version>
|
||||
<exclusions>
|
||||
<!-- https://issues.apache.org/jira/browse/IMPALA-9649 -->
|
||||
<exclusion>
|
||||
<groupId>org.apache.shiro</groupId>
|
||||
<artifactId>shiro-crypto-cipher</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.apache.shiro</groupId>
|
||||
<artifactId>shiro-core</artifactId>
|
||||
</exclusion>
|
||||
|
||||
<!-- https://issues.apache.org/jira/browse/HADOOP-14903 -->
|
||||
<exclusion>
|
||||
<groupId>net.minidev</groupId>
|
||||
<artifactId>json-smart</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.apache.hive.hcatalog</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.apache.hive</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.apache.hadoop</groupId>
|
||||
<artifactId>hadoop-mapreduce-client-jobclient</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>ant-contrib</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.sentry</groupId>
|
||||
<artifactId>sentry-provider-file</artifactId>
|
||||
<version>${sentry.version}</version>
|
||||
<exclusions>
|
||||
<!-- https://issues.apache.org/jira/browse/IMPALA-9649 -->
|
||||
<exclusion>
|
||||
<groupId>org.apache.shiro</groupId>
|
||||
<artifactId>shiro-crypto-cipher</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.apache.shiro</groupId>
|
||||
<artifactId>shiro-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.sentry</groupId>
|
||||
<artifactId>sentry-provider-cache</artifactId>
|
||||
<version>${sentry.version}</version>
|
||||
<exclusions>
|
||||
<!-- https://issues.apache.org/jira/browse/IMPALA-9649 -->
|
||||
<exclusion>
|
||||
<groupId>org.apache.shiro</groupId>
|
||||
<artifactId>shiro-crypto-cipher</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.apache.shiro</groupId>
|
||||
<artifactId>shiro-core</artifactId>
|
||||
</exclusion>
|
||||
|
||||
<!-- https://issues.apache.org/jira/browse/HADOOP-14903 -->
|
||||
<exclusion>
|
||||
<groupId>net.minidev</groupId>
|
||||
<artifactId>json-smart</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>ant-contrib</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.sentry</groupId>
|
||||
<artifactId>sentry-policy-common</artifactId>
|
||||
<version>${sentry.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.sentry</groupId>
|
||||
<artifactId>sentry-binding-hive</artifactId>
|
||||
<version>${sentry.version}</version>
|
||||
<exclusions>
|
||||
<!-- https://issues.apache.org/jira/browse/HADOOP-14903 -->
|
||||
<exclusion>
|
||||
<groupId>net.minidev</groupId>
|
||||
<artifactId>json-smart</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.apache.hive.hcatalog</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.sentry</groupId>
|
||||
<artifactId>sentry-policy-engine</artifactId>
|
||||
<version>${sentry.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.sentry</groupId>
|
||||
<artifactId>sentry-service-api</artifactId>
|
||||
<version>${sentry.version}</version>
|
||||
<exclusions>
|
||||
<!-- https://issues.apache.org/jira/browse/HADOOP-14903 -->
|
||||
<exclusion>
|
||||
<groupId>net.minidev</groupId>
|
||||
<artifactId>json-smart</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.apache.hive</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.parquet</groupId>
|
||||
<artifactId>parquet-hadoop-bundle</artifactId>
|
||||
@@ -858,13 +689,14 @@ under the License.
|
||||
<exclude>com.sun.jersey:jersey-server</exclude>
|
||||
<exclude>org.glassfish.jersey.core:jersey-servlet</exclude>
|
||||
<exclude>org.glassfish.jersey.core:jersey-servlet</exclude>
|
||||
<!-- IMPALA-9708: Sentry is removed. -->
|
||||
<exclude>org.apache.sentry:*</exclude>
|
||||
<!-- Assert that we only use artifacts from only the specified
|
||||
version of these components. -->
|
||||
<exclude>org.apache.hadoop:*</exclude>
|
||||
<exclude>org.apache.hbase:*</exclude>
|
||||
<exclude>org.apache.hive:*</exclude>
|
||||
<exclude>org.apache.kudu:*</exclude>
|
||||
<exclude>org.apache.sentry:*</exclude>
|
||||
<exclude>org.apache.parquet:*</exclude>
|
||||
<exclude>org.apache.orc:*</exclude>
|
||||
</excludes>
|
||||
@@ -878,7 +710,6 @@ under the License.
|
||||
<include>org.apache.hive:*:${hive.version}</include>
|
||||
<include>org.apache.hive:hive-storage-api:${hive.storage.api.version}</include>
|
||||
<include>org.apache.kudu:*:${kudu.version}</include>
|
||||
<include>org.apache.sentry:*:${sentry.version}</include>
|
||||
<include>org.apache.parquet:*:${parquet.version}</include>
|
||||
<include>org.apache.orc:*:${orc.version}</include>
|
||||
</includes>
|
||||
|
||||
@@ -52,9 +52,9 @@ public class AlterDbSetOwnerStmt extends AlterDbStmt {
|
||||
"%d characters. The given owner name has %d characters.",
|
||||
MetaStoreUtil.MAX_OWNER_LENGTH, ownerName.length()));
|
||||
}
|
||||
// We don't allow assigning to a non-existent role because Sentry should know about
|
||||
// all roles. Sentry does not track all users so we allow assigning to a user
|
||||
// that Sentry doesn't know about yet.
|
||||
// We don't allow assigning to a non-existent role because Ranger should know about
|
||||
// all roles. Ranger does not track all users so we allow assigning to a user
|
||||
// that Ranger doesn't know about yet.
|
||||
if (analyzer.isAuthzEnabled() && owner_.getOwnerType() == TOwnerType.ROLE
|
||||
&& analyzer.getCatalog().getAuthPolicy().getRole(ownerName) == null) {
|
||||
throw new AnalysisException(String.format("Role '%s' does not exist.", ownerName));
|
||||
|
||||
@@ -49,9 +49,9 @@ public abstract class AlterTableOrViewSetOwnerStmt extends AlterTableStmt {
|
||||
"%d characters. The given owner name has %d characters.",
|
||||
MetaStoreUtil.MAX_OWNER_LENGTH, ownerName.length()));
|
||||
}
|
||||
// We don't allow assigning to a non-existent role because Sentry should know about
|
||||
// all roles. Sentry does not track all users so we allow assigning to a user
|
||||
// that Sentry doesn't know about yet.
|
||||
// We don't allow assigning to a non-existent role because Ranger should know about
|
||||
// all roles. Ranger does not track all users so we allow assigning to a user
|
||||
// that Ranger doesn't know about yet.
|
||||
if (analyzer.isAuthzEnabled() && owner_.getOwnerType() == TOwnerType.ROLE
|
||||
&& analyzer.getCatalog().getAuthPolicy().getRole(ownerName) == null) {
|
||||
throw new AnalysisException(String.format("Role '%s' does not exist.", ownerName));
|
||||
|
||||
@@ -20,7 +20,6 @@ package org.apache.impala.analysis;
|
||||
import org.apache.impala.authorization.AuthorizationConfig;
|
||||
import org.apache.impala.authorization.AuthorizationProvider;
|
||||
import org.apache.impala.authorization.User;
|
||||
import org.apache.impala.authorization.sentry.SentryAuthorizationConfig;
|
||||
import org.apache.impala.common.AnalysisException;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
@@ -46,4 +45,4 @@ public class AuthorizationStmt extends StatementBase {
|
||||
}
|
||||
requestingUser_ = analyzer.getUser();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,7 +305,7 @@ public class CreateTableStmt extends StatementBase {
|
||||
private void analyzeKuduTableProperties(Analyzer analyzer) throws AnalysisException {
|
||||
AuthorizationConfig authzConfig = analyzer.getAuthzConfig();
|
||||
if (authzConfig.isEnabled()) {
|
||||
// Today there is no comprehensive way of enforcing a Sentry authorization policy
|
||||
// Today there is no comprehensive way of enforcing a Ranger authorization policy
|
||||
// against tables stored in Kudu. This is why only users with ALL privileges on
|
||||
// SERVER may create external Kudu tables or set the master addresses.
|
||||
// See IMPALA-4000 for details.
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
package org.apache.impala.authorization;
|
||||
|
||||
import org.apache.impala.authorization.ranger.RangerAuthorizationFactory;
|
||||
import org.apache.impala.authorization.sentry.SentryAuthorizationFactory;
|
||||
|
||||
/**
|
||||
* This enum contains the list of authorization providers supported in Impala.
|
||||
@@ -28,7 +27,6 @@ import org.apache.impala.authorization.sentry.SentryAuthorizationFactory;
|
||||
* used to create the correct authorization classes.
|
||||
*/
|
||||
public enum AuthorizationProvider {
|
||||
SENTRY(SentryAuthorizationFactory.class.getCanonicalName()),
|
||||
RANGER(RangerAuthorizationFactory.class.getCanonicalName()),
|
||||
NOOP(NoopAuthorizationFactory.class.getCanonicalName());
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ import org.apache.impala.catalog.FeTable;
|
||||
*
|
||||
* For example:
|
||||
* PrivilegeRequestBuilder builder = new PrivilegeRequestBuilder(
|
||||
* new AuthorizableFactory(AuthorizationProvider.SENTRY));
|
||||
* new AuthorizableFactory(AuthorizationProvider.RANGER));
|
||||
* PrivilegeRequest = builder.allOf(Privilege.SELECT).onTable("db", "tbl").build();
|
||||
*/
|
||||
public class PrivilegeRequestBuilder {
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
// 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.authorization.sentry;
|
||||
|
||||
import org.apache.impala.authorization.Privilege;
|
||||
import org.apache.sentry.core.common.Action;
|
||||
import org.apache.sentry.core.common.BitFieldAction;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* This enum provides a list of Sentry actions used in Impala.
|
||||
*/
|
||||
public enum ImpalaAction implements Action {
|
||||
SELECT("select", 1),
|
||||
INSERT("insert", 1 << 2),
|
||||
ALTER("alter", 1 << 3),
|
||||
CREATE("create", 1 << 4),
|
||||
DROP("drop", 1 << 5),
|
||||
REFRESH("refresh", 1 << 6),
|
||||
ALL("*",
|
||||
SELECT.getCode() |
|
||||
INSERT.getCode() |
|
||||
ALTER.getCode() |
|
||||
CREATE.getCode() |
|
||||
DROP.getCode() |
|
||||
REFRESH.getCode()),
|
||||
OWNER("owner", ALL.getCode());
|
||||
|
||||
private final BitFieldAction bitFieldAction_;
|
||||
|
||||
ImpalaAction(String value, int code) {
|
||||
bitFieldAction_ = new BitFieldAction(value, code);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() { return bitFieldAction_.getValue(); }
|
||||
|
||||
public int getCode() { return bitFieldAction_.getActionCode(); }
|
||||
|
||||
public BitFieldAction getBitFieldAction() { return bitFieldAction_; }
|
||||
|
||||
public static EnumSet<ImpalaAction> from(Privilege privilege) {
|
||||
switch (privilege) {
|
||||
case ALL:
|
||||
return EnumSet.of(ImpalaAction.ALL);
|
||||
case OWNER:
|
||||
return EnumSet.of(ImpalaAction.OWNER);
|
||||
case ALTER:
|
||||
return EnumSet.of(ImpalaAction.ALTER);
|
||||
case DROP:
|
||||
return EnumSet.of(ImpalaAction.DROP);
|
||||
case CREATE:
|
||||
return EnumSet.of(ImpalaAction.CREATE);
|
||||
case INSERT:
|
||||
return EnumSet.of(ImpalaAction.INSERT);
|
||||
case SELECT:
|
||||
return EnumSet.of(ImpalaAction.SELECT);
|
||||
case REFRESH:
|
||||
return EnumSet.of(ImpalaAction.REFRESH);
|
||||
case VIEW_METADATA:
|
||||
case ANY:
|
||||
return EnumSet.copyOf(privilege.getImpliedPrivileges()
|
||||
.stream()
|
||||
.flatMap(p -> from(p).stream())
|
||||
.collect(Collectors.toSet()));
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported privilege: " + privilege);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* 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.authorization.sentry;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.apache.sentry.core.common.BitFieldAction;
|
||||
import org.apache.sentry.core.common.BitFieldActionFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An implementation of BitFieldActionFactory for Impala.
|
||||
*/
|
||||
public class ImpalaActionFactory extends BitFieldActionFactory {
|
||||
@Override
|
||||
public List<? extends BitFieldAction> getActionsByCode(int actionCode) {
|
||||
Preconditions.checkArgument(
|
||||
actionCode >= 1 && actionCode <= ImpalaAction.ALL.getCode(),
|
||||
String.format("Action code must between 1 and %d.", ImpalaAction.ALL.getCode()));
|
||||
|
||||
List<BitFieldAction> actions = new ArrayList<>();
|
||||
for (ImpalaAction action : ImpalaAction.values()) {
|
||||
if ((action.getCode() & actionCode) == action.getCode()) {
|
||||
actions.add(action.getBitFieldAction());
|
||||
}
|
||||
}
|
||||
return actions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BitFieldAction getActionByName(String name) {
|
||||
Preconditions.checkNotNull(name);
|
||||
|
||||
for (ImpalaAction action : ImpalaAction.values()) {
|
||||
if (action.getValue().equalsIgnoreCase(name)) {
|
||||
return action.getBitFieldAction();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
// 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.authorization.sentry;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.sentry.core.common.BitFieldActionFactory;
|
||||
import org.apache.sentry.core.common.ImplyMethodType;
|
||||
import org.apache.sentry.core.model.db.HivePrivilegeModel;
|
||||
import org.apache.sentry.core.common.Model;
|
||||
|
||||
/**
|
||||
* Delegates to HivePrivilegeModel for getImplyMethodMap(), but
|
||||
* uses Impala's BitFieldActionFactory implementation.
|
||||
*/
|
||||
public class ImpalaPrivilegeModel implements Model {
|
||||
public static final ImpalaPrivilegeModel INSTANCE = new ImpalaPrivilegeModel();
|
||||
private final ImpalaActionFactory actionFactory = new ImpalaActionFactory();
|
||||
|
||||
@Override
|
||||
public Map<String, ImplyMethodType> getImplyMethodMap() {
|
||||
return HivePrivilegeModel.getInstance().getImplyMethodMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BitFieldActionFactory getBitFieldActionFactory() {
|
||||
return actionFactory;
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
// 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.authorization.sentry;
|
||||
|
||||
import org.apache.commons.lang.reflect.ConstructorUtils;
|
||||
import org.apache.impala.authorization.AuthorizationConfig;
|
||||
import org.apache.sentry.policy.engine.common.CommonPolicyEngine;
|
||||
import org.apache.sentry.provider.cache.PrivilegeCache;
|
||||
import org.apache.sentry.provider.cache.SimpleCacheProviderBackend;
|
||||
import org.apache.sentry.provider.common.ProviderBackend;
|
||||
import org.apache.sentry.provider.common.ProviderBackendContext;
|
||||
import org.apache.sentry.provider.common.ResourceAuthorizationProvider;
|
||||
import org.apache.sentry.provider.file.SimpleFileProviderBackend;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
/**
|
||||
* Wrapper to facilitate differences in Sentry APIs across Sentry versions.
|
||||
*/
|
||||
class SentryAuthProvider {
|
||||
/*
|
||||
* Creates a new ResourceAuthorizationProvider based on the given configuration.
|
||||
*/
|
||||
static ResourceAuthorizationProvider createProvider(AuthorizationConfig config,
|
||||
SentryAuthorizationPolicy policy) {
|
||||
Preconditions.checkArgument(policy instanceof PrivilegeCache);
|
||||
Preconditions.checkArgument(config instanceof SentryAuthorizationConfig);
|
||||
SentryAuthorizationConfig sentryAuthzConfig = (SentryAuthorizationConfig) config;
|
||||
try {
|
||||
// Note: The second parameter to the ProviderBackend is a "resourceFile" path
|
||||
// which is not used by Impala. We cannot pass 'null' so instead pass an empty
|
||||
// string.
|
||||
ProviderBackend providerBe = new SimpleCacheProviderBackend(
|
||||
sentryAuthzConfig.getSentryConfig().getConfig(), "");
|
||||
Preconditions.checkNotNull(policy);
|
||||
ProviderBackendContext context = new ProviderBackendContext();
|
||||
context.setBindingHandle(policy);
|
||||
providerBe.initialize(context);
|
||||
|
||||
CommonPolicyEngine engine =
|
||||
new CommonPolicyEngine(providerBe);
|
||||
|
||||
// Try to create an instance of the specified policy provider class.
|
||||
// Re-throw any exceptions that are encountered.
|
||||
|
||||
return (ResourceAuthorizationProvider) ConstructorUtils.invokeConstructor(
|
||||
Class.forName(sentryAuthzConfig.getPolicyProviderClassName()),
|
||||
new Object[] {"", engine, ImpalaPrivilegeModel.INSTANCE});
|
||||
} catch (Exception e) {
|
||||
// Re-throw as unchecked exception.
|
||||
throw new IllegalStateException(
|
||||
"Error creating ResourceAuthorizationProvider: ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
// 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.authorization.sentry;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.apache.impala.authorization.Authorizable;
|
||||
import org.apache.sentry.core.model.db.DBModelAuthorizable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* A base class for Authorizable with Sentry specific operations.
|
||||
*/
|
||||
public abstract class SentryAuthorizable extends Authorizable {
|
||||
/*
|
||||
* Returns the list of the Authorizable objects in their hierarchical order.
|
||||
* For example:
|
||||
* [Column] would return Db -> Table -> Column
|
||||
* [Table] would return Db -> Table
|
||||
* [Db] would return [Db]
|
||||
* [URI] would return [URI]
|
||||
*/
|
||||
public abstract List<Authorizable> getAuthorizableHierarchy();
|
||||
|
||||
/**
|
||||
* Returns the list of the Sentry DBModelAuthorizable objects in their hierarchical
|
||||
* order.
|
||||
*/
|
||||
public List<DBModelAuthorizable> getDBModelAuthorizableHierarchy() {
|
||||
return getAuthorizableHierarchy().stream()
|
||||
.map(a -> {
|
||||
Preconditions.checkState(a instanceof SentryAuthorizable);
|
||||
return ((SentryAuthorizable) a).getDBModelAuthorizable();
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Sentry DBModelAuthorizable that represents the type
|
||||
* of Authorizable.
|
||||
*/
|
||||
public abstract DBModelAuthorizable getDBModelAuthorizable();
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
// 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.authorization.sentry;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.apache.impala.authorization.Authorizable;
|
||||
import org.apache.sentry.core.model.db.AccessConstants;
|
||||
import org.apache.sentry.core.model.db.DBModelAuthorizable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Class used to authorize access to a column for Sentry.
|
||||
*/
|
||||
public class SentryAuthorizableColumn extends SentryAuthorizable {
|
||||
private final org.apache.sentry.core.model.db.Column column_;
|
||||
private final org.apache.sentry.core.model.db.Table table_;
|
||||
private final org.apache.sentry.core.model.db.Database database_;
|
||||
|
||||
public SentryAuthorizableColumn(String dbName) {
|
||||
this(dbName, AccessConstants.ALL);
|
||||
}
|
||||
|
||||
public SentryAuthorizableColumn(String dbName, String tableName) {
|
||||
this(dbName, tableName, AccessConstants.ALL);
|
||||
}
|
||||
|
||||
public SentryAuthorizableColumn(String dbName, String tableName, String columnName) {
|
||||
Preconditions.checkArgument(!Strings.isNullOrEmpty(dbName));
|
||||
Preconditions.checkArgument(!Strings.isNullOrEmpty(tableName));
|
||||
Preconditions.checkArgument(!Strings.isNullOrEmpty(columnName));
|
||||
column_ = new org.apache.sentry.core.model.db.Column(columnName);
|
||||
table_ = new org.apache.sentry.core.model.db.Table(tableName);
|
||||
database_ = new org.apache.sentry.core.model.db.Database(dbName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Authorizable> getAuthorizableHierarchy() {
|
||||
return Lists.newArrayList(new SentryAuthorizableDb(database_.getName()),
|
||||
new SentryAuthorizableTable(database_.getName(), table_.getName()), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() { return database_.getName() + "." + table_.getName() + "."
|
||||
+ column_.getName(); }
|
||||
|
||||
@Override
|
||||
public Type getType() { return Type.COLUMN; }
|
||||
|
||||
@Override
|
||||
public String getFullTableName() {
|
||||
return database_.getName() + "." + table_.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDbName() { return database_.getName(); }
|
||||
|
||||
@Override
|
||||
public String getTableName() { return table_.getName(); }
|
||||
|
||||
@Override
|
||||
public String getColumnName() { return column_.getName(); }
|
||||
|
||||
@Override
|
||||
public DBModelAuthorizable getDBModelAuthorizable() { return column_; }
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
// 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.authorization.sentry;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.apache.impala.authorization.Authorizable;
|
||||
import org.apache.sentry.core.model.db.DBModelAuthorizable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Class used to authorize access to a database for Sentry.
|
||||
*/
|
||||
public class SentryAuthorizableDb extends SentryAuthorizable {
|
||||
private final org.apache.sentry.core.model.db.Database database_;
|
||||
|
||||
public SentryAuthorizableDb(String dbName) {
|
||||
Preconditions.checkArgument(dbName != null && !dbName.isEmpty());
|
||||
database_ = new org.apache.sentry.core.model.db.Database(dbName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Authorizable> getAuthorizableHierarchy() {
|
||||
return Lists.newArrayList(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() { return database_.getName(); }
|
||||
|
||||
@Override
|
||||
public Type getType() { return Authorizable.Type.DB; }
|
||||
|
||||
@Override
|
||||
public String getDbName() { return getName(); }
|
||||
|
||||
@Override
|
||||
public DBModelAuthorizable getDBModelAuthorizable() { return database_; }
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
// 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.authorization.sentry;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.apache.impala.authorization.Authorizable;
|
||||
import org.apache.impala.authorization.AuthorizableFactory;
|
||||
|
||||
/**
|
||||
* A Sentry implementation of {@link AuthorizableFactory}.
|
||||
*/
|
||||
public class SentryAuthorizableFactory implements AuthorizableFactory {
|
||||
@Override
|
||||
public Authorizable newServer(String serverName) {
|
||||
return new SentryAuthorizableServer(serverName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authorizable newDatabase(String dbName, String ownerUser) {
|
||||
// Sentry works with OWNER privilege. Hence ownerUser is ignored.
|
||||
Preconditions.checkNotNull(dbName);
|
||||
return new SentryAuthorizableDb(dbName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authorizable newTable(String dbName, String tableName, String ownerUser) {
|
||||
// Sentry works with OWNER privilege. Hence ownerUser is ignored.
|
||||
Preconditions.checkNotNull(dbName);
|
||||
Preconditions.checkNotNull(tableName);
|
||||
return new SentryAuthorizableTable(dbName, tableName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authorizable newColumnAllTbls(String dbName, String dbOwnerUser) {
|
||||
// Sentry works with OWNER privilege. Hence ownerUser is ignored.
|
||||
Preconditions.checkNotNull(dbName);
|
||||
return new SentryAuthorizableColumn(dbName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authorizable newColumnInTable(
|
||||
String dbName, String tableName, String dbOwnerUser) {
|
||||
// Sentry works with OWNER privilege. Hence ownerUser is ignored.
|
||||
Preconditions.checkNotNull(dbName);
|
||||
Preconditions.checkNotNull(tableName);
|
||||
return new SentryAuthorizableColumn(dbName, tableName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authorizable newColumnInTable(
|
||||
String dbName, String tableName, String columnName, String tblOwnerUser) {
|
||||
// Sentry works with OWNER privilege. Hence ownerUser is ignored.
|
||||
Preconditions.checkNotNull(dbName);
|
||||
Preconditions.checkNotNull(tableName);
|
||||
Preconditions.checkNotNull(columnName);
|
||||
return new SentryAuthorizableColumn(dbName, tableName, columnName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authorizable newUri(String uri) {
|
||||
Preconditions.checkNotNull(uri);
|
||||
return new SentryAuthorizableUri(uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authorizable newFunction(String dbName, String fnName) {
|
||||
Preconditions.checkNotNull(dbName);
|
||||
Preconditions.checkNotNull(fnName);
|
||||
return new SentryAuthorizableFn(dbName, fnName);
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
// 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.authorization.sentry;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.apache.impala.authorization.Authorizable;
|
||||
import org.apache.sentry.core.model.db.DBModelAuthorizable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Class used to authorize access to a Function for Sentry.
|
||||
*/
|
||||
public class SentryAuthorizableFn extends SentryAuthorizable {
|
||||
private final String fnName_;
|
||||
private final org.apache.sentry.core.model.db.Database database_;
|
||||
|
||||
public SentryAuthorizableFn(String dbName, String fnName) {
|
||||
Preconditions.checkArgument(!Strings.isNullOrEmpty(dbName));
|
||||
Preconditions.checkArgument(!Strings.isNullOrEmpty(fnName));
|
||||
database_ = new org.apache.sentry.core.model.db.Database(dbName);
|
||||
fnName_ = fnName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Authorizable> getAuthorizableHierarchy() {
|
||||
return Lists.newArrayList(new SentryAuthorizableDb(database_.getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() { return database_.getName() + "." + fnName_; }
|
||||
|
||||
@Override
|
||||
public Type getType() { return Type.FUNCTION; }
|
||||
|
||||
@Override
|
||||
public String getDbName() { return database_.getName(); }
|
||||
|
||||
@Override
|
||||
public String getFnName() { return fnName_; }
|
||||
|
||||
@Override
|
||||
public DBModelAuthorizable getDBModelAuthorizable() { return database_; }
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
// 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.authorization.sentry;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import org.apache.impala.authorization.Authorizable;
|
||||
import org.apache.sentry.core.model.db.DBModelAuthorizable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Class used to authorize access at the catalog level for Sentry. Generally, all
|
||||
* Impala services in the cluster will be configured with the same catalog name.
|
||||
* What Sentry refers to as a Server maps to our concept of a Catalog, thus
|
||||
* the name AuthorizableServer.
|
||||
*/
|
||||
public class SentryAuthorizableServer extends SentryAuthorizable {
|
||||
private final org.apache.sentry.core.model.db.Server server_;
|
||||
|
||||
public SentryAuthorizableServer(String serverName) {
|
||||
server_ = new org.apache.sentry.core.model.db.Server(
|
||||
serverName == null ? "server" : serverName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Authorizable> getAuthorizableHierarchy() {
|
||||
return Lists.newArrayList(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() { return server_.getName(); }
|
||||
|
||||
@Override
|
||||
public Type getType() { return Type.SERVER; }
|
||||
|
||||
@Override
|
||||
public DBModelAuthorizable getDBModelAuthorizable() { return server_; }
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
// 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.authorization.sentry;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.apache.impala.authorization.Authorizable;
|
||||
import org.apache.sentry.core.model.db.AccessConstants;
|
||||
import org.apache.sentry.core.model.db.DBModelAuthorizable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Class used to authorize access to a table or view for Sentry.
|
||||
* Even though Hive's spec includes an authorizable object 'view', we chose
|
||||
* to treat views the same way as tables for the sake of authorization.
|
||||
*/
|
||||
public class SentryAuthorizableTable extends SentryAuthorizable {
|
||||
private final org.apache.sentry.core.model.db.Table table_;
|
||||
private final org.apache.sentry.core.model.db.Database database_;
|
||||
|
||||
public SentryAuthorizableTable(String dbName) {
|
||||
this(dbName, AccessConstants.ALL);
|
||||
}
|
||||
|
||||
public SentryAuthorizableTable(String dbName, String tableName) {
|
||||
Preconditions.checkArgument(!Strings.isNullOrEmpty(tableName));
|
||||
Preconditions.checkArgument(!Strings.isNullOrEmpty(dbName));
|
||||
table_ = new org.apache.sentry.core.model.db.Table(tableName);
|
||||
database_ = new org.apache.sentry.core.model.db.Database(dbName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Authorizable> getAuthorizableHierarchy() {
|
||||
return Lists.newArrayList(new SentryAuthorizableDb(database_.getName()), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() { return database_.getName() + "." + table_.getName(); }
|
||||
|
||||
@Override
|
||||
public Type getType() { return Type.TABLE; }
|
||||
|
||||
@Override
|
||||
public String getDbName() { return database_.getName(); }
|
||||
|
||||
@Override
|
||||
public String getTableName() { return table_.getName(); }
|
||||
|
||||
@Override
|
||||
public String getFullTableName() { return getName(); }
|
||||
|
||||
@Override
|
||||
public DBModelAuthorizable getDBModelAuthorizable() { return table_; }
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
// 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.authorization.sentry;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.apache.impala.authorization.Authorizable;
|
||||
import org.apache.sentry.core.model.db.AccessURI;
|
||||
import org.apache.sentry.core.model.db.DBModelAuthorizable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Class used to authorize access to a URI for Sentry.
|
||||
*/
|
||||
public class SentryAuthorizableUri extends SentryAuthorizable {
|
||||
private final String uriName_;
|
||||
|
||||
public SentryAuthorizableUri(String uriName) {
|
||||
Preconditions.checkArgument(!Strings.isNullOrEmpty(uriName));
|
||||
uriName_ = uriName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Authorizable> getAuthorizableHierarchy() {
|
||||
return Lists.newArrayList(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() { return uriName_; }
|
||||
|
||||
@Override
|
||||
public Type getType() { return Type.URI; }
|
||||
|
||||
@Override
|
||||
public DBModelAuthorizable getDBModelAuthorizable() { return new AccessURI(uriName_); }
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
// 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.authorization.sentry;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.apache.impala.authorization.Authorizable.Type;
|
||||
import org.apache.impala.authorization.AuthorizationConfig;
|
||||
import org.apache.impala.authorization.AuthorizationContext;
|
||||
import org.apache.impala.authorization.AuthorizationException;
|
||||
import org.apache.impala.authorization.BaseAuthorizationChecker;
|
||||
import org.apache.impala.authorization.Privilege;
|
||||
import org.apache.impala.authorization.PrivilegeRequest;
|
||||
import org.apache.impala.authorization.User;
|
||||
import org.apache.impala.authorization.AuthorizationPolicy;
|
||||
import org.apache.impala.common.InternalException;
|
||||
import org.apache.impala.thrift.TSessionState;
|
||||
import org.apache.impala.util.EventSequence;
|
||||
import org.apache.sentry.core.common.ActiveRoleSet;
|
||||
import org.apache.sentry.core.common.Subject;
|
||||
import org.apache.sentry.core.model.db.DBModelAuthorizable;
|
||||
import org.apache.sentry.provider.common.ResourceAuthorizationProvider;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* An implementation of AuthorizationChecker that uses Sentry.
|
||||
*/
|
||||
public class SentryAuthorizationChecker extends BaseAuthorizationChecker {
|
||||
private final ResourceAuthorizationProvider provider_;
|
||||
private final SentryAuthorizableServer server_;
|
||||
|
||||
public SentryAuthorizationChecker(AuthorizationConfig config,
|
||||
AuthorizationPolicy policy) {
|
||||
super(config);
|
||||
if (config.isEnabled()) {
|
||||
server_ = new SentryAuthorizableServer(config.getServerName());
|
||||
provider_ = createProvider(config, new SentryAuthorizationPolicy(policy));
|
||||
Preconditions.checkNotNull(provider_);
|
||||
} else {
|
||||
provider_ = null;
|
||||
server_ = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of groups this user belongs to.
|
||||
*/
|
||||
public Set<String> getUserGroups(User user) throws InternalException {
|
||||
try {
|
||||
return provider_.getGroupMapping().getGroups(user.getShortName());
|
||||
} catch (Exception e) {
|
||||
if (SentryUtil.isSentryGroupNotFound(e)) {
|
||||
// Sentry 2.1+ throws exceptions when user does not exist; swallow the
|
||||
// exception and just return an empty set for this case.
|
||||
return Collections.emptySet();
|
||||
}
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void authorizeRowFilterAndColumnMask(User user,
|
||||
List<PrivilegeRequest> privilegeRequests)
|
||||
throws AuthorizationException, InternalException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateAuthorizationCache() {
|
||||
// Authorization refresh in Sentry is done by updating {@link AuthorizationPolicy}.
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsMaskingOrFiltering(User user, String dbName, String tableName,
|
||||
List<String> requiredColumns) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createColumnMask(User user, String dbName, String tableName,
|
||||
String columnName, AuthorizationContext authzCtx) {
|
||||
return columnName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthorizationContext createAuthorizationContext(boolean doAudits,
|
||||
String sqlStmt, TSessionState sessionState, Optional<EventSequence> timeline) {
|
||||
return new AuthorizationContext(timeline);
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates a new ResourceAuthorizationProvider based on the given configuration.
|
||||
*/
|
||||
private static ResourceAuthorizationProvider createProvider(AuthorizationConfig config,
|
||||
SentryAuthorizationPolicy policy) {
|
||||
return SentryAuthProvider.createProvider(config, policy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean authorizeResource(AuthorizationContext authzCtx, User user,
|
||||
PrivilegeRequest request) throws InternalException {
|
||||
EnumSet<ImpalaAction> actions = ImpalaAction.from(request.getPrivilege());
|
||||
|
||||
List<DBModelAuthorizable> authorizables = Lists.newArrayList(
|
||||
server_.getDBModelAuthorizableHierarchy());
|
||||
// If request.getAuthorizable() is null, the request is for server-level permission.
|
||||
if (request.getAuthorizable() != null) {
|
||||
Preconditions.checkState(request.getAuthorizable() instanceof SentryAuthorizable);
|
||||
authorizables.addAll(((SentryAuthorizable) request.getAuthorizable())
|
||||
.getDBModelAuthorizableHierarchy());
|
||||
}
|
||||
|
||||
// The Hive Access API does not currently provide a way to check if the user
|
||||
// has any privileges on a given resource.
|
||||
if (request.getPrivilege().hasAnyOf()) {
|
||||
for (ImpalaAction action: actions) {
|
||||
if (provider_.hasAccess(new Subject(user.getShortName()), authorizables,
|
||||
EnumSet.of(action), request.hasGrantOption(), ActiveRoleSet.ALL)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
// AuthorizableFn is special due to Sentry not having the concept of a function in
|
||||
// DBModelAuthorizable.AuthorizableType. As a result, the list of authorizables for
|
||||
// an AuthorizableFn only contains the server and database, but not the function
|
||||
// itself. So there is no need to remove the last authorizable here.
|
||||
} else if (request.getPrivilege() == Privilege.CREATE && authorizables.size() > 1 &&
|
||||
!(request.getAuthorizable().getType() == Type.FUNCTION)) {
|
||||
// CREATE on an object requires CREATE on the parent,
|
||||
// so don't check access on the object we're creating.
|
||||
authorizables.remove(authorizables.size() - 1);
|
||||
}
|
||||
return provider_.hasAccess(new Subject(user.getShortName()), authorizables, actions,
|
||||
request.hasGrantOption(), ActiveRoleSet.ALL);
|
||||
}
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
// 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.authorization.sentry;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Strings;
|
||||
import org.apache.impala.authorization.AuthorizationConfig;
|
||||
import org.apache.impala.authorization.AuthorizationProvider;
|
||||
import org.apache.sentry.provider.common.HadoopGroupResourceAuthorizationProvider;
|
||||
import org.apache.sentry.provider.common.ResourceAuthorizationProvider;
|
||||
|
||||
/**
|
||||
* Impala authorization configuration with Sentry.
|
||||
*/
|
||||
public class SentryAuthorizationConfig implements AuthorizationConfig {
|
||||
private final String serverName_;
|
||||
private final SentryConfig sentryConfig_;
|
||||
private final String policyProviderClassName_;
|
||||
|
||||
/**
|
||||
* Creates a new authorization configuration object.
|
||||
* @param serverName - The name of this Impala server.
|
||||
* @param sentryConfigFile - Absolute path and file name of the sentry service.
|
||||
* @param policyProviderClassName - Class name of the policy provider to use.
|
||||
*/
|
||||
public SentryAuthorizationConfig(String serverName, String sentryConfigFile,
|
||||
String policyProviderClassName) {
|
||||
serverName_ = serverName;
|
||||
sentryConfig_ = new SentryConfig(sentryConfigFile);
|
||||
if (!Strings.isNullOrEmpty(policyProviderClassName)) {
|
||||
policyProviderClassName = policyProviderClassName.trim();
|
||||
}
|
||||
policyProviderClassName_ = policyProviderClassName;
|
||||
validateConfig();
|
||||
}
|
||||
|
||||
public SentryAuthorizationConfig(SentryConfig config) {
|
||||
sentryConfig_ = config;
|
||||
serverName_ = null;
|
||||
policyProviderClassName_ = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an AuthorizationConfig object that has authorization disabled.
|
||||
*/
|
||||
public static SentryAuthorizationConfig createAuthDisabledConfig() {
|
||||
return new SentryAuthorizationConfig(null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an AuthorizationConfig object configured to use Hadoop user->group mappings
|
||||
* for the authorization provider.
|
||||
*/
|
||||
public static SentryAuthorizationConfig createHadoopGroupAuthConfig(String serverName,
|
||||
String sentryConfigFile) {
|
||||
return new SentryAuthorizationConfig(serverName, sentryConfigFile,
|
||||
HadoopGroupResourceAuthorizationProvider.class.getName());
|
||||
}
|
||||
|
||||
/*
|
||||
* Validates the authorization configuration and throws an AuthorizationException
|
||||
* if any problems are found. If authorization is disabled, config checks are skipped.
|
||||
*/
|
||||
private void validateConfig() throws IllegalArgumentException {
|
||||
// If authorization is not enabled, config checks are skipped.
|
||||
if (!isEnabled()) return;
|
||||
|
||||
// Only load the sentry configuration if a sentry-site.xml configuration file was
|
||||
// specified. It is optional for impalad.
|
||||
if (!Strings.isNullOrEmpty(sentryConfig_.getConfigFile())) {
|
||||
sentryConfig_.loadConfig();
|
||||
}
|
||||
|
||||
if (Strings.isNullOrEmpty(serverName_)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Authorization is enabled but the server name is null or empty. Set the " +
|
||||
"server name using the impalad --server_name flag.");
|
||||
}
|
||||
if (Strings.isNullOrEmpty(policyProviderClassName_)) {
|
||||
throw new IllegalArgumentException("Authorization is enabled but the " +
|
||||
"authorization policy provider class name is null or empty. Set the class " +
|
||||
"name using the --authorization_policy_provider_class impalad flag.");
|
||||
}
|
||||
|
||||
Class<?> providerClass = null;
|
||||
try {
|
||||
// Get the Class object without performing any initialization.
|
||||
providerClass = Class.forName(policyProviderClassName_, false,
|
||||
this.getClass().getClassLoader());
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new IllegalArgumentException(String.format("The authorization policy " +
|
||||
"provider class '%s' was not found.", policyProviderClassName_), e);
|
||||
}
|
||||
Preconditions.checkNotNull(providerClass);
|
||||
if (!ResourceAuthorizationProvider.class.isAssignableFrom(providerClass)) {
|
||||
throw new IllegalArgumentException(String.format("The authorization policy " +
|
||||
"provider class '%s' must be a subclass of '%s'.",
|
||||
policyProviderClassName_,
|
||||
ResourceAuthorizationProvider.class.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if authorization is enabled.
|
||||
* If either serverName_, policyFile_, or sentryConfig_ file is set (not null
|
||||
* or empty), authorization is considered enabled.
|
||||
*/
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return !Strings.isNullOrEmpty(serverName_) ||
|
||||
!Strings.isNullOrEmpty(sentryConfig_.getConfigFile());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProviderName() { return "sentry"; }
|
||||
|
||||
/**
|
||||
* The server name to secure.
|
||||
*/
|
||||
@Override
|
||||
public String getServerName() { return serverName_; }
|
||||
|
||||
/**
|
||||
* The Sentry configuration.
|
||||
*/
|
||||
public SentryConfig getSentryConfig() { return sentryConfig_; }
|
||||
|
||||
/**
|
||||
* Returns Sentry policy provider class name.
|
||||
*/
|
||||
public String getPolicyProviderClassName() { return policyProviderClassName_; }
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
// 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.authorization.sentry;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Strings;
|
||||
import org.apache.impala.authorization.AuthorizableFactory;
|
||||
import org.apache.impala.authorization.AuthorizationChecker;
|
||||
import org.apache.impala.authorization.AuthorizationConfig;
|
||||
import org.apache.impala.authorization.AuthorizationManager;
|
||||
import org.apache.impala.authorization.AuthorizationPolicy;
|
||||
import org.apache.impala.authorization.AuthorizationFactory;
|
||||
import org.apache.impala.catalog.CatalogServiceCatalog;
|
||||
import org.apache.impala.common.ImpalaException;
|
||||
import org.apache.impala.service.BackendConfig;
|
||||
import org.apache.impala.service.FeCatalogManager;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* An implementation of {@link AuthorizationFactory} that uses Sentry.
|
||||
*/
|
||||
public class SentryAuthorizationFactory implements AuthorizationFactory {
|
||||
private static final SentryAuthorizableFactory AUTHORIZABLE_FACTORY =
|
||||
new SentryAuthorizableFactory();
|
||||
|
||||
private final AuthorizationConfig authzConfig_;
|
||||
|
||||
public SentryAuthorizationFactory(BackendConfig backendConfig) {
|
||||
Preconditions.checkNotNull(backendConfig);
|
||||
authzConfig_ = newAuthorizationConfig(backendConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is for testing.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public SentryAuthorizationFactory(AuthorizationConfig authzConfig) {
|
||||
Preconditions.checkNotNull(authzConfig);
|
||||
authzConfig_ = authzConfig;
|
||||
}
|
||||
|
||||
private static AuthorizationConfig newAuthorizationConfig(BackendConfig backendConfig) {
|
||||
String serverName = backendConfig.getBackendCfg().getServer_name();
|
||||
String sentryConfigFile = backendConfig.getBackendCfg().getSentry_config();
|
||||
String policyProviderClassName = backendConfig.getBackendCfg()
|
||||
.getAuthorization_policy_provider_class();
|
||||
// The logic for creating Sentry authorization config is inconsistent between
|
||||
// catalogd and impalad. In catalogd, only --sentry_config flag is required to enable
|
||||
// authorization. In impalad, --server_name and --sentry_config are required.
|
||||
// Keeping the same logic for backward compatibility.
|
||||
if (Strings.isNullOrEmpty(serverName)) {
|
||||
// Check if the Sentry Service is configured. If so, create a configuration object.
|
||||
SentryConfig sentryConfig = new SentryConfig(null);
|
||||
if (!Strings.isNullOrEmpty(sentryConfigFile)) {
|
||||
sentryConfig = new SentryConfig(sentryConfigFile);
|
||||
sentryConfig.loadConfig();
|
||||
}
|
||||
return new SentryAuthorizationConfig(sentryConfig);
|
||||
}
|
||||
return new SentryAuthorizationConfig(serverName, sentryConfigFile,
|
||||
policyProviderClassName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthorizationConfig getAuthorizationConfig() { return authzConfig_; }
|
||||
|
||||
@Override
|
||||
public AuthorizationChecker newAuthorizationChecker(AuthorizationPolicy authzPolicy) {
|
||||
return new SentryAuthorizationChecker(authzConfig_, authzPolicy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthorizableFactory getAuthorizableFactory() { return AUTHORIZABLE_FACTORY; }
|
||||
|
||||
@Override
|
||||
public AuthorizationManager newAuthorizationManager(FeCatalogManager catalog,
|
||||
Supplier<? extends AuthorizationChecker> authzChecker) {
|
||||
return new SentryImpaladAuthorizationManager(catalog, authzChecker);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthorizationManager newAuthorizationManager(CatalogServiceCatalog catalog)
|
||||
throws ImpalaException {
|
||||
return new SentryCatalogdAuthorizationManager(
|
||||
(SentryAuthorizationConfig) getAuthorizationConfig(), catalog);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsColumnMasking() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,167 +0,0 @@
|
||||
// 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.authorization.sentry;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.apache.impala.authorization.AuthorizationPolicy;
|
||||
import org.apache.impala.catalog.PrincipalPrivilege;
|
||||
import org.apache.impala.catalog.PrincipalPrivilegeTree;
|
||||
import org.apache.impala.catalog.Role;
|
||||
import org.apache.impala.catalog.User;
|
||||
import org.apache.sentry.core.common.ActiveRoleSet;
|
||||
import org.apache.sentry.core.common.Authorizable;
|
||||
import org.apache.sentry.core.common.utils.SentryConstants;
|
||||
import org.apache.sentry.core.model.db.DBModelAuthorizable;
|
||||
import org.apache.sentry.policy.common.Privilege;
|
||||
import org.apache.sentry.policy.common.PrivilegeFactory;
|
||||
import org.apache.sentry.policy.engine.common.CommonPrivilegeFactory;
|
||||
import org.apache.sentry.provider.cache.FilteredPrivilegeCache;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* The source data this cache is backing is read from the Sentry Policy Service.
|
||||
* Writing to the cache will replace any matching items, but will not write back to the
|
||||
* Sentry Policy Service. Acts as the backing cache for the Sentry cached based provider.
|
||||
* TODO: Instead of calling into Sentry to perform final authorization checks, we
|
||||
* should parse/validate the privileges in Impala.
|
||||
*/
|
||||
public class SentryAuthorizationPolicy implements FilteredPrivilegeCache {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(
|
||||
SentryAuthorizationPolicy.class);
|
||||
|
||||
private final AuthorizationPolicy authzPolicy_;
|
||||
private final PrivilegeFactory privilegeFactory;
|
||||
|
||||
public SentryAuthorizationPolicy(AuthorizationPolicy authzPolicy) {
|
||||
Preconditions.checkNotNull(authzPolicy);
|
||||
authzPolicy_ = authzPolicy;
|
||||
this.privilegeFactory = new CommonPrivilegeFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a set of privilege strings in Sentry format.
|
||||
*/
|
||||
@Override
|
||||
public Set<String> listPrivileges(Set<String> groups,
|
||||
ActiveRoleSet roleSet) {
|
||||
return listPrivilegesForGroups(groups, roleSet, null);
|
||||
}
|
||||
|
||||
|
||||
private Set<String> listPrivilegesForGroups(Set<String> groups,
|
||||
ActiveRoleSet roleSet, PrincipalPrivilegeTree.Filter filter) {
|
||||
Set<String> privileges = Sets.newHashSet();
|
||||
if (roleSet != ActiveRoleSet.ALL) {
|
||||
throw new UnsupportedOperationException("Impala does not support role subsets.");
|
||||
}
|
||||
|
||||
// Collect all privileges granted to all roles.
|
||||
for (String groupName: groups) {
|
||||
List<Role> grantedRoles = authzPolicy_.getGrantedRoles(groupName);
|
||||
for (Role role: grantedRoles) {
|
||||
privileges.addAll(role.getFilteredPrivilegeNames(filter));
|
||||
}
|
||||
}
|
||||
return privileges;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a set of privilege strings in Sentry format.
|
||||
*/
|
||||
@Override
|
||||
public Set<String> listPrivileges(Set<String> groups, Set<String> users,
|
||||
ActiveRoleSet roleSet) {
|
||||
return listPrivilegesForGroupsAndUsers(groups, users, roleSet, null);
|
||||
}
|
||||
|
||||
private Set<String> listPrivilegesForGroupsAndUsers(Set<String> groups,
|
||||
Set<String> users, ActiveRoleSet roleSet, PrincipalPrivilegeTree.Filter filter) {
|
||||
Set<String> privileges = listPrivilegesForGroups(groups, roleSet, filter);
|
||||
for (String userName: users) {
|
||||
User user = authzPolicy_.getUser(userName);
|
||||
if (user != null) {
|
||||
privileges.addAll(user.getFilteredPrivilegeNames(filter));
|
||||
}
|
||||
}
|
||||
return privileges;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> listPrivileges(Set<String> groups, Set<String> users,
|
||||
ActiveRoleSet roleSet, Authorizable... authorizationHierarchy) {
|
||||
PrincipalPrivilegeTree.Filter filter = createPrivilegeFilter(authorizationHierarchy);
|
||||
return listPrivilegesForGroupsAndUsers(groups, users, roleSet, filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Privilege> listPrivilegeObjects(Set<String> groups, Set<String> users,
|
||||
ActiveRoleSet roleSet, Authorizable... authorizationHierarchy) {
|
||||
Set<String> privilegeStrings =
|
||||
listPrivileges(groups, users, roleSet, authorizationHierarchy);
|
||||
|
||||
return privilegeStrings.stream()
|
||||
.filter(priString -> priString != null)
|
||||
.map(priString -> getPrivilegeObject(priString))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
private Privilege getPrivilegeObject(String priString) {
|
||||
return privilegeFactory.createPrivilege(priString);
|
||||
}
|
||||
|
||||
private PrincipalPrivilegeTree.Filter createPrivilegeFilter(
|
||||
Authorizable... authorizationHierarchy) {
|
||||
PrincipalPrivilegeTree.Filter filter = new PrincipalPrivilegeTree.Filter();
|
||||
for (Authorizable auth : authorizationHierarchy) {
|
||||
String name = auth.getName().toLowerCase();
|
||||
if (name.equals(SentryConstants.RESOURCE_WILDCARD_VALUE) ||
|
||||
name.equals(SentryConstants.RESOURCE_WILDCARD_VALUE_SOME)||
|
||||
name.equals(SentryConstants.RESOURCE_WILDCARD_VALUE_ALL)) {
|
||||
name = null; // null will match with everything
|
||||
}
|
||||
if (!(auth instanceof DBModelAuthorizable)) continue;
|
||||
DBModelAuthorizable dbAuth = (DBModelAuthorizable) auth;
|
||||
switch (dbAuth.getAuthzType()) {
|
||||
case Server:
|
||||
filter.setServer(name);
|
||||
break;
|
||||
case Db:
|
||||
filter.setDb(name);
|
||||
break;
|
||||
case Table:
|
||||
filter.setTable(name);
|
||||
break;
|
||||
case URI:
|
||||
filter.setIsUri(true);
|
||||
// Do not do anything for Column and View
|
||||
}
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// Nothing to do, but required by PrivilegeCache.
|
||||
}
|
||||
}
|
||||
@@ -1,541 +0,0 @@
|
||||
// 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.authorization.sentry;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import org.apache.hadoop.hive.metastore.api.PrincipalType;
|
||||
import org.apache.impala.authorization.AuthorizationDelta;
|
||||
import org.apache.impala.authorization.AuthorizationException;
|
||||
import org.apache.impala.authorization.AuthorizationManager;
|
||||
import org.apache.impala.authorization.User;
|
||||
import org.apache.impala.catalog.CatalogException;
|
||||
import org.apache.impala.catalog.CatalogServiceCatalog;
|
||||
import org.apache.impala.catalog.Principal;
|
||||
import org.apache.impala.catalog.PrincipalPrivilege;
|
||||
import org.apache.impala.catalog.Role;
|
||||
import org.apache.impala.common.ImpalaException;
|
||||
import org.apache.impala.common.Reference;
|
||||
import org.apache.impala.common.UnsupportedFeatureException;
|
||||
import org.apache.impala.thrift.TCatalogObject;
|
||||
import org.apache.impala.thrift.TCatalogServiceRequestHeader;
|
||||
import org.apache.impala.thrift.TCreateDropRoleParams;
|
||||
import org.apache.impala.thrift.TDdlExecResponse;
|
||||
import org.apache.impala.thrift.TGrantRevokePrivParams;
|
||||
import org.apache.impala.thrift.TGrantRevokeRoleParams;
|
||||
import org.apache.impala.thrift.TPrincipalType;
|
||||
import org.apache.impala.thrift.TPrivilege;
|
||||
import org.apache.impala.thrift.TPrivilegeLevel;
|
||||
import org.apache.impala.thrift.TPrivilegeScope;
|
||||
import org.apache.impala.thrift.TResultSet;
|
||||
import org.apache.impala.thrift.TShowGrantPrincipalParams;
|
||||
import org.apache.impala.thrift.TShowRolesParams;
|
||||
import org.apache.impala.thrift.TShowRolesResult;
|
||||
import org.apache.impala.util.ClassUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* An implementation of {@link AuthorizationManager} for Catalogd that uses Sentry.
|
||||
*
|
||||
* The methods here manage the authorization metadata stored in the Catalogd catalog via
|
||||
* {@link org.apache.impala.authorization.AuthorizationPolicy} through operations
|
||||
* performed by {@link SentryProxy}. Any update to authorization metadata will then be
|
||||
* broadcasted to all Impalads.
|
||||
*
|
||||
* Operations not supported by Sentry will throw an {@link UnsupportedFeatureException}.
|
||||
*/
|
||||
public class SentryCatalogdAuthorizationManager implements AuthorizationManager {
|
||||
private static final Logger LOG =
|
||||
LoggerFactory.getLogger(SentryCatalogdAuthorizationManager.class);
|
||||
|
||||
private final CatalogServiceCatalog catalog_;
|
||||
// Proxy to access the Sentry Service and also periodically refreshes the
|
||||
// policy metadata. Null if Sentry Service is not enabled.
|
||||
private final SentryProxy sentryProxy_;
|
||||
|
||||
public SentryCatalogdAuthorizationManager(SentryAuthorizationConfig authzConfig,
|
||||
CatalogServiceCatalog catalog) throws ImpalaException {
|
||||
sentryProxy_ = new SentryProxy(Preconditions.checkNotNull(authzConfig), catalog);
|
||||
catalog_ = Preconditions.checkNotNull(catalog);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given user is a Sentry admin.
|
||||
*/
|
||||
public boolean isSentryAdmin(User user) throws ImpalaException {
|
||||
return sentryProxy_.isSentryAdmin(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createRole(User requestingUser, TCreateDropRoleParams params,
|
||||
TDdlExecResponse response) throws ImpalaException {
|
||||
verifySentryServiceEnabled();
|
||||
|
||||
Role role = catalog_.getAuthPolicy().getRole(params.getRole_name());
|
||||
if (role != null) {
|
||||
throw new AuthorizationException(String.format("Role '%s' already exists.",
|
||||
params.getRole_name()));
|
||||
}
|
||||
|
||||
role = sentryProxy_.createRole(requestingUser, params.getRole_name());
|
||||
Preconditions.checkNotNull(role);
|
||||
|
||||
TCatalogObject catalogObject = new TCatalogObject();
|
||||
catalogObject.setType(role.getCatalogObjectType());
|
||||
catalogObject.setPrincipal(role.toThrift());
|
||||
catalogObject.setCatalog_version(role.getCatalogVersion());
|
||||
response.result.addToUpdated_catalog_objects(catalogObject);
|
||||
response.result.setVersion(role.getCatalogVersion());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dropRole(User requestingUser, TCreateDropRoleParams params,
|
||||
TDdlExecResponse response) throws ImpalaException {
|
||||
verifySentryServiceEnabled();
|
||||
|
||||
Role role = catalog_.getAuthPolicy().getRole(params.getRole_name());
|
||||
if (role == null) {
|
||||
throw new AuthorizationException(String.format("Role '%s' does not exist.",
|
||||
params.getRole_name()));
|
||||
}
|
||||
|
||||
role = sentryProxy_.dropRole(requestingUser, params.getRole_name());
|
||||
if (role == null) {
|
||||
// Nothing was removed from the catalogd's cache.
|
||||
response.result.setVersion(catalog_.getCatalogVersion());
|
||||
return;
|
||||
}
|
||||
Preconditions.checkNotNull(role);
|
||||
|
||||
TCatalogObject catalogObject = new TCatalogObject();
|
||||
catalogObject.setType(role.getCatalogObjectType());
|
||||
catalogObject.setPrincipal(role.toThrift());
|
||||
catalogObject.setCatalog_version(role.getCatalogVersion());
|
||||
response.result.addToRemoved_catalog_objects(catalogObject);
|
||||
response.result.setVersion(role.getCatalogVersion());
|
||||
}
|
||||
|
||||
@Override
|
||||
public TShowRolesResult getRoles(TShowRolesParams params) throws ImpalaException {
|
||||
throw new UnsupportedOperationException(String.format(
|
||||
"%s is not supported in Catalogd", ClassUtil.getMethodName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void grantRoleToGroup(User requestingUser, TGrantRevokeRoleParams params,
|
||||
TDdlExecResponse response) throws ImpalaException {
|
||||
Preconditions.checkArgument(!params.getRole_names().isEmpty());
|
||||
Preconditions.checkArgument(!params.getGroup_names().isEmpty());
|
||||
verifySentryServiceEnabled();
|
||||
|
||||
if (catalog_.getAuthPolicy().getRole(params.getRole_names().get(0)) == null) {
|
||||
throw new AuthorizationException(String.format("Role '%s' does not exist.",
|
||||
params.getRole_names().get(0)));
|
||||
}
|
||||
|
||||
String roleName = params.getRole_names().get(0);
|
||||
String groupName = params.getGroup_names().get(0);
|
||||
Role role = sentryProxy_.grantRoleGroup(requestingUser, roleName, groupName);
|
||||
Preconditions.checkNotNull(role);
|
||||
response.result.addToUpdated_catalog_objects(createRoleObject(role));
|
||||
response.result.setVersion(role.getCatalogVersion());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void revokeRoleFromGroup(User requestingUser, TGrantRevokeRoleParams params,
|
||||
TDdlExecResponse response) throws ImpalaException {
|
||||
Preconditions.checkArgument(!params.getRole_names().isEmpty());
|
||||
Preconditions.checkArgument(!params.getGroup_names().isEmpty());
|
||||
verifySentryServiceEnabled();
|
||||
|
||||
if (catalog_.getAuthPolicy().getRole(params.getRole_names().get(0)) == null) {
|
||||
throw new AuthorizationException(String.format("Role '%s' does not exist.",
|
||||
params.getRole_names().get(0)));
|
||||
}
|
||||
|
||||
String roleName = params.getRole_names().get(0);
|
||||
String groupName = params.getGroup_names().get(0);
|
||||
Role role = sentryProxy_.revokeRoleGroup(requestingUser, roleName, groupName);
|
||||
response.result.addToUpdated_catalog_objects(createRoleObject(role));
|
||||
response.result.setVersion(role.getCatalogVersion());
|
||||
}
|
||||
|
||||
private static TCatalogObject createRoleObject(Role role) {
|
||||
Preconditions.checkNotNull(role);
|
||||
|
||||
TCatalogObject catalogObject = new TCatalogObject();
|
||||
catalogObject.setType(role.getCatalogObjectType());
|
||||
catalogObject.setPrincipal(role.toThrift());
|
||||
catalogObject.setCatalog_version(role.getCatalogVersion());
|
||||
return catalogObject;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void grantPrivilegeToRole(TCatalogServiceRequestHeader header,
|
||||
TGrantRevokePrivParams params, TDdlExecResponse response) throws ImpalaException {
|
||||
verifySentryServiceEnabled();
|
||||
|
||||
String roleName = params.getPrincipal_name();
|
||||
Role role = catalog_.getAuthPolicy().getRole(roleName);
|
||||
if (role == null) {
|
||||
throw new AuthorizationException(String.format("Role '%s' does not exist.",
|
||||
roleName));
|
||||
}
|
||||
|
||||
List<TPrivilege> privileges = params.getPrivileges().stream()
|
||||
.peek(p -> p.setPrincipal_id(role.getId()))
|
||||
.collect(Collectors.toList());
|
||||
List<PrincipalPrivilege> removedGrantOptPrivileges =
|
||||
Lists.newArrayListWithExpectedSize(privileges.size());
|
||||
List<PrincipalPrivilege> addedRolePrivileges =
|
||||
sentryProxy_.grantRolePrivileges(new User(header.getRequesting_user()), roleName,
|
||||
privileges, params.isHas_grant_opt(), removedGrantOptPrivileges);
|
||||
|
||||
Preconditions.checkNotNull(addedRolePrivileges);
|
||||
List<TCatalogObject> updatedPrivs =
|
||||
Lists.newArrayListWithExpectedSize(addedRolePrivileges.size());
|
||||
for (PrincipalPrivilege rolePriv: addedRolePrivileges) {
|
||||
updatedPrivs.add(rolePriv.toTCatalogObject());
|
||||
}
|
||||
|
||||
List<TCatalogObject> removedPrivs =
|
||||
Lists.newArrayListWithExpectedSize(removedGrantOptPrivileges.size());
|
||||
for (PrincipalPrivilege rolePriv: removedGrantOptPrivileges) {
|
||||
removedPrivs.add(rolePriv.toTCatalogObject());
|
||||
}
|
||||
|
||||
if (!updatedPrivs.isEmpty()) {
|
||||
response.result.setUpdated_catalog_objects(updatedPrivs);
|
||||
response.result.setVersion(
|
||||
updatedPrivs.get(updatedPrivs.size() - 1).getCatalog_version());
|
||||
if (!removedPrivs.isEmpty()) {
|
||||
response.result.setRemoved_catalog_objects(removedPrivs);
|
||||
response.result.setVersion(
|
||||
Math.max(getLastItemVersion(updatedPrivs), getLastItemVersion(removedPrivs)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void revokePrivilegeFromRole(TCatalogServiceRequestHeader header,
|
||||
TGrantRevokePrivParams params, TDdlExecResponse response) throws ImpalaException {
|
||||
verifySentryServiceEnabled();
|
||||
Preconditions.checkArgument(!params.getPrivileges().isEmpty());
|
||||
|
||||
Role role = catalog_.getAuthPolicy().getRole(params.principal_name);
|
||||
if (role == null) {
|
||||
throw new AuthorizationException(String.format("Role '%s' does not exist.",
|
||||
params.getPrincipal_name()));
|
||||
}
|
||||
|
||||
String roleName = params.getPrincipal_name();
|
||||
List<TPrivilege> privileges = params.getPrivileges().stream()
|
||||
.peek(p -> {
|
||||
if (role != null) p.setPrincipal_id(role.getId());
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
// If this is a revoke of a privilege that contains the grant option, the privileges
|
||||
// with the grant option will be revoked and new privileges without the grant option
|
||||
// will be added. The privilege in the catalog cannot simply be updated since the
|
||||
// name of the catalog object now contains the grantoption.
|
||||
|
||||
// If privileges contain the grant option and are revoked, this api will return a
|
||||
// list of the revoked privileges that contain the grant option. The
|
||||
// addedRolePrivileges parameter will contain a list of new privileges without the
|
||||
// grant option that are granted. If this is simply a revoke of a privilege without
|
||||
// grant options, the api will still return revoked privileges, but the
|
||||
// addedRolePrivileges will be empty since there will be no newly granted
|
||||
// privileges.
|
||||
List<PrincipalPrivilege> addedRolePrivileges =
|
||||
Lists.newArrayListWithExpectedSize(privileges.size());
|
||||
List<PrincipalPrivilege> removedGrantOptPrivileges =
|
||||
sentryProxy_.revokeRolePrivileges(new User(header.getRequesting_user()), roleName,
|
||||
privileges, params.isHas_grant_opt(), addedRolePrivileges);
|
||||
Preconditions.checkNotNull(addedRolePrivileges);
|
||||
|
||||
List<TCatalogObject> updatedPrivs =
|
||||
Lists.newArrayListWithExpectedSize(addedRolePrivileges.size());
|
||||
for (PrincipalPrivilege rolePriv : addedRolePrivileges) {
|
||||
updatedPrivs.add(rolePriv.toTCatalogObject());
|
||||
}
|
||||
|
||||
List<TCatalogObject> removedPrivs =
|
||||
Lists.newArrayListWithExpectedSize(removedGrantOptPrivileges.size());
|
||||
for (PrincipalPrivilege rolePriv : removedGrantOptPrivileges) {
|
||||
removedPrivs.add(rolePriv.toTCatalogObject());
|
||||
}
|
||||
|
||||
// If this is a REVOKE statement with hasGrantOpt, only the GRANT OPTION is removed
|
||||
// from the privileges. Otherwise the privileges are removed from the catalog.
|
||||
if (privileges.get(0).isHas_grant_opt()) {
|
||||
if (!updatedPrivs.isEmpty() && !removedPrivs.isEmpty()) {
|
||||
response.result.setUpdated_catalog_objects(updatedPrivs);
|
||||
response.result.setRemoved_catalog_objects(removedPrivs);
|
||||
response.result.setVersion(
|
||||
Math.max(getLastItemVersion(updatedPrivs), getLastItemVersion(removedPrivs)));
|
||||
}
|
||||
} else if (!removedPrivs.isEmpty()) {
|
||||
response.result.setRemoved_catalog_objects(removedPrivs);
|
||||
response.result.setVersion(
|
||||
removedPrivs.get(removedPrivs.size() - 1).getCatalog_version());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void grantPrivilegeToUser(TCatalogServiceRequestHeader header,
|
||||
TGrantRevokePrivParams params, TDdlExecResponse response) throws ImpalaException {
|
||||
throw new UnsupportedFeatureException(
|
||||
"GRANT <privilege> TO USER is not supported by Sentry.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void revokePrivilegeFromUser(TCatalogServiceRequestHeader header,
|
||||
TGrantRevokePrivParams params, TDdlExecResponse response) throws ImpalaException {
|
||||
throw new UnsupportedFeatureException(
|
||||
"REVOKE <privilege> FROM USER is not supported by Sentry.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void grantPrivilegeToGroup(TCatalogServiceRequestHeader header,
|
||||
TGrantRevokePrivParams params, TDdlExecResponse response) throws ImpalaException {
|
||||
throw new UnsupportedFeatureException(
|
||||
"GRANT <privilege> TO GROUP is not supported by Sentry.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void revokePrivilegeFromGroup(TCatalogServiceRequestHeader header,
|
||||
TGrantRevokePrivParams params, TDdlExecResponse response) throws ImpalaException {
|
||||
throw new UnsupportedFeatureException(
|
||||
"REVOKE <privilege> FROM GROUP is not supported by Sentry.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public TResultSet getPrivileges(TShowGrantPrincipalParams params)
|
||||
throws ImpalaException {
|
||||
throw new UnsupportedOperationException(String.format(
|
||||
"%s is not supported in Catalogd", ClassUtil.getMethodName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDatabaseOwnerPrivilege(String serverName, String databaseName,
|
||||
String oldOwner, PrincipalType oldOwnerType, String newOwner,
|
||||
PrincipalType newOwnerType, TDdlExecResponse response) throws ImpalaException {
|
||||
verifySentryServiceEnabled();
|
||||
if (!sentryProxy_.isObjectOwnershipEnabled()) return;
|
||||
|
||||
Preconditions.checkNotNull(serverName);
|
||||
Preconditions.checkNotNull(databaseName);
|
||||
TPrivilege filter = createDatabaseOwnerPrivilegeFilter(databaseName, serverName);
|
||||
updateOwnerPrivilege(oldOwner, oldOwnerType, newOwner, newOwnerType, response,
|
||||
filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the owner privileges for an object.
|
||||
* If object ownership is enabled in Sentry, we need to update the owner privilege
|
||||
* in the catalog so that any subsequent statements that rely on that privilege, or
|
||||
* the absence, will function correctly without waiting for the next refresh.
|
||||
* If oldOwner is not null, the privilege will be removed. If newOwner is not null,
|
||||
* the privilege will be added.
|
||||
* The catalog will correctly reflect the owner in HMS, however because the owner
|
||||
* privileges are created by HMS in Sentry, Impala does not have visibility on
|
||||
* whether or not that create was successful. If Sentry failed to properly update the
|
||||
* owner privilege, Impala will have a different view of privileges until the next
|
||||
* Sentry refresh.
|
||||
* e.g. For create, the privileges should be available to immediately create a table.
|
||||
* Additionally, if the metadata operation is successful, but sentry fails to add
|
||||
* the privilege, it will be removed on the next refresh. ALTER DATABASE SET OWNER
|
||||
* can be used to try adding the owner privilege again.
|
||||
* This method should be called from within a DDLLock or table lock (in the case of
|
||||
* alter table statements.) to ensure that the privileges are in sync with the metadata
|
||||
* operations.
|
||||
*/
|
||||
@Override
|
||||
public void updateTableOwnerPrivilege(String serverName, String databaseName,
|
||||
String tableName, String oldOwner, PrincipalType oldOwnerType, String newOwner,
|
||||
PrincipalType newOwnerType, TDdlExecResponse response) throws ImpalaException {
|
||||
verifySentryServiceEnabled();
|
||||
if (!sentryProxy_.isObjectOwnershipEnabled()) return;
|
||||
|
||||
Preconditions.checkNotNull(serverName);
|
||||
Preconditions.checkNotNull(databaseName);
|
||||
Preconditions.checkNotNull(tableName);
|
||||
TPrivilege filter = createTableOwnerPrivilegeFilter(databaseName, tableName,
|
||||
serverName);
|
||||
updateOwnerPrivilege(oldOwner, oldOwnerType, newOwner, newOwnerType, response,
|
||||
filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthorizationDelta refreshAuthorization(boolean resetVersions)
|
||||
throws ImpalaException {
|
||||
List<TCatalogObject> catalogObjectsAdded = new ArrayList<>();
|
||||
List<TCatalogObject> catalogObjectsRemoved = new ArrayList<>();
|
||||
sentryProxy_.refresh(resetVersions, catalogObjectsAdded, catalogObjectsRemoved);
|
||||
return new AuthorizationDelta(catalogObjectsAdded, catalogObjectsRemoved);
|
||||
}
|
||||
|
||||
private void updateOwnerPrivilege(String oldOwner, PrincipalType oldOwnerType,
|
||||
String newOwner, PrincipalType newOwnerType, TDdlExecResponse response,
|
||||
TPrivilege filter) {
|
||||
if (oldOwner != null && !oldOwner.isEmpty()) {
|
||||
removePrivilegeFromCatalog(oldOwner, oldOwnerType, filter, response);
|
||||
}
|
||||
if (newOwner != null && !newOwner.isEmpty()) {
|
||||
addPrivilegeToCatalog(newOwner, newOwnerType, filter, response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws a CatalogException if the Sentry Service is not enabled.
|
||||
*/
|
||||
private void verifySentryServiceEnabled() throws CatalogException {
|
||||
if (sentryProxy_ == null) {
|
||||
throw new CatalogException("Sentry Service is not enabled on the " +
|
||||
"CatalogServer.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if with grant is enabled for object ownership in Sentry.
|
||||
*/
|
||||
private boolean isObjectOwnershipGrantEnabled() throws ImpalaException {
|
||||
return sentryProxy_ == null ? false : sentryProxy_.isObjectOwnershipGrantEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a TPrivilege for an owner of a table for use as a filter.
|
||||
*/
|
||||
private TPrivilege createTableOwnerPrivilegeFilter(String databaseName,
|
||||
String tableName, String serverName) throws ImpalaException {
|
||||
TPrivilege privilege = createDatabaseOwnerPrivilegeFilter(databaseName, serverName);
|
||||
privilege.setScope(TPrivilegeScope.TABLE);
|
||||
privilege.setTable_name(tableName);
|
||||
return privilege;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a TPrivilege for an owner of a database for use as a filter.
|
||||
*/
|
||||
private TPrivilege createDatabaseOwnerPrivilegeFilter(String databaseName,
|
||||
String serverName) throws ImpalaException {
|
||||
TPrivilege privilege = new TPrivilege();
|
||||
privilege.setScope(TPrivilegeScope.DATABASE).setServer_name(serverName)
|
||||
.setPrivilege_level(TPrivilegeLevel.OWNER)
|
||||
.setDb_name(databaseName).setCreate_time_ms(-1)
|
||||
.setHas_grant_opt(isObjectOwnershipGrantEnabled());
|
||||
return privilege;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a helper method to take care of catalog related updates when removing
|
||||
* a privilege.
|
||||
*/
|
||||
private void removePrivilegeFromCatalog(String ownerString, PrincipalType ownerType,
|
||||
TPrivilege filter, TDdlExecResponse response) {
|
||||
Preconditions.checkNotNull(ownerString);
|
||||
Preconditions.checkNotNull(ownerType);
|
||||
Preconditions.checkNotNull(filter);
|
||||
try {
|
||||
PrincipalPrivilege removedPrivilege = null;
|
||||
switch (ownerType) {
|
||||
case ROLE:
|
||||
removedPrivilege = catalog_.removeRolePrivilege(ownerString,
|
||||
PrincipalPrivilege.buildPrivilegeName(filter));
|
||||
break;
|
||||
case USER:
|
||||
removedPrivilege = catalog_.removeUserPrivilege(ownerString,
|
||||
PrincipalPrivilege.buildPrivilegeName(filter));
|
||||
break;
|
||||
default:
|
||||
Preconditions.checkArgument(false,
|
||||
"Unexpected PrincipalType: " + ownerType.name());
|
||||
}
|
||||
if (removedPrivilege != null) {
|
||||
response.result.addToRemoved_catalog_objects(removedPrivilege
|
||||
.toTCatalogObject());
|
||||
}
|
||||
} catch (CatalogException e) {
|
||||
// Failure removing an owner privilege is not an issue because it could be
|
||||
// that Sentry refresh occurred while executing this method and this method
|
||||
// is used as a a best-effort to do what Sentry refresh does to make the
|
||||
// owner privilege available right away without having to wait for a Sentry
|
||||
// refresh.
|
||||
LOG.warn("Unable to remove owner privilege: " +
|
||||
PrincipalPrivilege.buildPrivilegeName(filter), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a helper method to take care of catalog related updates when adding
|
||||
* a privilege. This will also add a user to the catalog if it doesn't exist.
|
||||
*/
|
||||
private void addPrivilegeToCatalog(String ownerString, PrincipalType ownerType,
|
||||
TPrivilege filter, TDdlExecResponse response) {
|
||||
Preconditions.checkNotNull(ownerString);
|
||||
Preconditions.checkNotNull(ownerType);
|
||||
Preconditions.checkNotNull(filter);
|
||||
try {
|
||||
Principal owner;
|
||||
PrincipalPrivilege cPrivilege = null;
|
||||
if (ownerType == PrincipalType.USER) {
|
||||
Reference<Boolean> existingUser = new Reference<>();
|
||||
owner = catalog_.addUserIfNotExists(ownerString, existingUser);
|
||||
filter.setPrincipal_id(owner.getId());
|
||||
filter.setPrincipal_type(TPrincipalType.USER);
|
||||
cPrivilege = catalog_.addUserPrivilege(ownerString, filter);
|
||||
if (!existingUser.getRef()) {
|
||||
response.result.addToUpdated_catalog_objects(owner.toTCatalogObject());
|
||||
}
|
||||
} else if (ownerType == PrincipalType.ROLE) {
|
||||
owner = catalog_.getAuthPolicy().getRole(ownerString);
|
||||
Preconditions.checkNotNull(owner);
|
||||
filter.setPrincipal_id(owner.getId());
|
||||
filter.setPrincipal_type(TPrincipalType.ROLE);
|
||||
cPrivilege = catalog_.addRolePrivilege(ownerString, filter);
|
||||
} else {
|
||||
Preconditions.checkArgument(false, "Unexpected PrincipalType: " +
|
||||
ownerType.name());
|
||||
}
|
||||
response.result.addToUpdated_catalog_objects(cPrivilege.toTCatalogObject());
|
||||
} catch (CatalogException e) {
|
||||
// Failure adding an owner privilege is not an issue because it could be
|
||||
// that Sentry refresh occurred while executing this method and this method
|
||||
// is used as a a best-effort to do what Sentry refresh does to make the
|
||||
// owner privilege available right away without having to wait for a Sentry
|
||||
// refresh.
|
||||
LOG.warn("Unable to add owner privilege: " +
|
||||
PrincipalPrivilege.buildPrivilegeName(filter), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version from the last item in the list. This assumes that the items
|
||||
* are added in version order.
|
||||
*/
|
||||
private long getLastItemVersion(List<TCatalogObject> items) {
|
||||
Preconditions.checkState(items != null && !items.isEmpty());
|
||||
return items.get(items.size() - 1).getCatalog_version();
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
// 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.authorization.sentry;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.MalformedURLException;
|
||||
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
|
||||
import org.apache.impala.common.FileSystemUtil;
|
||||
import com.google.common.base.Strings;
|
||||
|
||||
/**
|
||||
* Class used to load a sentry-site.xml configuration file.
|
||||
*/
|
||||
public class SentryConfig {
|
||||
// Absolute path to the sentry-site.xml configuration file.
|
||||
private final String configFile_;
|
||||
|
||||
// The Sentry configuration. Valid only after calling loadConfig().
|
||||
private final Configuration config_;
|
||||
|
||||
public SentryConfig(String configFilePath) {
|
||||
configFile_ = configFilePath;
|
||||
config_ = FileSystemUtil.getConfiguration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the Sentry configuration.
|
||||
*/
|
||||
public void loadConfig() {
|
||||
if (Strings.isNullOrEmpty(configFile_)) {
|
||||
throw new IllegalArgumentException("A valid path to a sentry-site.xml config " +
|
||||
"file must be set using --sentry_config to enable authorization.");
|
||||
}
|
||||
|
||||
File configFile = new File(configFile_);
|
||||
if (!configFile.exists()) {
|
||||
String configFilePath = "\"" + configFile_ + "\"";
|
||||
throw new RuntimeException("Sentry configuration file does not exist: " +
|
||||
configFilePath);
|
||||
}
|
||||
|
||||
if (!configFile.canRead()) {
|
||||
throw new RuntimeException("Cannot read Sentry configuration file: " +
|
||||
configFile_);
|
||||
}
|
||||
|
||||
// Load the config.
|
||||
try {
|
||||
config_.addResource(configFile.toURI().toURL());
|
||||
} catch (MalformedURLException e) {
|
||||
throw new RuntimeException("Invalid Sentry config file path: " + configFile_, e);
|
||||
}
|
||||
}
|
||||
|
||||
public Configuration getConfig() { return config_; }
|
||||
public String getConfigFile() { return configFile_; }
|
||||
}
|
||||
@@ -1,306 +0,0 @@
|
||||
// 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.authorization.sentry;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.apache.hadoop.hive.metastore.api.PrincipalType;
|
||||
import org.apache.impala.authorization.AuthorizationChecker;
|
||||
import org.apache.impala.authorization.AuthorizationDelta;
|
||||
import org.apache.impala.authorization.AuthorizationException;
|
||||
import org.apache.impala.authorization.AuthorizationManager;
|
||||
import org.apache.impala.authorization.User;
|
||||
import org.apache.impala.catalog.Principal;
|
||||
import org.apache.impala.catalog.Role;
|
||||
import org.apache.impala.common.ImpalaException;
|
||||
import org.apache.impala.common.InternalException;
|
||||
import org.apache.impala.common.JniUtil;
|
||||
import org.apache.impala.common.UnsupportedFeatureException;
|
||||
import org.apache.impala.service.FeCatalogManager;
|
||||
import org.apache.impala.service.FeSupport;
|
||||
import org.apache.impala.thrift.TCatalogServiceRequestHeader;
|
||||
import org.apache.impala.thrift.TCreateDropRoleParams;
|
||||
import org.apache.impala.thrift.TDdlExecResponse;
|
||||
import org.apache.impala.thrift.TErrorCode;
|
||||
import org.apache.impala.thrift.TGrantRevokePrivParams;
|
||||
import org.apache.impala.thrift.TGrantRevokeRoleParams;
|
||||
import org.apache.impala.thrift.TResultSet;
|
||||
import org.apache.impala.thrift.TSentryAdminCheckRequest;
|
||||
import org.apache.impala.thrift.TSentryAdminCheckResponse;
|
||||
import org.apache.impala.thrift.TShowGrantPrincipalParams;
|
||||
import org.apache.impala.thrift.TShowRolesParams;
|
||||
import org.apache.impala.thrift.TShowRolesResult;
|
||||
import org.apache.impala.util.ClassUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static org.apache.impala.thrift.TPrincipalType.ROLE;
|
||||
|
||||
/**
|
||||
* An implementation of {@link AuthorizationManager} for Impalad that uses Sentry.
|
||||
*
|
||||
* The methods here use the authorization data stored in Impalad catalog via
|
||||
* {@link org.apache.impala.authorization.AuthorizationPolicy}.
|
||||
*
|
||||
* Operations not supported by Sentry will throw an {@link UnsupportedFeatureException}.
|
||||
*/
|
||||
public class SentryImpaladAuthorizationManager implements AuthorizationManager {
|
||||
private static final Logger LOG =
|
||||
LoggerFactory.getLogger(SentryImpaladAuthorizationManager.class);
|
||||
|
||||
private final FeCatalogManager catalog_;
|
||||
private final Supplier<? extends SentryAuthorizationChecker> authzChecker_;
|
||||
|
||||
public SentryImpaladAuthorizationManager(FeCatalogManager catalog,
|
||||
Supplier<? extends AuthorizationChecker> authzChecker) {
|
||||
Preconditions.checkNotNull(catalog);
|
||||
Preconditions.checkNotNull(authzChecker);
|
||||
Preconditions.checkArgument(authzChecker.get() instanceof SentryAuthorizationChecker);
|
||||
catalog_ = catalog;
|
||||
authzChecker_ = (Supplier<SentryAuthorizationChecker>) authzChecker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createRole(User requestingUser, TCreateDropRoleParams params,
|
||||
TDdlExecResponse response) throws ImpalaException {
|
||||
throw new UnsupportedOperationException(String.format(
|
||||
"%s is not supported in Impalad", ClassUtil.getMethodName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dropRole(User requestingUser, TCreateDropRoleParams params,
|
||||
TDdlExecResponse response) throws ImpalaException {
|
||||
throw new UnsupportedOperationException(String.format(
|
||||
"%s is not supported in Impalad", ClassUtil.getMethodName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TShowRolesResult getRoles(TShowRolesParams params) throws ImpalaException {
|
||||
Set<String> groups = authzChecker_.get()
|
||||
.getUserGroups(new User(params.requesting_user));
|
||||
|
||||
// Check if the user is part of the group (case-sensitive) this SHOW ROLES
|
||||
// statement is targeting. If they are already a member of the group,
|
||||
// the admin requirement can be removed.
|
||||
// If the the statement is SHOW CURRENT ROLES, the admin requirement can also be
|
||||
// removed.
|
||||
boolean adminOp =
|
||||
!(groups.contains(params.getGrant_group()) || params.is_show_current_roles);
|
||||
|
||||
if (adminOp) {
|
||||
validateSentryAdmin(params.getRequesting_user());
|
||||
}
|
||||
|
||||
TShowRolesResult result = new TShowRolesResult();
|
||||
List<Role> roles = Lists.newArrayList();
|
||||
if (params.isIs_show_current_roles() || params.isSetGrant_group()) {
|
||||
User user = new User(params.getRequesting_user());
|
||||
Set<String> groupNames;
|
||||
if (params.isIs_show_current_roles()) {
|
||||
groupNames = authzChecker_.get().getUserGroups(user);
|
||||
} else {
|
||||
Preconditions.checkState(params.isSetGrant_group());
|
||||
groupNames = Sets.newHashSet(params.getGrant_group());
|
||||
}
|
||||
for (String groupName: groupNames) {
|
||||
roles.addAll(catalog_.getOrCreateCatalog().getAuthPolicy()
|
||||
.getGrantedRoles(groupName));
|
||||
}
|
||||
} else {
|
||||
Preconditions.checkState(!params.isIs_show_current_roles());
|
||||
roles = catalog_.getOrCreateCatalog().getAuthPolicy().getAllRoles();
|
||||
}
|
||||
|
||||
result.setRole_names(Lists.newArrayListWithExpectedSize(roles.size()));
|
||||
for (Role role: roles) {
|
||||
result.getRole_names().add(role.getName());
|
||||
}
|
||||
|
||||
Collections.sort(result.getRole_names());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void grantRoleToGroup(User requestingUser, TGrantRevokeRoleParams params,
|
||||
TDdlExecResponse response) throws ImpalaException {
|
||||
throw new UnsupportedOperationException(String.format(
|
||||
"%s is not supported in Impalad", ClassUtil.getMethodName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void revokeRoleFromGroup(User requestingUser, TGrantRevokeRoleParams params,
|
||||
TDdlExecResponse response) throws ImpalaException {
|
||||
throw new UnsupportedOperationException(String.format(
|
||||
"%s is not supported in Impalad", ClassUtil.getMethodName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void grantPrivilegeToRole(TCatalogServiceRequestHeader header,
|
||||
TGrantRevokePrivParams params, TDdlExecResponse response) throws ImpalaException {
|
||||
throw new UnsupportedOperationException(String.format(
|
||||
"%s is not supported in Impalad", ClassUtil.getMethodName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void revokePrivilegeFromRole(TCatalogServiceRequestHeader header,
|
||||
TGrantRevokePrivParams params, TDdlExecResponse response) throws ImpalaException {
|
||||
throw new UnsupportedOperationException(String.format(
|
||||
"%s is not supported in Impalad", ClassUtil.getMethodName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void grantPrivilegeToUser(TCatalogServiceRequestHeader header,
|
||||
TGrantRevokePrivParams params, TDdlExecResponse response) throws ImpalaException {
|
||||
throw new UnsupportedOperationException(String.format(
|
||||
"%s is not supported in Impalad", ClassUtil.getMethodName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void revokePrivilegeFromUser(TCatalogServiceRequestHeader header,
|
||||
TGrantRevokePrivParams params, TDdlExecResponse response) throws ImpalaException {
|
||||
throw new UnsupportedOperationException(String.format(
|
||||
"%s is not supported in Impalad", ClassUtil.getMethodName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void grantPrivilegeToGroup(TCatalogServiceRequestHeader header,
|
||||
TGrantRevokePrivParams params, TDdlExecResponse response) throws ImpalaException {
|
||||
throw new UnsupportedOperationException(String.format(
|
||||
"%s is not supported in Impalad", ClassUtil.getMethodName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void revokePrivilegeFromGroup(TCatalogServiceRequestHeader header,
|
||||
TGrantRevokePrivParams params, TDdlExecResponse response) throws ImpalaException {
|
||||
throw new UnsupportedOperationException(String.format(
|
||||
"%s is not supported in Impalad", ClassUtil.getMethodName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TResultSet getPrivileges(TShowGrantPrincipalParams params)
|
||||
throws ImpalaException {
|
||||
Principal principal = (params.principal_type == ROLE) ?
|
||||
catalog_.getOrCreateCatalog().getAuthPolicy().getPrincipal(params.getName(),
|
||||
params.getPrincipal_type()) :
|
||||
Principal.newInstance(params.name, params.principal_type, new HashSet<>());
|
||||
|
||||
if (principal == null) {
|
||||
throw new AuthorizationException(String.format("%s '%s' does not exist.",
|
||||
Principal.toString(params.principal_type), params.name));
|
||||
}
|
||||
|
||||
if (isAdminOp(params, principal, authzChecker_.get())) {
|
||||
validateSentryAdmin(params.getRequesting_user());
|
||||
}
|
||||
|
||||
switch (params.getPrincipal_type()) {
|
||||
case USER:
|
||||
Set<String> groupNames = authzChecker_.get().getUserGroups(
|
||||
new User(params.getName()));
|
||||
return catalog_.getOrCreateCatalog().getAuthPolicy().getUserPrivileges(
|
||||
params.getName(), groupNames, params.getPrivilege());
|
||||
case ROLE:
|
||||
return catalog_.getOrCreateCatalog().getAuthPolicy().getRolePrivileges(
|
||||
params.getName(), params.getPrivilege());
|
||||
case GROUP:
|
||||
throw new UnsupportedFeatureException(
|
||||
"SHOW GRANT GROUP is not supported by Sentry.");
|
||||
default:
|
||||
throw new InternalException(String.format("Unexpected TPrincipalType: %s",
|
||||
params.getPrincipal_type()));
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isAdminOp(TShowGrantPrincipalParams params, Principal principal,
|
||||
SentryAuthorizationChecker authzChecker) throws ImpalaException {
|
||||
Set<String> groupNames = authzChecker.getUserGroups(new User(params.requesting_user));
|
||||
|
||||
switch (params.principal_type) {
|
||||
case USER:
|
||||
return !principal.getName().equals(params.requesting_user);
|
||||
case GROUP:
|
||||
case ROLE:
|
||||
return Sets.intersection(groupNames, principal.getGrantGroups()).isEmpty();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDatabaseOwnerPrivilege(String serverName, String databaseName,
|
||||
String oldOwner, PrincipalType oldOwnerType, String newOwner,
|
||||
PrincipalType newOwnerType, TDdlExecResponse response) throws ImpalaException {
|
||||
throw new UnsupportedOperationException(String.format(
|
||||
"%s is not supported in Impalad", ClassUtil.getMethodName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTableOwnerPrivilege(String serverName, String databaseName,
|
||||
String tableName, String oldOwner, PrincipalType oldOwnerType, String newOwner,
|
||||
PrincipalType newOwnerType, TDdlExecResponse response) throws ImpalaException {
|
||||
throw new UnsupportedOperationException(String.format(
|
||||
"%s is not supported in Impalad", ClassUtil.getMethodName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthorizationDelta refreshAuthorization(boolean resetVersions) {
|
||||
throw new UnsupportedOperationException(String.format(
|
||||
"%s is not supported in Impalad", ClassUtil.getMethodName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if the given user is a Sentry admin. The Sentry admin check will make an
|
||||
* RPC call to the Catalog server. This check is necessary because some operations
|
||||
* in this class does not need to make a call to Sentry, e.g. "show roles" and
|
||||
* "show grant" because the authorization data can be retrieved directly from the
|
||||
* Impalad catalog without going to Sentry. In order to ensure those operations can
|
||||
* only be executed by a Sentry admin, a separate call to the Catalog server is needed
|
||||
* to check if the given user is a Sentry admin.
|
||||
*
|
||||
* @throws AuthorizationException thrown when a given user is not a Sentry admin.
|
||||
*/
|
||||
private static void validateSentryAdmin(String user) throws ImpalaException {
|
||||
TSentryAdminCheckRequest request = new TSentryAdminCheckRequest();
|
||||
TCatalogServiceRequestHeader header = new TCatalogServiceRequestHeader();
|
||||
header.setRequesting_user(user);
|
||||
request.setHeader(header);
|
||||
|
||||
byte[] thriftReq = JniUtil.serializeToThrift(request);
|
||||
byte[] thriftRes = FeSupport.CheckSentryAdmin(thriftReq);
|
||||
|
||||
TSentryAdminCheckResponse response = new TSentryAdminCheckResponse();
|
||||
JniUtil.deserializeThrift(response, thriftRes);
|
||||
|
||||
if (response.getStatus().getStatus_code() != TErrorCode.OK) {
|
||||
throw new InternalException(String.format("Error requesting SentryAdminCheck: %s",
|
||||
Joiner.on("\n").join(response.getStatus().getError_msgs())));
|
||||
}
|
||||
if (!response.isIs_admin()) {
|
||||
throw new AuthorizationException(String.format("User '%s' does not have " +
|
||||
"privileges to access the requested policy metadata.", user));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
// 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.authorization.sentry;
|
||||
|
||||
/**
|
||||
* Thrown as a generic exception when processing Sentry policy.
|
||||
*/
|
||||
public class SentryPolicyReaderException extends RuntimeException {
|
||||
public SentryPolicyReaderException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
public SentryPolicyReaderException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public SentryPolicyReaderException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
@@ -1,544 +0,0 @@
|
||||
// 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.authorization.sentry;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.impala.catalog.Principal;
|
||||
import org.apache.impala.thrift.TPrincipalType;
|
||||
import org.apache.sentry.api.service.thrift.SentryPolicyServiceClient;
|
||||
import org.apache.sentry.api.service.thrift.TSentryGrantOption;
|
||||
import org.apache.sentry.api.service.thrift.TSentryPrivilege;
|
||||
import org.apache.sentry.api.service.thrift.TSentryRole;
|
||||
import org.apache.sentry.core.common.exception.SentryUserException;
|
||||
import org.apache.sentry.service.thrift.SentryServiceClientFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.apache.impala.analysis.PrivilegeSpec;
|
||||
import org.apache.impala.authorization.User;
|
||||
import org.apache.impala.authorization.AuthorizationException;
|
||||
import org.apache.impala.common.ImpalaException;
|
||||
import org.apache.impala.common.InternalException;
|
||||
import org.apache.impala.thrift.TPrivilege;
|
||||
import org.apache.impala.thrift.TPrivilegeLevel;
|
||||
import org.apache.impala.thrift.TPrivilegeScope;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
/**
|
||||
* Wrapper around the SentryService APIs that are used by Impala and Impala tests.
|
||||
*/
|
||||
public class SentryPolicyService {
|
||||
private final static Logger LOG = LoggerFactory.getLogger(SentryPolicyService.class);
|
||||
private final String ACCESS_DENIED_ERROR_MSG =
|
||||
"User '%s' does not have privileges to execute: %s";
|
||||
private final SentryConfig config_;
|
||||
|
||||
/**
|
||||
* Wrapper around a SentryPolicyServiceClient.
|
||||
* TODO: When SENTRY-296 is resolved we can more easily cache connections instead of
|
||||
* opening a new connection for each request.
|
||||
*/
|
||||
class SentryServiceClient implements AutoCloseable {
|
||||
private final SentryPolicyServiceClient client_;
|
||||
|
||||
/**
|
||||
* Creates and opens a new Sentry Service thrift client.
|
||||
*/
|
||||
public SentryServiceClient() throws InternalException {
|
||||
client_ = createClient();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the underlying SentryPolicyServiceClient.
|
||||
*/
|
||||
public SentryPolicyServiceClient get() {
|
||||
return client_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this client back to the connection pool. Can be called multiple times.
|
||||
*/
|
||||
public void close() throws InternalException {
|
||||
try {
|
||||
client_.close();
|
||||
} catch (Exception e) {
|
||||
throw new InternalException("Error closing client: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new client to the SentryService.
|
||||
*/
|
||||
private SentryPolicyServiceClient createClient() throws InternalException {
|
||||
SentryPolicyServiceClient client;
|
||||
try {
|
||||
client = SentryServiceClientFactory.create(config_.getConfig());
|
||||
} catch (Exception e) {
|
||||
throw new InternalException("Error creating Sentry Service client: ", e);
|
||||
}
|
||||
return client;
|
||||
}
|
||||
}
|
||||
|
||||
public SentryPolicyService(SentryConfig config) {
|
||||
config_ = config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops a role.
|
||||
*
|
||||
* @param requestingUser - The requesting user.
|
||||
* @param roleName - The role to drop.
|
||||
* @param ifExists - If true, no error is thrown if the role does not exist.
|
||||
* @throws ImpalaException - On any error dropping the role.
|
||||
*/
|
||||
public void dropRole(User requestingUser, String roleName, boolean ifExists)
|
||||
throws ImpalaException {
|
||||
if (LOG.isTraceEnabled()) {
|
||||
LOG.trace(String.format("Dropping role: %s on behalf of: %s", roleName,
|
||||
requestingUser.getName()));
|
||||
}
|
||||
SentryServiceClient client = new SentryServiceClient();
|
||||
try {
|
||||
if (ifExists) {
|
||||
client.get().dropRoleIfExists(requestingUser.getShortName(), roleName);
|
||||
} else {
|
||||
client.get().dropRole(requestingUser.getShortName(), roleName);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (SentryUtil.isSentryAccessDenied(e)) {
|
||||
throw new AuthorizationException(String.format(ACCESS_DENIED_ERROR_MSG,
|
||||
requestingUser.getName(), "DROP_ROLE"));
|
||||
}
|
||||
throw new InternalException("Error dropping role: ", e);
|
||||
} finally {
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new role.
|
||||
*
|
||||
* @param requestingUser - The requesting user.
|
||||
* @param roleName - The role to create.
|
||||
* @param ifNotExists - If true, no error is thrown if the role already exists.
|
||||
* @throws ImpalaException - On any error creating the role.
|
||||
*/
|
||||
public void createRole(User requestingUser, String roleName, boolean ifNotExists)
|
||||
throws ImpalaException {
|
||||
if (LOG.isTraceEnabled()) {
|
||||
LOG.trace(String.format("Creating role: %s on behalf of: %s", roleName,
|
||||
requestingUser.getName()));
|
||||
}
|
||||
SentryServiceClient client = new SentryServiceClient();
|
||||
try {
|
||||
client.get().createRole(requestingUser.getShortName(), roleName);
|
||||
} catch (Exception e) {
|
||||
if (SentryUtil.isSentryAccessDenied(e)) {
|
||||
throw new AuthorizationException(String.format(ACCESS_DENIED_ERROR_MSG,
|
||||
requestingUser.getName(), "CREATE_ROLE"));
|
||||
}
|
||||
if (SentryUtil.isSentryAlreadyExists(e)) {
|
||||
if (ifNotExists) return;
|
||||
throw new InternalException("Error creating role: " + e.getMessage(), e);
|
||||
}
|
||||
throw new InternalException("Error creating role: " + e.getMessage(), e);
|
||||
} finally {
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Grants a role to a group.
|
||||
*
|
||||
* @param requestingUser - The requesting user.
|
||||
* @param roleName - The role to grant to a group. Role must already exist.
|
||||
* @param groupName - The group to grant the role to.
|
||||
* @throws ImpalaException - On any error.
|
||||
*/
|
||||
public void grantRoleToGroup(User requestingUser, String roleName, String groupName)
|
||||
throws ImpalaException {
|
||||
if (LOG.isTraceEnabled()) {
|
||||
LOG.trace(String.format("Granting role '%s' to group '%s' on behalf of: %s",
|
||||
roleName, groupName, requestingUser.getName()));
|
||||
}
|
||||
SentryServiceClient client = new SentryServiceClient();
|
||||
try {
|
||||
client.get().grantRoleToGroup(requestingUser.getShortName(), groupName, roleName);
|
||||
} catch (Exception e) {
|
||||
if (SentryUtil.isSentryAccessDenied(e)) {
|
||||
throw new AuthorizationException(String.format(ACCESS_DENIED_ERROR_MSG,
|
||||
requestingUser.getName(), "GRANT_ROLE"));
|
||||
}
|
||||
throw new InternalException(
|
||||
"Error making 'grantRoleToGroup' RPC to Sentry Service: ", e);
|
||||
} finally {
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Grants a role to the groups.
|
||||
*
|
||||
* @param requestingUser - The requesting user.
|
||||
* @param roleName - The role to grant to the groups. Role must already exist.
|
||||
* @param groupNames - The groups to grant the role to.
|
||||
* @throws ImpalaException - On any error.
|
||||
*/
|
||||
public void grantRoleToGroups(User requestingUser, String roleName,
|
||||
Set<String> groupNames) throws ImpalaException {
|
||||
for (String groupName : groupNames) {
|
||||
grantRoleToGroup(requestingUser, roleName, groupName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a role from a group.
|
||||
*
|
||||
* @param requestingUser - The requesting user.
|
||||
* @param roleName - The role name to remove.
|
||||
* @param groupName - The group to remove the role from.
|
||||
* @throws InternalException - On any error.
|
||||
*/
|
||||
public void revokeRoleFromGroup(User requestingUser, String roleName, String groupName)
|
||||
throws ImpalaException {
|
||||
if (LOG.isTraceEnabled()) {
|
||||
LOG.trace(String.format("Revoking role '%s' from group '%s' on behalf of: %s",
|
||||
roleName, groupName, requestingUser.getName()));
|
||||
}
|
||||
SentryServiceClient client = new SentryServiceClient();
|
||||
try {
|
||||
client.get().revokeRoleFromGroup(requestingUser.getShortName(),
|
||||
groupName, roleName);
|
||||
} catch (Exception e) {
|
||||
if (SentryUtil.isSentryAccessDenied(e)) {
|
||||
throw new AuthorizationException(String.format(ACCESS_DENIED_ERROR_MSG,
|
||||
requestingUser.getName(), "REVOKE_ROLE"));
|
||||
}
|
||||
throw new InternalException(
|
||||
"Error making 'revokeRoleFromGroup' RPC to Sentry Service: ", e);
|
||||
} finally {
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Grants a privilege to an existing role.
|
||||
*/
|
||||
public void grantRolePrivilege(User requestingUser, String roleName,
|
||||
TPrivilege privilege) throws ImpalaException {
|
||||
grantRolePrivileges(requestingUser, roleName, Lists.newArrayList(privilege));
|
||||
}
|
||||
|
||||
/**
|
||||
* Grants privileges to an existing role.
|
||||
*
|
||||
* @param requestingUser - The requesting user.
|
||||
* @param roleName - The role to grant privileges to (case insensitive).
|
||||
* @param privileges - The privileges to grant.
|
||||
* @throws ImpalaException - On any error
|
||||
*/
|
||||
public void grantRolePrivileges(User requestingUser, String roleName,
|
||||
List<TPrivilege> privileges) throws ImpalaException {
|
||||
Preconditions.checkState(!privileges.isEmpty());
|
||||
TPrivilege privilege = privileges.get(0);
|
||||
TPrivilegeScope scope = privilege.getScope();
|
||||
if (LOG.isTraceEnabled()) {
|
||||
LOG.trace(String.format(
|
||||
"Granting role '%s' '%s' privilege on '%s' on behalf of: %s",
|
||||
roleName, privilege.getPrivilege_level().toString(), scope.toString(),
|
||||
requestingUser.getName()));
|
||||
}
|
||||
// Verify that all privileges have the same scope.
|
||||
for (int i = 1; i < privileges.size(); ++i) {
|
||||
Preconditions.checkState(privileges.get(i).getScope() == scope, "All the " +
|
||||
"privileges must have the same scope.");
|
||||
}
|
||||
Preconditions.checkState(scope == TPrivilegeScope.COLUMN || privileges.size() == 1,
|
||||
"Cannot grant multiple " + scope + " privileges with a singe RPC to the " +
|
||||
"Sentry Service.");
|
||||
SentryServiceClient client = new SentryServiceClient();
|
||||
try {
|
||||
switch (scope) {
|
||||
case SERVER:
|
||||
client.get().grantServerPrivilege(requestingUser.getShortName(), roleName,
|
||||
privilege.getServer_name(), privilege.getPrivilege_level().toString(),
|
||||
privilege.isHas_grant_opt());
|
||||
break;
|
||||
case DATABASE:
|
||||
client.get().grantDatabasePrivilege(requestingUser.getShortName(), roleName,
|
||||
privilege.getServer_name(), privilege.getDb_name(),
|
||||
privilege.getPrivilege_level().toString(),
|
||||
privilege.isHas_grant_opt());
|
||||
break;
|
||||
case TABLE:
|
||||
client.get().grantTablePrivilege(requestingUser.getShortName(), roleName,
|
||||
privilege.getServer_name(), privilege.getDb_name(),
|
||||
privilege.getTable_name(), privilege.getPrivilege_level().toString(),
|
||||
privilege.isHas_grant_opt());
|
||||
break;
|
||||
case COLUMN:
|
||||
client.get().grantColumnsPrivileges(requestingUser.getShortName(), roleName,
|
||||
privilege.getServer_name(), privilege.getDb_name(),
|
||||
privilege.getTable_name(), getColumnNames(privileges),
|
||||
privilege.getPrivilege_level().toString(), privilege.isHas_grant_opt());
|
||||
break;
|
||||
case URI:
|
||||
client.get().grantURIPrivilege(requestingUser.getShortName(),
|
||||
roleName, privilege.getServer_name(), privilege.getUri(),
|
||||
privilege.isHas_grant_opt());
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (SentryUtil.isSentryAccessDenied(e)) {
|
||||
throw new AuthorizationException(String.format(ACCESS_DENIED_ERROR_MSG,
|
||||
requestingUser.getName(), "GRANT_PRIVILEGE"));
|
||||
}
|
||||
throw new InternalException(
|
||||
"Error making 'grantPrivilege*' RPC to Sentry Service: ", e);
|
||||
} finally {
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Revokes privileges from an existing role.
|
||||
*
|
||||
* @param requestingUser - The requesting user.
|
||||
* @param roleName - The role to revoke privileges from (case insensitive).
|
||||
* @param privileges - The privileges to revoke.
|
||||
* @throws ImpalaException - On any error
|
||||
*/
|
||||
public void revokeRolePrivileges(User requestingUser, String roleName,
|
||||
List<TPrivilege> privileges) throws ImpalaException {
|
||||
Preconditions.checkState(!privileges.isEmpty());
|
||||
TPrivilege privilege = privileges.get(0);
|
||||
TPrivilegeScope scope = privilege.getScope();
|
||||
if (LOG.isTraceEnabled()) {
|
||||
LOG.trace(String.format("Revoking from role '%s' '%s' privilege on '%s' on " +
|
||||
"behalf of: %s", roleName, privilege.getPrivilege_level().toString(),
|
||||
scope.toString(), requestingUser.getName()));
|
||||
}
|
||||
// Verify that all privileges have the same scope.
|
||||
for (int i = 1; i < privileges.size(); ++i) {
|
||||
Preconditions.checkState(privileges.get(i).getScope() == scope, "All the " +
|
||||
"privileges must have the same scope.");
|
||||
}
|
||||
Preconditions.checkState(scope == TPrivilegeScope.COLUMN || privileges.size() == 1,
|
||||
"Cannot revoke multiple " + scope + " privileges with a singe RPC to the " +
|
||||
"Sentry Service.");
|
||||
SentryServiceClient client = new SentryServiceClient();
|
||||
try {
|
||||
switch (scope) {
|
||||
case SERVER:
|
||||
client.get().revokeServerPrivilege(requestingUser.getShortName(), roleName,
|
||||
privilege.getServer_name(), privilege.getPrivilege_level().toString(),
|
||||
/* grant option */ null);
|
||||
break;
|
||||
case DATABASE:
|
||||
client.get().revokeDatabasePrivilege(requestingUser.getShortName(), roleName,
|
||||
privilege.getServer_name(), privilege.getDb_name(),
|
||||
privilege.getPrivilege_level().toString(), null);
|
||||
break;
|
||||
case TABLE:
|
||||
client.get().revokeTablePrivilege(requestingUser.getShortName(), roleName,
|
||||
privilege.getServer_name(), privilege.getDb_name(),
|
||||
privilege.getTable_name(), privilege.getPrivilege_level().toString(),
|
||||
null);
|
||||
break;
|
||||
case COLUMN:
|
||||
client.get().revokeColumnsPrivilege(requestingUser.getShortName(), roleName,
|
||||
privilege.getServer_name(), privilege.getDb_name(),
|
||||
privilege.getTable_name(), getColumnNames(privileges),
|
||||
privilege.getPrivilege_level().toString(), null);
|
||||
break;
|
||||
case URI:
|
||||
client.get().revokeURIPrivilege(requestingUser.getShortName(),
|
||||
roleName, privilege.getServer_name(), privilege.getUri(),
|
||||
null);
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (SentryUtil.isSentryAccessDenied(e)) {
|
||||
throw new AuthorizationException(String.format(ACCESS_DENIED_ERROR_MSG,
|
||||
requestingUser.getName(), "REVOKE_PRIVILEGE"));
|
||||
}
|
||||
throw new InternalException(
|
||||
"Error making 'revokePrivilege*' RPC to Sentry Service: ", e);
|
||||
} finally {
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the column names referenced in a list of column-level privileges.
|
||||
* Verifies that all column-level privileges refer to the same table.
|
||||
*/
|
||||
private List<String> getColumnNames(List<TPrivilege> privileges) {
|
||||
List<String> columnNames = Lists.newArrayList();
|
||||
String tablePath = PrivilegeSpec.getTablePath(privileges.get(0));
|
||||
columnNames.add(privileges.get(0).getColumn_name());
|
||||
// Collect all column names and verify that they belong to the same table.
|
||||
for (int i = 1; i < privileges.size(); ++i) {
|
||||
TPrivilege privilege = privileges.get(i);
|
||||
Preconditions.checkState(tablePath.equals(PrivilegeSpec.getTablePath(privilege))
|
||||
&& privilege.getScope() == TPrivilegeScope.COLUMN);
|
||||
columnNames.add(privileges.get(i).getColumn_name());
|
||||
}
|
||||
return columnNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists all roles.
|
||||
*/
|
||||
public List<TSentryRole> listAllRoles(User requestingUser) throws ImpalaException {
|
||||
SentryServiceClient client = new SentryServiceClient();
|
||||
try {
|
||||
return Lists.newArrayList(client.get().listAllRoles(requestingUser.getShortName()));
|
||||
} catch (Exception e) {
|
||||
if (SentryUtil.isSentryAccessDenied(e)) {
|
||||
throw new AuthorizationException(String.format(ACCESS_DENIED_ERROR_MSG,
|
||||
requestingUser.getName(), "LIST_ROLES"));
|
||||
}
|
||||
throw new InternalException("Error making 'listRoles' RPC to Sentry Service: ", e);
|
||||
} finally {
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists all privileges granted to a role.
|
||||
*/
|
||||
public List<TSentryPrivilege> listRolePrivileges(User requestingUser, String roleName)
|
||||
throws ImpalaException {
|
||||
SentryServiceClient client = new SentryServiceClient();
|
||||
try {
|
||||
return Lists.newArrayList(client.get().listAllPrivilegesByRoleName(
|
||||
requestingUser.getShortName(), roleName));
|
||||
} catch (Exception e) {
|
||||
if (SentryUtil.isSentryAccessDenied(e)) {
|
||||
throw new AuthorizationException(String.format(ACCESS_DENIED_ERROR_MSG,
|
||||
requestingUser.getName(), "LIST_ROLE_PRIVILEGES"));
|
||||
}
|
||||
throw new InternalException("Error making 'listAllPrivilegesByRoleName' RPC to " +
|
||||
"Sentry Service: ", e);
|
||||
} finally {
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a map of all roles with their associated privileges.
|
||||
*/
|
||||
public Map<String, Set<TSentryPrivilege>> listAllRolesPrivileges(User requestingUser)
|
||||
throws ImpalaException {
|
||||
return listAllPrincipalsPrivileges(requestingUser, TPrincipalType.ROLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a map of all users with their associated privileges.
|
||||
*/
|
||||
public Map<String, Set<TSentryPrivilege>> listAllUsersPrivileges(User requestingUser)
|
||||
throws ImpalaException {
|
||||
return listAllPrincipalsPrivileges(requestingUser, TPrincipalType.USER);
|
||||
}
|
||||
|
||||
private Map<String, Set<TSentryPrivilege>> listAllPrincipalsPrivileges(
|
||||
User requestingUser, TPrincipalType type) throws ImpalaException {
|
||||
SentryServiceClient client = new SentryServiceClient();
|
||||
try {
|
||||
return type == TPrincipalType.ROLE ?
|
||||
client.get().listAllRolesPrivileges(requestingUser.getShortName()) :
|
||||
client.get().listAllUsersPrivileges(requestingUser.getShortName());
|
||||
} catch (Exception e) {
|
||||
if (SentryUtil.isSentryAccessDenied(e)) {
|
||||
throw new AuthorizationException(String.format(ACCESS_DENIED_ERROR_MSG,
|
||||
requestingUser.getName(),
|
||||
type == TPrincipalType.ROLE ?
|
||||
"LIST_ALL_ROLES_PRIVILEGES" : "LIST_ALL_USERS_PRIVILEGES"));
|
||||
}
|
||||
throw new InternalException(String.format("Error making '%s' RPC to " +
|
||||
"Sentry Service: ",
|
||||
type == TPrincipalType.ROLE ?
|
||||
"listAllRolesPrivileges" :
|
||||
"listAllUsersPrivileges"), e);
|
||||
} finally {
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the configuration value for the specified key. Will return an empty string
|
||||
* if no value is set.
|
||||
*/
|
||||
public String getConfigValue(String key) throws ImpalaException {
|
||||
try (SentryServiceClient client = new SentryServiceClient()) {
|
||||
return client.get().getConfigValue(key, "");
|
||||
} catch (SentryUserException e) {
|
||||
throw new InternalException("Error making 'getConfigValue' RPC to Sentry Service: ",
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function that converts a TSentryPrivilege to an Impala TPrivilege object.
|
||||
*/
|
||||
public static TPrivilege sentryPrivilegeToTPrivilege(TSentryPrivilege sentryPriv,
|
||||
Principal principal) {
|
||||
TPrivilege privilege = new TPrivilege();
|
||||
privilege.setServer_name(sentryPriv.getServerName());
|
||||
if (sentryPriv.isSetDbName()) privilege.setDb_name(sentryPriv.getDbName());
|
||||
if (sentryPriv.isSetTableName()) privilege.setTable_name(sentryPriv.getTableName());
|
||||
if (sentryPriv.isSetColumnName()) {
|
||||
privilege.setColumn_name(sentryPriv.getColumnName());
|
||||
}
|
||||
if (sentryPriv.isSetURI()) privilege.setUri(sentryPriv.getURI());
|
||||
privilege.setScope(Enum.valueOf(TPrivilegeScope.class,
|
||||
sentryPriv.getPrivilegeScope().toUpperCase()));
|
||||
if (sentryPriv.getAction().equals("*")) {
|
||||
privilege.setPrivilege_level(TPrivilegeLevel.ALL);
|
||||
} else {
|
||||
privilege.setPrivilege_level(Enum.valueOf(TPrivilegeLevel.class,
|
||||
sentryPriv.getAction().toUpperCase()));
|
||||
}
|
||||
privilege.setCreate_time_ms(sentryPriv.getCreateTime());
|
||||
if (sentryPriv.isSetGrantOption() &&
|
||||
sentryPriv.getGrantOption() == TSentryGrantOption.TRUE) {
|
||||
privilege.setHas_grant_opt(true);
|
||||
} else {
|
||||
privilege.setHas_grant_opt(false);
|
||||
}
|
||||
privilege.setPrincipal_id(principal.getId());
|
||||
privilege.setPrincipal_type(principal.getPrincipalType());
|
||||
return privilege;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given user is a Sentry admin.
|
||||
*/
|
||||
public boolean isSentryAdmin(User user)
|
||||
throws InternalException, SentryUserException {
|
||||
try (SentryServiceClient client = new SentryServiceClient()) {
|
||||
return client.get().isAdmin(user.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,651 +0,0 @@
|
||||
// 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.authorization.sentry;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import org.apache.impala.catalog.CatalogException;
|
||||
import org.apache.impala.catalog.CatalogServiceCatalog;
|
||||
import org.apache.impala.catalog.Principal;
|
||||
import org.apache.impala.catalog.PrincipalPrivilege;
|
||||
import org.apache.impala.catalog.Role;
|
||||
import org.apache.impala.common.Reference;
|
||||
import org.apache.impala.thrift.TCatalogObject;
|
||||
import org.apache.impala.thrift.TPrincipalType;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.apache.sentry.api.service.thrift.TSentryGroup;
|
||||
import org.apache.sentry.api.service.thrift.TSentryPrivilege;
|
||||
import org.apache.sentry.api.service.thrift.TSentryRole;
|
||||
import org.apache.impala.authorization.User;
|
||||
import org.apache.impala.common.ImpalaException;
|
||||
import org.apache.impala.common.ImpalaRuntimeException;
|
||||
import org.apache.impala.common.InternalException;
|
||||
import org.apache.impala.service.BackendConfig;
|
||||
import org.apache.impala.thrift.TPrivilege;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import org.apache.sentry.core.common.exception.SentryUserException;
|
||||
import org.apache.sentry.service.common.SentryOwnerPrivilegeType;
|
||||
import org.apache.sentry.service.common.ServiceConstants;
|
||||
import org.apache.thrift.transport.TTransportException;
|
||||
|
||||
/**
|
||||
* Thread safe class that acts as a link between the Sentry Service and the Catalog
|
||||
* to ensure both places are updated consistently. More specifically, this class
|
||||
* synchronizes updates to the Sentry Service and the Impala catalog to ensure
|
||||
* they are applied atomically (in Impala's view) and only if reading/writing the
|
||||
* policy via the Sentry Service succeeds. Note that there may be external updates
|
||||
* to the Sentry Service that cannot be protected against.
|
||||
* It also periodically refreshes the authorization policy metadata and updates the
|
||||
* catalog with any changes. Because any catalog updates need to be synchronized with
|
||||
* updates from GRANT/REVOKE statements, it makes sense for this class to
|
||||
* synchronize all modifications.
|
||||
*/
|
||||
public class SentryProxy {
|
||||
/**
|
||||
* This class keeps track of catalog objects added and removed.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
protected static class AuthorizationDelta {
|
||||
private final List<TCatalogObject> added_;
|
||||
private final List<TCatalogObject> removed_;
|
||||
|
||||
public AuthorizationDelta() {
|
||||
this(new ArrayList<>(), new ArrayList<>());
|
||||
}
|
||||
|
||||
public AuthorizationDelta(List<TCatalogObject> added,
|
||||
List<TCatalogObject> removed) {
|
||||
added_ = added;
|
||||
removed_ = removed;
|
||||
}
|
||||
|
||||
public List<TCatalogObject> getAddedCatalogObjects() { return added_; }
|
||||
public List<TCatalogObject> getRemovedCatalogObjects() { return removed_; }
|
||||
}
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(SentryProxy.class);
|
||||
|
||||
// Used to periodically poll the Sentry Service and updates the catalog with any
|
||||
// changes.
|
||||
private final ScheduledExecutorService policyReader_ =
|
||||
Executors.newScheduledThreadPool(1);
|
||||
|
||||
// The Catalog the SentryPolicyUpdater is associated with.
|
||||
private final CatalogServiceCatalog catalog_;
|
||||
|
||||
// The interface to access the Sentry Policy Service to read policy metadata.
|
||||
private final SentryPolicyService sentryPolicyService_;
|
||||
|
||||
// This is the user that the Catalog Service is running as. For kerberized clusters,
|
||||
// this is set to the Kerberos principal of Catalog. This user should always be a
|
||||
// Sentry Service admin => have full rights to read/update the Sentry Service.
|
||||
private final User processUser_;
|
||||
|
||||
// The value for the object ownership config.
|
||||
private final String objectOwnershipConfigValue_;
|
||||
|
||||
public SentryProxy(SentryAuthorizationConfig authzConfig, CatalogServiceCatalog catalog)
|
||||
throws ImpalaException {
|
||||
Preconditions.checkNotNull(authzConfig);
|
||||
SentryConfig sentryConfig = authzConfig.getSentryConfig();
|
||||
Preconditions.checkNotNull(catalog);
|
||||
Preconditions.checkNotNull(sentryConfig);
|
||||
catalog_ = catalog;
|
||||
String kerberosPrincipal = BackendConfig.INSTANCE.getBackendCfg().getPrincipal();
|
||||
if (Strings.isNullOrEmpty(kerberosPrincipal)) {
|
||||
processUser_ = new User(System.getProperty("user.name"));
|
||||
} else {
|
||||
processUser_ = new User(kerberosPrincipal);
|
||||
}
|
||||
sentryPolicyService_ = new SentryPolicyService(sentryConfig);
|
||||
|
||||
// For some tests, we create a config but may not have a config file.
|
||||
if (sentryConfig.getConfigFile() != null && !sentryConfig.getConfigFile().isEmpty()) {
|
||||
objectOwnershipConfigValue_ = sentryPolicyService_
|
||||
.getConfigValue(ServiceConstants.ServerConfig
|
||||
.SENTRY_DB_POLICY_STORE_OWNER_AS_PRIVILEGE);
|
||||
} else {
|
||||
objectOwnershipConfigValue_ = SentryOwnerPrivilegeType.NONE.toString();
|
||||
}
|
||||
// We configure the PolicyReader to swallow any exception because we do not want to
|
||||
// kill the PolicyReader background thread on exception.
|
||||
policyReader_.scheduleAtFixedRate(new PolicyReader(/*reset versions*/ false,
|
||||
/*swallow exception*/ true, new AuthorizationDelta()), 0,
|
||||
BackendConfig.INSTANCE.getSentryCatalogPollingFrequency(), TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the authorization policy metadata by querying the Sentry Policy Service.
|
||||
* There is currently no way to get a snapshot of the policy from the Sentry Service,
|
||||
* so it is possible that Impala will end up in a state that is not consistent with a
|
||||
* state the Sentry Service has ever been in. For example, consider the case where a
|
||||
* refresh is running and all privileges for Principal A have been processed. Before
|
||||
* moving to Principal B, the user revokes a privilege from Principal A and grants it to
|
||||
* Principal B. Impala will temporarily (until the next refresh) think the privilege is
|
||||
* granted to Principal A AND to Principal B.
|
||||
* TODO: Think more about consistency as well as how to recover from errors that leave
|
||||
* the policy in a potentially inconsistent state (an RPC fails part-way through a
|
||||
* refresh). We should also consider applying this entire update to the catalog
|
||||
* atomically.
|
||||
*/
|
||||
private class PolicyReader implements Runnable {
|
||||
private final boolean resetVersions_;
|
||||
// A flag to indicate whether or not to swallow any exception thrown.
|
||||
private final boolean swallowException_;
|
||||
// Keep track of catalog objects added/removed.
|
||||
private final AuthorizationDelta authzDelta_;
|
||||
|
||||
public PolicyReader(boolean resetVersions, boolean swallowException,
|
||||
AuthorizationDelta authzDelta) {
|
||||
resetVersions_ = resetVersions;
|
||||
swallowException_ = swallowException;
|
||||
authzDelta_ = authzDelta;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
synchronized (SentryProxy.this) {
|
||||
refreshSentryAuthorization(catalog_, sentryPolicyService_, processUser_,
|
||||
resetVersions_, swallowException_, authzDelta_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes Sentry authorization and updates the catalog.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static void refreshSentryAuthorization(CatalogServiceCatalog catalog,
|
||||
SentryPolicyService sentryPolicyService, User processUser, boolean resetVersions,
|
||||
boolean swallowException, AuthorizationDelta authzDelta) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
try {
|
||||
refreshRolePrivileges(catalog, sentryPolicyService, processUser, resetVersions,
|
||||
authzDelta);
|
||||
refreshUserPrivileges(catalog, sentryPolicyService, processUser, resetVersions,
|
||||
authzDelta);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error refreshing Sentry policy: ", e);
|
||||
if (swallowException) return;
|
||||
// We need to differentiate between Sentry not available exception and
|
||||
// any other exceptions.
|
||||
if (e.getCause() != null && e.getCause() instanceof SentryUserException) {
|
||||
Throwable sentryException = e.getCause();
|
||||
if (sentryException.getCause() != null &&
|
||||
sentryException.getCause() instanceof TTransportException) {
|
||||
throw new SentryUnavailableException(e);
|
||||
}
|
||||
}
|
||||
throw new SentryPolicyReaderException(e);
|
||||
} finally {
|
||||
LOG.debug("Refreshing Sentry policy took " +
|
||||
(System.currentTimeMillis() - startTime) + "ms");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates all roles and their associated privileges in the catalog by adding,
|
||||
* removing, and replacing the catalog objects to match those in Sentry since
|
||||
* the last sentry sync update.
|
||||
*/
|
||||
private static void refreshRolePrivileges(CatalogServiceCatalog catalog,
|
||||
SentryPolicyService sentryPolicyService, User processUser, boolean resetVersions,
|
||||
AuthorizationDelta authzDelta) throws ImpalaException {
|
||||
// Assume all roles should be removed. Then query the Policy Service and remove
|
||||
// roles from this set that actually exist.
|
||||
Set<String> rolesToRemove = catalog.getAuthPolicy().getAllRoleNames();
|
||||
// The keys (role names) in listAllRolesPrivileges here are always in lower case.
|
||||
Map<String, Set<TSentryPrivilege>> allRolesPrivileges =
|
||||
sentryPolicyService.listAllRolesPrivileges(processUser);
|
||||
// Read the full policy, adding new/modified roles to "updatedRoles".
|
||||
for (TSentryRole sentryRole:
|
||||
sentryPolicyService.listAllRoles(processUser)) {
|
||||
// This role exists and should not be removed, delete it from the
|
||||
// rolesToRemove set.
|
||||
rolesToRemove.remove(sentryRole.getRoleName().toLowerCase());
|
||||
|
||||
Set<String> grantGroups = Sets.newHashSet();
|
||||
for (TSentryGroup group: sentryRole.getGroups()) {
|
||||
grantGroups.add(group.getGroupName());
|
||||
}
|
||||
Role existingRole =
|
||||
catalog.getAuthPolicy().getRole(sentryRole.getRoleName());
|
||||
Role role;
|
||||
// These roles are the same, use the current role.
|
||||
if (existingRole != null &&
|
||||
existingRole.getGrantGroups().equals(grantGroups)) {
|
||||
role = existingRole;
|
||||
if (resetVersions) {
|
||||
role.setCatalogVersion(catalog.incrementAndGetCatalogVersion());
|
||||
}
|
||||
} else {
|
||||
LOG.debug("Adding role: " + sentryRole.getRoleName());
|
||||
role = catalog.addRole(sentryRole.getRoleName(), grantGroups);
|
||||
authzDelta.getAddedCatalogObjects().add(role.toTCatalogObject());
|
||||
}
|
||||
// allRolesPrivileges keys and sentryRole.getName() are used here since they both
|
||||
// come from Sentry so they agree in case.
|
||||
refreshPrivilegesInCatalog(catalog, resetVersions, sentryRole.getRoleName(), role,
|
||||
allRolesPrivileges, authzDelta);
|
||||
}
|
||||
// Remove all the roles, incrementing the catalog version to indicate
|
||||
// a change.
|
||||
for (String roleName: rolesToRemove) {
|
||||
LOG.debug("Removing role: " + roleName);
|
||||
authzDelta.getRemovedCatalogObjects().add(
|
||||
catalog.removeRole(roleName).toTCatalogObject());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates all users and their associated privileges in the catalog by adding,
|
||||
* removing, and replacing the catalog objects to match those in Sentry since the
|
||||
* last Sentry sync update. Take note that we only store the users with privileges
|
||||
* stored in Sentry and not all available users in the system. User privileges do not
|
||||
* support grant groups.
|
||||
*/
|
||||
private static void refreshUserPrivileges(CatalogServiceCatalog catalog,
|
||||
SentryPolicyService sentryPolicyService, User processUser, boolean resetVersions,
|
||||
AuthorizationDelta authzDelta) throws ImpalaException {
|
||||
// Assume all users should be removed. Then query the Policy Service and remove
|
||||
// users from this set that actually exist.
|
||||
Set<String> usersToRemove = catalog.getAuthPolicy().getAllUserNames();
|
||||
// The keys (user names) in listAllUsersPrivileges here are always in lower case.
|
||||
Map<String, Set<TSentryPrivilege>> allUsersPrivileges =
|
||||
sentryPolicyService.listAllUsersPrivileges(processUser);
|
||||
for (Map.Entry<String, Set<TSentryPrivilege>> userPrivilegesEntry:
|
||||
allUsersPrivileges.entrySet()) {
|
||||
String userName = userPrivilegesEntry.getKey();
|
||||
// This user exists and should not be removed so remove it from the
|
||||
// usersToRemove set.
|
||||
usersToRemove.remove(userName);
|
||||
|
||||
Reference<Boolean> existingUser = new Reference<>();
|
||||
org.apache.impala.catalog.User user = catalog.addUserIfNotExists(userName,
|
||||
existingUser);
|
||||
if (existingUser.getRef() && resetVersions) {
|
||||
user.setCatalogVersion(catalog.incrementAndGetCatalogVersion());
|
||||
} else if (!existingUser.getRef()) {
|
||||
LOG.debug("Adding user: " + user.getName());
|
||||
authzDelta.getAddedCatalogObjects().add(user.toTCatalogObject());
|
||||
}
|
||||
// allUsersPrivileges keys and userPrivilegesEntry.getKey() are used here since
|
||||
// they both come from Sentry so they agree in case.
|
||||
refreshPrivilegesInCatalog(catalog, resetVersions, userPrivilegesEntry.getKey(),
|
||||
user, allUsersPrivileges, authzDelta);
|
||||
}
|
||||
// Remove all the users, incrementing the catalog version to indicate
|
||||
// a change.
|
||||
for (String userName: usersToRemove) {
|
||||
LOG.debug("Removing user: " + userName);
|
||||
authzDelta.getRemovedCatalogObjects().add(
|
||||
catalog.removeUser(userName).toTCatalogObject());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the privileges for a given principal in the catalog since the last Sentry
|
||||
* sync update. The sentryPrincipalName is used to match against the key in
|
||||
* allPrincipalPrivileges, which both come from Sentry, so they should have the
|
||||
* same case sensitivity.
|
||||
*/
|
||||
private static void refreshPrivilegesInCatalog(CatalogServiceCatalog catalog,
|
||||
boolean resetVersions, String sentryPrincipalName, Principal principal,
|
||||
Map<String, Set<TSentryPrivilege>> allPrincipalPrivileges,
|
||||
AuthorizationDelta authzDelta) throws CatalogException {
|
||||
// Assume all privileges should be removed. Privileges that still exist are
|
||||
// deleted from this set and we are left with the set of privileges that need
|
||||
// to be removed.
|
||||
Set<String> privilegesToRemove = principal.getPrivilegeNames();
|
||||
// It is important to get a set of privileges using sentryPrincipalName
|
||||
// and not principal.getName() because principal.getName() may return a
|
||||
// principal name with a different case than the principal names stored
|
||||
// in allPrincipalPrivileges. See IMPALA-7729 for more information.
|
||||
Set<TSentryPrivilege> sentryPrivileges = allPrincipalPrivileges.get(
|
||||
sentryPrincipalName);
|
||||
if (sentryPrivileges == null) {
|
||||
sentryPrivileges = Sets.newHashSet();
|
||||
}
|
||||
// Check all the privileges that are part of this principal.
|
||||
for (TSentryPrivilege sentryPriv: sentryPrivileges) {
|
||||
TPrivilege thriftPriv =
|
||||
SentryPolicyService.sentryPrivilegeToTPrivilege(sentryPriv, principal);
|
||||
String privilegeName = PrincipalPrivilege.buildPrivilegeName(thriftPriv);
|
||||
privilegesToRemove.remove(privilegeName);
|
||||
PrincipalPrivilege existingPrincipalPriv = principal.getPrivilege(privilegeName);
|
||||
// We already know about this privilege (privileges cannot be modified).
|
||||
if (existingPrincipalPriv != null &&
|
||||
existingPrincipalPriv.getCreateTimeMs() == sentryPriv.getCreateTime()) {
|
||||
if (resetVersions) {
|
||||
existingPrincipalPriv.setCatalogVersion(
|
||||
catalog.incrementAndGetCatalogVersion());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (principal.getPrincipalType() == TPrincipalType.ROLE) {
|
||||
LOG.debug("Adding role privilege: " + privilegeName);
|
||||
authzDelta.getAddedCatalogObjects().add(
|
||||
catalog.addRolePrivilege(principal.getName(), thriftPriv).toTCatalogObject());
|
||||
} else {
|
||||
LOG.debug("Adding user privilege: " + privilegeName);
|
||||
authzDelta.getAddedCatalogObjects().add(
|
||||
catalog.addUserPrivilege(principal.getName(), thriftPriv).toTCatalogObject());
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the privileges that no longer exist.
|
||||
for (String privilegeName: privilegesToRemove) {
|
||||
if (principal.getPrincipalType() == TPrincipalType.ROLE) {
|
||||
LOG.debug("Removing role privilege: " + privilegeName);
|
||||
authzDelta.getRemovedCatalogObjects().add(
|
||||
catalog.removeRolePrivilege(principal.getName(), privilegeName)
|
||||
.toTCatalogObject());
|
||||
} else {
|
||||
LOG.debug("Removing user privilege: " + privilegeName);
|
||||
authzDelta.getRemovedCatalogObjects().add(
|
||||
catalog.removeUserPrivilege(principal.getName(), privilegeName)
|
||||
.toTCatalogObject());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given user is an admin on the Sentry Service.
|
||||
*/
|
||||
public boolean isSentryAdmin(User user)
|
||||
throws InternalException {
|
||||
try {
|
||||
return sentryPolicyService_.isSentryAdmin(user);
|
||||
} catch (SentryUserException e) {
|
||||
// When a user is not defined in Sentry, isAdmin() will throw
|
||||
// SentryUserException, we will consider this requesting user
|
||||
// as a non-Sentry administrator.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new role using the Sentry Service and updates the Impala catalog.
|
||||
* If the RPC to the Sentry Service fails the Impala catalog will not
|
||||
* be modified. Returns the new Role.
|
||||
* Throws exception if there was any error updating the Sentry Service or
|
||||
* if a role with the same name already exists in the catalog. This includes
|
||||
* the case where a role was added externally (eg. via Hive). If the role was added
|
||||
* externally, Impala will load it during the next refresh of the policy.
|
||||
* TODO: Consider adding the role to the policy if we find it was created
|
||||
* externally.
|
||||
*/
|
||||
public synchronized Role createRole(User user, String roleName)
|
||||
throws ImpalaException {
|
||||
Role role = null;
|
||||
if (catalog_.getAuthPolicy().getRole(roleName) != null) {
|
||||
throw new CatalogException("Role already exists: " + roleName);
|
||||
}
|
||||
sentryPolicyService_.createRole(user, roleName, false);
|
||||
// Initially the role has no grant groups (empty set).
|
||||
role = catalog_.addRole(roleName, Sets.<String>newHashSet());
|
||||
return role;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops the given role using the Sentry Service and updates the Impala catalog.
|
||||
* If the RPC to the Sentry Service fails the Impala catalog will not
|
||||
* be modified. Returns the removed Role or null if the role did not exist in the
|
||||
* Catalog.
|
||||
* Throws exception if there was any error updating the Sentry Service.
|
||||
*/
|
||||
public synchronized Role dropRole(User user, String roleName) throws ImpalaException {
|
||||
sentryPolicyService_.dropRole(user, roleName, false);
|
||||
return catalog_.removeRole(roleName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the role grant group using the Sentry Service and updates the Impala
|
||||
* catalog. If the RPC to the Sentry Service fails the Impala catalog will not
|
||||
* be modified. Returns the updated Role.
|
||||
* Throws exception if there was any error updating the Sentry Service or if the Impala
|
||||
* catalog does not contain the given role name.
|
||||
*/
|
||||
public synchronized Role grantRoleGroup(User user, String roleName, String groupName)
|
||||
throws ImpalaException {
|
||||
sentryPolicyService_.grantRoleToGroup(user, roleName, groupName);
|
||||
return catalog_.addRoleGrantGroup(roleName, groupName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the role grant group using the Sentry Service and updates the Impala
|
||||
* catalog. If the RPC to the Sentry Service fails the Impala catalog will not
|
||||
* be modified. Returns the updated Role.
|
||||
* Throws exception if there was any error updating the Sentry Service or if the Impala
|
||||
* catalog does not contain the given role name.
|
||||
*/
|
||||
public synchronized Role revokeRoleGroup(User user, String roleName, String groupName)
|
||||
throws ImpalaException {
|
||||
sentryPolicyService_.revokeRoleFromGroup(user, roleName, groupName);
|
||||
return catalog_.removeRoleGrantGroup(roleName, groupName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Grants privileges to a role in the Sentry Service and updates the Impala
|
||||
* catalog. If the RPC to the Sentry Service fails, the Impala catalog will not
|
||||
* be modified. Returns the granted privileges.
|
||||
* Throws exception if there was any error updating the Sentry Service or if the Impala
|
||||
* catalog does not contain the given role name.
|
||||
* This code is odd because we need to avoid duplicate privileges in Sentry because
|
||||
* the same privilege with and without grant option are two different privileges.
|
||||
* https://issues.apache.org/jira/browse/SENTRY-2408
|
||||
* If the current privilege and the requested privilege have the same grant option
|
||||
* state, then just execute the grant. This is necessary so that if the user does not
|
||||
* have the ability to grant, Sentry will throw an exception. We don't want to
|
||||
* expose data by skipping the grant if it already exists.
|
||||
* For the case when the existing privilege does not have the grant option, but the
|
||||
* request does, we need to first add the new privilege, then revoke the old one.
|
||||
* If this is done in the wrong order, and an exception is thrown, the user will get
|
||||
* a "REVOKE_PRIVILEGE" error on a grant.
|
||||
* For the case when the existing privilege does have the grant option, but the
|
||||
* request does not, we add the grant option to the request because the privilege
|
||||
* should not be "downgraded", we don't want to have duplicates, and we still need
|
||||
* Sentry to perform the "has grant ability" check. Downgraded indicates changing a
|
||||
* privilege from one that has a grant option to one that does not.
|
||||
*/
|
||||
public synchronized List<PrincipalPrivilege> grantRolePrivileges(User user,
|
||||
String roleName, List<TPrivilege> privileges, boolean hasGrantOption,
|
||||
List<PrincipalPrivilege> removedPrivileges) throws ImpalaException {
|
||||
// First find out what's in the catalog. All privileges will have the same grant
|
||||
// option set. The only case there will be more than one privilege is in the case
|
||||
// of multiple column privileges.
|
||||
Preconditions.checkArgument(!privileges.isEmpty());
|
||||
TPrivilege tWithGrant = PrincipalPrivilege.copyPrivilegeWithGrant(privileges.get(0),
|
||||
true);
|
||||
PrincipalPrivilege catWithGrant = catalog_.getPrincipalPrivilege(roleName,
|
||||
tWithGrant);
|
||||
TPrivilege tNoGrant = PrincipalPrivilege.copyPrivilegeWithGrant(privileges.get(0),
|
||||
false);
|
||||
PrincipalPrivilege catNoGrant = catalog_.getPrincipalPrivilege(roleName, tNoGrant);
|
||||
|
||||
// List of privileges that should be removed. If removed, they will be added to
|
||||
// the removedPrivileges list.
|
||||
List<TPrivilege> toRemove = null;
|
||||
|
||||
if (catNoGrant != null && hasGrantOption) {
|
||||
toRemove = privileges.stream().map(p ->
|
||||
PrincipalPrivilege.copyPrivilegeWithGrant(p, false))
|
||||
.collect(Collectors.toList());
|
||||
} else if (catWithGrant != null && !hasGrantOption) {
|
||||
// Elevate the requested privileges.
|
||||
privileges = privileges.stream().map(p -> p.setHas_grant_opt(true))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
// This is a list of privileges that were added, to be returned.
|
||||
List<PrincipalPrivilege> rolePrivileges = Lists.newArrayList();
|
||||
// Do the grants first
|
||||
sentryPolicyService_.grantRolePrivileges(user, roleName, privileges);
|
||||
// Update the catalog
|
||||
for (TPrivilege privilege: privileges) {
|
||||
rolePrivileges.add(catalog_.addRolePrivilege(roleName, privilege));
|
||||
}
|
||||
|
||||
// Then the revokes
|
||||
if (toRemove != null && !toRemove.isEmpty()) {
|
||||
sentryPolicyService_.revokeRolePrivileges(user, roleName, toRemove);
|
||||
for (TPrivilege privilege : toRemove) {
|
||||
PrincipalPrivilege rolePriv = catalog_.removeRolePrivilege(roleName,
|
||||
PrincipalPrivilege.buildPrivilegeName(privilege));
|
||||
if (rolePriv == null) continue;
|
||||
removedPrivileges.add(rolePriv);
|
||||
}
|
||||
// If we removed anything, it might have removed the grants, so redo the grants.
|
||||
// TODO: https://issues.apache.org/jira/browse/SENTRY-2408
|
||||
// When Sentry adds API to modify privileges, this code can be refactored.
|
||||
sentryPolicyService_.grantRolePrivileges(user, roleName, privileges);
|
||||
}
|
||||
return rolePrivileges;
|
||||
}
|
||||
|
||||
/**
|
||||
* Revokes privileges from a role in the Sentry Service and updates the Impala
|
||||
* catalog. If the RPC to the Sentry Service fails the Impala catalog will not be
|
||||
* modified. Returns the removed privileges. Throws an exception if there was any error
|
||||
* updating the Sentry Service or if the Impala catalog does not contain the given role
|
||||
* name. The addedPrivileges parameter will be populated with any new privileges that
|
||||
* are granted, in the case where the revoke involves a grant option. In this case
|
||||
* privileges that contain the grant option are removed and the new privileges without
|
||||
* the grant option are added.
|
||||
*
|
||||
* The revoke code is confusing because of the various usages of "grant option". The
|
||||
* parameter "hasGrantOption" indicates that the revoke should just remove the grant
|
||||
* option from an existing privilege. The grant option on the privilege indicates
|
||||
* whether the existing privilege has the grant option set which for the case of
|
||||
* revokes, there's currently no SQL statement that will result in the grant option
|
||||
* being set on the request.
|
||||
*/
|
||||
public synchronized List<PrincipalPrivilege> revokeRolePrivileges(User user,
|
||||
String roleName, List<TPrivilege> privileges, boolean hasGrantOption,
|
||||
List<PrincipalPrivilege> addedPrivileges) throws ImpalaException {
|
||||
List<PrincipalPrivilege> rolePrivileges = Lists.newArrayList();
|
||||
if (!hasGrantOption) {
|
||||
sentryPolicyService_.revokeRolePrivileges(user, roleName, privileges);
|
||||
// Update the catalog. The catalog should only have one privilege whether it has
|
||||
// the grant option or not. We need to remove it which ever one is set. Since the
|
||||
// catalog object name for privileges contains the grantoption value, we need to
|
||||
// check both.
|
||||
for (TPrivilege privilege: privileges) {
|
||||
TPrivilege privNotGrant = privilege.deepCopy();
|
||||
privNotGrant.setHas_grant_opt(!privilege.has_grant_opt);
|
||||
PrincipalPrivilege rolePrivNotGrant = catalog_.removeRolePrivilege(roleName,
|
||||
PrincipalPrivilege.buildPrivilegeName(privNotGrant));
|
||||
PrincipalPrivilege rolePriv = catalog_.removeRolePrivilege(roleName,
|
||||
PrincipalPrivilege.buildPrivilegeName(privilege));
|
||||
if (rolePrivNotGrant != null) {
|
||||
rolePrivileges.add(rolePrivNotGrant);
|
||||
}
|
||||
if (rolePriv != null) {
|
||||
rolePrivileges.add(rolePriv);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If the REVOKE GRANT OPTION has been specified, the privileges with grant must be
|
||||
// revoked and these same privileges are added back to the catalog with the grant
|
||||
// option set to false. They can not simply be updated because "grantoption" is part
|
||||
// of the name of the catalog object. Sentry does not yet provide an
|
||||
// "alter privilege" API so we need to revoke the privileges and re-grant them.
|
||||
sentryPolicyService_.revokeRolePrivileges(user, roleName, privileges);
|
||||
List<TPrivilege> newPrivs = Lists.newArrayList();
|
||||
for (TPrivilege privilege: privileges) {
|
||||
PrincipalPrivilege existingPriv = catalog_.getPrincipalPrivilege(roleName,
|
||||
privilege);
|
||||
if (existingPriv == null) continue;
|
||||
rolePrivileges.add(catalog_.removeRolePrivilege(roleName,
|
||||
PrincipalPrivilege.buildPrivilegeName(privilege)));
|
||||
|
||||
TPrivilege addedPriv = new TPrivilege(existingPriv.toThrift());
|
||||
addedPriv.setHas_grant_opt(false);
|
||||
newPrivs.add(addedPriv);
|
||||
}
|
||||
// Re-grant the updated privileges.
|
||||
if (!newPrivs.isEmpty()) {
|
||||
sentryPolicyService_.grantRolePrivileges(user, roleName, newPrivs);
|
||||
}
|
||||
// Update the catalog
|
||||
for (TPrivilege newPriv: newPrivs) {
|
||||
addedPrivileges.add(catalog_.addRolePrivilege(roleName, newPriv));
|
||||
}
|
||||
}
|
||||
return rolePrivileges;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a synchronous refresh of all authorization policy metadata and updates
|
||||
* the Catalog with any changes. Throws an ImpalaRuntimeException if there are any
|
||||
* errors executing the refresh job.
|
||||
*/
|
||||
public void refresh(boolean resetVersions, List<TCatalogObject> added,
|
||||
List<TCatalogObject> removed) throws ImpalaRuntimeException {
|
||||
try {
|
||||
// Since this is a synchronous refresh, any exception thrown while running the
|
||||
// refresh should be thrown here instead of silently swallowing it.
|
||||
policyReader_.submit(new PolicyReader(resetVersions, /*swallow exception*/ false,
|
||||
new AuthorizationDelta(added, removed))).get();
|
||||
} catch (Exception e) {
|
||||
if (e.getCause() != null && e.getCause() instanceof SentryUnavailableException) {
|
||||
throw new ImpalaRuntimeException("Error refreshing authorization policy. " +
|
||||
"Sentry is unavailable. Ensure Sentry is up: ", e);
|
||||
}
|
||||
throw new ImpalaRuntimeException("Error refreshing authorization policy, " +
|
||||
"current policy state may be inconsistent. Running 'invalidate metadata' " +
|
||||
"may resolve this problem: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if object ownership is enabled in Sentry.
|
||||
*/
|
||||
public boolean isObjectOwnershipEnabled() {
|
||||
if (objectOwnershipConfigValue_.length() == 0 ||
|
||||
objectOwnershipConfigValue_.equalsIgnoreCase(
|
||||
SentryOwnerPrivilegeType.NONE.toString())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if 'with grant' is enabled for object ownership in Sentry.
|
||||
*/
|
||||
public boolean isObjectOwnershipGrantEnabled() {
|
||||
if (objectOwnershipConfigValue_.equalsIgnoreCase(
|
||||
SentryOwnerPrivilegeType.ALL_WITH_GRANT.toString())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
// 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.authorization.sentry;
|
||||
|
||||
/**
|
||||
* Thrown when Sentry is not available.
|
||||
*/
|
||||
public class SentryUnavailableException extends RuntimeException {
|
||||
public SentryUnavailableException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
public SentryUnavailableException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public SentryUnavailableException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
// 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.authorization.sentry;
|
||||
|
||||
import org.apache.sentry.core.common.exception.SentryAccessDeniedException;
|
||||
import org.apache.sentry.core.common.exception.SentryAlreadyExistsException;
|
||||
import org.apache.sentry.core.common.exception.SentryGroupNotFoundException;
|
||||
import org.apache.sentry.core.common.exception.SentryUserException;
|
||||
import org.apache.thrift.transport.TTransportException;
|
||||
|
||||
/**
|
||||
* A helper class for Sentry.
|
||||
*/
|
||||
public class SentryUtil {
|
||||
private SentryUtil() {}
|
||||
|
||||
public static boolean isSentryAlreadyExists(Exception e) {
|
||||
return e instanceof SentryAlreadyExistsException;
|
||||
}
|
||||
|
||||
public static boolean isSentryAccessDenied(Exception e) {
|
||||
return e instanceof SentryAccessDeniedException;
|
||||
}
|
||||
|
||||
public static boolean isSentryGroupNotFound(Exception e) {
|
||||
return e instanceof SentryGroupNotFoundException;
|
||||
}
|
||||
|
||||
public static boolean isSentryUnavailable(Exception e) {
|
||||
return e instanceof SentryUserException &&
|
||||
e.getCause() instanceof TTransportException;
|
||||
}
|
||||
}
|
||||
@@ -70,9 +70,6 @@ public class BackendConfig {
|
||||
public String getImpalaBuildVersion() { return backendCfg_.impala_build_version; }
|
||||
public int getImpalaLogLevel() { return backendCfg_.impala_log_lvl; }
|
||||
public int getNonImpalaJavaVlogLevel() { return backendCfg_.non_impala_java_vlog; }
|
||||
public long getSentryCatalogPollingFrequency() {
|
||||
return backendCfg_.sentry_catalog_polling_frequency_s;
|
||||
}
|
||||
|
||||
public int maxHdfsPartsParallelLoad() {
|
||||
return backendCfg_.max_hdfs_partitions_parallel_load;
|
||||
|
||||
@@ -115,9 +115,6 @@ public class FeSupport {
|
||||
|
||||
public native static byte[] NativeUpdateTableUsage(byte[] thriftReq);
|
||||
|
||||
// Does an RPC to the Catalog Server to check if the given user is a Sentry admin.
|
||||
public native static byte[] NativeSentryAdminCheck(byte[] thriftReq);
|
||||
|
||||
// Parses a string of comma-separated key=value query options ('csvQueryOptions'),
|
||||
// updates the existing query options ('queryOptions') with them and returns the
|
||||
// resulting serialized TQueryOptions object.
|
||||
@@ -444,15 +441,6 @@ public class FeSupport {
|
||||
return NativeGetPartialCatalogObject(thriftReq);
|
||||
}
|
||||
|
||||
public static byte[] CheckSentryAdmin(byte[] thriftReq) {
|
||||
try {
|
||||
return NativeSentryAdminCheck(thriftReq);
|
||||
} catch (UnsatisfiedLinkError e) {
|
||||
loadLibrary();
|
||||
}
|
||||
return NativeSentryAdminCheck(thriftReq);
|
||||
}
|
||||
|
||||
private static byte[] parseDateStringUtil(String date) {
|
||||
try {
|
||||
return nativeParseDateString(date);
|
||||
|
||||
@@ -1099,8 +1099,9 @@ public class Frontend {
|
||||
* level objects in the hierarchy.
|
||||
*/
|
||||
private boolean authEngineSupportsDenyRules() {
|
||||
// Sentry did not support deny rules, but Ranger does. So, this now returns true.
|
||||
// TODO: could check config for Ranger and return true if deny rules are disabled
|
||||
return !authzFactory_.getAuthorizationConfig().getProviderName().equals("sentry");
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,7 +29,6 @@ import org.apache.impala.authorization.AuthorizationConfig;
|
||||
import org.apache.impala.authorization.AuthorizationFactory;
|
||||
import org.apache.impala.authorization.AuthorizationManager;
|
||||
import org.apache.impala.authorization.User;
|
||||
import org.apache.impala.authorization.sentry.SentryCatalogdAuthorizationManager;
|
||||
import org.apache.impala.catalog.CatalogException;
|
||||
import org.apache.impala.catalog.CatalogServiceCatalog;
|
||||
import org.apache.impala.catalog.Db;
|
||||
@@ -62,8 +61,6 @@ import org.apache.impala.thrift.TGetTablesResult;
|
||||
import org.apache.impala.thrift.TLogLevel;
|
||||
import org.apache.impala.thrift.TPrioritizeLoadRequest;
|
||||
import org.apache.impala.thrift.TResetMetadataRequest;
|
||||
import org.apache.impala.thrift.TSentryAdminCheckRequest;
|
||||
import org.apache.impala.thrift.TSentryAdminCheckResponse;
|
||||
import org.apache.impala.thrift.TStatus;
|
||||
import org.apache.impala.thrift.TUniqueId;
|
||||
import org.apache.impala.thrift.TUpdateCatalogRequest;
|
||||
@@ -303,24 +300,6 @@ public class JniCatalog {
|
||||
return serializer.serialize(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies whether the user is configured as a Sentry admin.
|
||||
*/
|
||||
public byte[] checkUserSentryAdmin(byte[] thriftReq)
|
||||
throws ImpalaException, TException {
|
||||
TSentryAdminCheckRequest request = new TSentryAdminCheckRequest();
|
||||
JniUtil.deserializeThrift(protocolFactory_, request, thriftReq);
|
||||
TSerializer serializer = new TSerializer(protocolFactory_);
|
||||
User user = new User(request.getHeader().getRequesting_user());
|
||||
Preconditions.checkState(catalogOpExecutor_.getAuthzManager() instanceof
|
||||
SentryCatalogdAuthorizationManager);
|
||||
TSentryAdminCheckResponse response = new TSentryAdminCheckResponse();
|
||||
boolean isSentryAdmin = ((SentryCatalogdAuthorizationManager)
|
||||
catalogOpExecutor_.getAuthzManager()).isSentryAdmin(user);
|
||||
response.setIs_admin(isSentryAdmin);
|
||||
return serializer.serialize(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process any updates to the metastore required after a query executes.
|
||||
* The argument is a serialized TCatalogUpdate.
|
||||
|
||||
@@ -49,7 +49,10 @@ public class AuthorizationUtil {
|
||||
authzFactoryClassName = authzFactoryClassOption;
|
||||
} else {
|
||||
// use authorization_provider flag
|
||||
final String authzProvider = beCfg.getAuthorizationProvider();
|
||||
String authzProvider = beCfg.getAuthorizationProvider();
|
||||
// If authorization_provider is empty, use the Noop policy that disables
|
||||
// authorization.
|
||||
if (authzProvider.equals("")) authzProvider = "noop";
|
||||
try {
|
||||
final AuthorizationProvider provider = AuthorizationProvider.valueOf(
|
||||
authzProvider.toUpperCase().trim());
|
||||
|
||||
@@ -20,8 +20,8 @@ package org.apache.impala.analysis;
|
||||
import java.util.HashSet;
|
||||
|
||||
import org.apache.impala.authorization.NoopAuthorizationFactory;
|
||||
import org.apache.impala.authorization.sentry.SentryAuthorizationConfig;
|
||||
import org.apache.impala.authorization.sentry.SentryAuthorizationFactory;
|
||||
import org.apache.impala.authorization.ranger.RangerAuthorizationConfig;
|
||||
import org.apache.impala.authorization.ranger.RangerAuthorizationFactory;
|
||||
import org.apache.impala.catalog.Catalog;
|
||||
import org.apache.impala.catalog.Role;
|
||||
import org.apache.impala.catalog.User;
|
||||
@@ -33,6 +33,9 @@ import org.apache.impala.util.EventSequence;
|
||||
import org.junit.Test;
|
||||
|
||||
public class AnalyzeAuthStmtsTest extends FrontendTestBase {
|
||||
protected static final String SERVER_NAME = "server1";
|
||||
protected static final String RANGER_SERVICE_TYPE = "hive";
|
||||
protected static final String RANGER_APP_ID = "impala";
|
||||
|
||||
// TODO: Change this to a @BeforeClass method. Then, clean up these
|
||||
// items in @AfterClass, else we've made a global change that may affect
|
||||
@@ -69,8 +72,9 @@ public class AnalyzeAuthStmtsTest extends FrontendTestBase {
|
||||
defaultDb, System.getProperty("user.name"));
|
||||
EventSequence timeline = new EventSequence("Authorization Test");
|
||||
AnalysisContext analysisCtx = new AnalysisContext(queryCtx,
|
||||
new SentryAuthorizationFactory(
|
||||
SentryAuthorizationConfig.createHadoopGroupAuthConfig("server1", null)),
|
||||
new RangerAuthorizationFactory(
|
||||
new RangerAuthorizationConfig(RANGER_SERVICE_TYPE, RANGER_APP_ID,
|
||||
SERVER_NAME, null, null, null)),
|
||||
timeline);
|
||||
return analysisCtx;
|
||||
}
|
||||
@@ -316,8 +320,9 @@ public class AnalyzeAuthStmtsTest extends FrontendTestBase {
|
||||
Catalog.DEFAULT_DB, "");
|
||||
EventSequence timeline = new EventSequence("Authorization Test");
|
||||
AnalysisContext noUserNameCtx = new AnalysisContext(noUserNameQueryCtx,
|
||||
new SentryAuthorizationFactory(
|
||||
SentryAuthorizationConfig.createHadoopGroupAuthConfig("server1", null)),
|
||||
new RangerAuthorizationFactory(
|
||||
new RangerAuthorizationConfig(RANGER_SERVICE_TYPE, RANGER_APP_ID,
|
||||
SERVER_NAME, null, null, null)),
|
||||
timeline);
|
||||
AnalysisError("GRANT ALL ON SERVER TO myRole", noUserNameCtx,
|
||||
"Cannot execute authorization statement with an empty username.");
|
||||
|
||||
@@ -33,7 +33,6 @@ import org.apache.impala.thrift.TDescribeOutputStyle;
|
||||
import org.apache.impala.thrift.TPrivilegeLevel;
|
||||
import org.apache.impala.thrift.TQueryOptions;
|
||||
import org.apache.impala.thrift.TTableName;
|
||||
import org.apache.sentry.api.service.thrift.TSentryRole;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
@@ -77,28 +76,9 @@ public class AuthorizationStmtTest extends AuthorizationTestBase {
|
||||
RuntimeEnv.INSTANCE.reset();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void before() throws ImpalaException {
|
||||
if (authzProvider_ == AuthorizationProvider.SENTRY) {
|
||||
// Remove existing roles in order to not interfere with these tests. To be able to
|
||||
// list existing roles, we have to invoke listAllRoles() as the user corresponding
|
||||
// to User(System.getProperty("user.name")), which has the privilege to execute
|
||||
// LIST_ROLES.
|
||||
User user = new User(System.getProperty("user.name"));
|
||||
for (TSentryRole role : sentryService_.listAllRoles(user)) {
|
||||
authzCatalog_.removeRole(role.getRoleName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Parameters
|
||||
public static Collection<AuthorizationProvider> data() {
|
||||
String envDisableSentry = System.getenv("DISABLE_SENTRY");
|
||||
if (envDisableSentry != null && envDisableSentry.equals("true")) {
|
||||
return Arrays.asList(AuthorizationProvider.RANGER);
|
||||
} else {
|
||||
return Arrays.asList(AuthorizationProvider.SENTRY, AuthorizationProvider.RANGER);
|
||||
}
|
||||
return Arrays.asList(AuthorizationProvider.RANGER);
|
||||
}
|
||||
|
||||
private static final String[] ALLTYPES_COLUMNS_WITHOUT_ID = new String[]{"bool_col",
|
||||
@@ -2147,21 +2127,9 @@ public class AuthorizationStmtTest extends AuthorizationTestBase {
|
||||
.error(accessError(true, "functional.alltypes"),
|
||||
onTable(true, "functional", "alltypes", allExcept(
|
||||
TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER)));
|
||||
// TODO: Checking if a request is allowed by checking if grant option flag is set
|
||||
// is to Sentry.
|
||||
// The following do not result in Ranger authorization errors.
|
||||
if (authzProvider_ == AuthorizationProvider.SENTRY) {
|
||||
test.error(accessError(true, "functional.alltypes"), onServer(
|
||||
TPrivilegeLevel.values()))
|
||||
.error(accessError(true, "functional.alltypes"), onDatabase("functional",
|
||||
TPrivilegeLevel.values()))
|
||||
.error(accessError(true, "functional.alltypes"), onTable("functional",
|
||||
"alltypes", TPrivilegeLevel.values()));
|
||||
} else {
|
||||
test.ok(onServer(TPrivilegeLevel.values()));
|
||||
test.ok(onDatabase("functional", TPrivilegeLevel.values()));
|
||||
test.ok(onTable("functional", "alltypes", TPrivilegeLevel.values()));
|
||||
}
|
||||
test.ok(onServer(TPrivilegeLevel.values()));
|
||||
test.ok(onDatabase("functional", TPrivilegeLevel.values()));
|
||||
test.ok(onTable("functional", "alltypes", TPrivilegeLevel.values()));
|
||||
}
|
||||
} finally {
|
||||
authzCatalog_.removeRole("foo_owner");
|
||||
@@ -2400,21 +2368,9 @@ public class AuthorizationStmtTest extends AuthorizationTestBase {
|
||||
.error(accessError(true, "functional.alltypes_view"), onTable("functional",
|
||||
"alltypes_view", allExcept(TPrivilegeLevel.ALL,
|
||||
TPrivilegeLevel.OWNER)));
|
||||
// TODO: Checking if a request is allowed by checking if grant option flag is set
|
||||
// is to Sentry.
|
||||
// The following do not result in Ranger authorization errors.
|
||||
if (authzProvider_ == AuthorizationProvider.SENTRY) {
|
||||
test.error(accessError(true, "functional.alltypes_view"), onServer(
|
||||
TPrivilegeLevel.values()))
|
||||
.error(accessError(true, "functional.alltypes_view"), onDatabase(
|
||||
"functional", TPrivilegeLevel.values()))
|
||||
.error(accessError(true, "functional.alltypes_view"), onTable("functional",
|
||||
"alltypes_view", TPrivilegeLevel.values()));
|
||||
} else {
|
||||
test.ok(onServer(TPrivilegeLevel.values()));
|
||||
test.ok(onDatabase("functional", TPrivilegeLevel.values()));
|
||||
test.ok(onTable("functional", "alltypes_view", TPrivilegeLevel.values()));
|
||||
}
|
||||
test.ok(onServer(TPrivilegeLevel.values()));
|
||||
test.ok(onDatabase("functional", TPrivilegeLevel.values()));
|
||||
test.ok(onTable("functional", "alltypes_view", TPrivilegeLevel.values()));
|
||||
}
|
||||
} finally {
|
||||
authzCatalog_.removeRole("foo_owner");
|
||||
@@ -2454,21 +2410,10 @@ public class AuthorizationStmtTest extends AuthorizationTestBase {
|
||||
TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER)))
|
||||
.error(accessError(true, "functional"), onDatabase(true, "functional",
|
||||
allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER)));
|
||||
// TODO: Checking if a request is allowed by checking if grant option flag is set
|
||||
// is to Sentry.
|
||||
// The following do not result in Ranger authorization errors.
|
||||
if (authzProvider_ == AuthorizationProvider.SENTRY) {
|
||||
authorize(String.format("alter database functional set owner %s foo",
|
||||
ownerType))
|
||||
.error(accessError(true, "functional"), onServer(TPrivilegeLevel.values()))
|
||||
.error(accessError(true, "functional"), onDatabase("functional",
|
||||
TPrivilegeLevel.values()));
|
||||
} else {
|
||||
authorize(String.format("alter database functional set owner %s foo",
|
||||
ownerType))
|
||||
.ok(onServer(TPrivilegeLevel.values()))
|
||||
.ok(onDatabase("functional", TPrivilegeLevel.values()));
|
||||
}
|
||||
authorize(String.format("alter database functional set owner %s foo",
|
||||
ownerType))
|
||||
.ok(onServer(TPrivilegeLevel.values()))
|
||||
.ok(onDatabase("functional", TPrivilegeLevel.values()));
|
||||
|
||||
// Database does not exist.
|
||||
authorize(String.format("alter database nodb set owner %s foo", ownerType))
|
||||
@@ -2847,7 +2792,7 @@ public class AuthorizationStmtTest extends AuthorizationTestBase {
|
||||
// analysis context, we have to explicitly specify the requesting user
|
||||
// corresponding to 'user_' defined in AuthorizationTestBase.java. Otherwise,
|
||||
// an analysis context will be created with a user corresponding to
|
||||
// User(System.getProperty("user.name")), resulting in a Sentry authorization
|
||||
// User(System.getProperty("user.name")), resulting in a Ranger authorization
|
||||
// error.
|
||||
authorize(createAnalysisCtx(options, authzFactory_, user_.getName()),
|
||||
"select functional.to_lower('ABCDEF')")
|
||||
@@ -2883,8 +2828,6 @@ public class AuthorizationStmtTest extends AuthorizationTestBase {
|
||||
*/
|
||||
@Test
|
||||
public void testColumnMaskEnabled() throws ImpalaException {
|
||||
if (authzProvider_ == AuthorizationProvider.SENTRY) return;
|
||||
|
||||
String policyName = "col_mask";
|
||||
for (String tableName: new String[]{"alltypes", "alltypes_view"}) {
|
||||
BackendConfig.INSTANCE.setColumnMaskingEnabled(false);
|
||||
@@ -3005,8 +2948,6 @@ public class AuthorizationStmtTest extends AuthorizationTestBase {
|
||||
|
||||
@Test
|
||||
public void testRowFilterEnabled() throws ImpalaException {
|
||||
if (authzProvider_ == AuthorizationProvider.SENTRY) return;
|
||||
|
||||
String policyName = "row_filter";
|
||||
for (String tableName: new String[]{"alltypes", "alltypes_view"}) {
|
||||
String json = String.format("{\n" +
|
||||
@@ -3118,8 +3059,6 @@ public class AuthorizationStmtTest extends AuthorizationTestBase {
|
||||
*/
|
||||
@Test
|
||||
public void testRangerObjectOwnership() throws Exception {
|
||||
if (authzProvider_ == AuthorizationProvider.SENTRY) return;
|
||||
|
||||
// 'as_owner_' is by default set to false for AuthorizationTestBase. But since this
|
||||
// test is meant for testing Ranger's behavior when the requesting user is the owner
|
||||
// of the resources, we set 'as_owner_' to true.
|
||||
|
||||
@@ -1,673 +0,0 @@
|
||||
// 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.authorization;
|
||||
|
||||
import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTH_TO_LOCAL;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hive.service.rpc.thrift.TGetColumnsReq;
|
||||
import org.apache.hive.service.rpc.thrift.TGetCrossReferenceReq;
|
||||
import org.apache.hive.service.rpc.thrift.TGetPrimaryKeysReq;
|
||||
import org.apache.hive.service.rpc.thrift.TGetSchemasReq;
|
||||
import org.apache.hive.service.rpc.thrift.TGetTablesReq;
|
||||
import org.apache.impala.analysis.AnalysisContext;
|
||||
import org.apache.impala.authorization.sentry.SentryAuthorizationConfig;
|
||||
import org.apache.impala.authorization.sentry.SentryAuthorizationFactory;
|
||||
import org.apache.impala.catalog.CatalogException;
|
||||
import org.apache.impala.catalog.FeDb;
|
||||
import org.apache.impala.catalog.Role;
|
||||
import org.apache.impala.common.AnalysisException;
|
||||
import org.apache.impala.common.FrontendTestBase;
|
||||
import org.apache.impala.common.ImpalaException;
|
||||
import org.apache.impala.common.InternalException;
|
||||
import org.apache.impala.common.RuntimeEnv;
|
||||
import org.apache.impala.testutil.TestSentryGroupMapper;
|
||||
import org.apache.impala.service.Frontend;
|
||||
import org.apache.impala.testutil.ImpaladTestCatalog;
|
||||
import org.apache.impala.thrift.TMetadataOpRequest;
|
||||
import org.apache.impala.thrift.TMetadataOpcode;
|
||||
import org.apache.impala.thrift.TNetworkAddress;
|
||||
import org.apache.impala.thrift.TPrincipalType;
|
||||
import org.apache.impala.thrift.TPrivilege;
|
||||
import org.apache.impala.thrift.TPrivilegeLevel;
|
||||
import org.apache.impala.thrift.TPrivilegeScope;
|
||||
import org.apache.impala.thrift.TResultSet;
|
||||
import org.apache.impala.thrift.TSessionState;
|
||||
import org.apache.impala.util.PatternMatcher;
|
||||
import org.apache.sentry.provider.common.ResourceAuthorizationProvider;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Assume;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
public class AuthorizationTest extends FrontendTestBase {
|
||||
public static final User USER = new User(System.getProperty("user.name"));
|
||||
// Tables in functional that the current user and 'test_user' have table- or
|
||||
// column-level SELECT or INSERT permission. I.e. that should be returned by
|
||||
// 'SHOW TABLES'.
|
||||
private static final List<String> FUNCTIONAL_VISIBLE_TABLES = Lists.newArrayList(
|
||||
"alltypes", "alltypesagg", "alltypeserror", "alltypessmall", "alltypestiny",
|
||||
"child_table", "parent_table", "parent_table_2");
|
||||
|
||||
private static final SentryAuthorizationConfig AUTHZ_CONFIG =
|
||||
SentryAuthorizationConfig.createHadoopGroupAuthConfig("server1",
|
||||
System.getenv("IMPALA_HOME") + "/fe/src/test/resources/sentry-site.xml");
|
||||
|
||||
private static ImpaladTestCatalog AUTHZ_CATALOG = null;
|
||||
private static AuthorizationFactory AUTHZ_FACTORY = null;
|
||||
private static Frontend AUTHZ_FE = null;
|
||||
|
||||
private final AnalysisContext authzCtx;
|
||||
|
||||
public AuthorizationTest() throws ImpalaException {
|
||||
String envDisableSentry = System.getenv("DISABLE_SENTRY");
|
||||
if (envDisableSentry != null) Assume.assumeTrue(envDisableSentry.equals("false"));
|
||||
|
||||
// Initialize 'AUTHZ_CATALOG' and 'AUTHZ_FACTORY' only if $DISABLE_SENTRY evaluates
|
||||
// to false, in which case the Sentry service is available to answer the RPC's
|
||||
// resulting from SentryAuthorizationFactory().
|
||||
AUTHZ_CATALOG = new ImpaladTestCatalog(new SentryAuthorizationFactory(AUTHZ_CONFIG));
|
||||
AUTHZ_FACTORY = new SentryAuthorizationFactory(AUTHZ_CONFIG);
|
||||
try {
|
||||
AUTHZ_FE = new Frontend(AUTHZ_FACTORY, AUTHZ_CATALOG);
|
||||
} catch (ImpalaException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
authzCtx = createAnalysisCtx(AUTHZ_FACTORY, USER.getName());
|
||||
setupImpalaCatalog(AUTHZ_CATALOG);
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUp() throws ImpalaException {
|
||||
RuntimeEnv.INSTANCE.setTestEnv(true);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void cleanUp() throws ImpalaException {
|
||||
RuntimeEnv.INSTANCE.reset();
|
||||
}
|
||||
|
||||
private static void setupImpalaCatalog(ImpaladTestCatalog catalog)
|
||||
throws ImpalaException {
|
||||
// Create admin user
|
||||
String role_ = "admin";
|
||||
TPrivilege privilege = new TPrivilege(TPrivilegeLevel.ALL, TPrivilegeScope.SERVER,
|
||||
false);
|
||||
Role role = catalog.addRole(role_);
|
||||
addRolePrivilege(catalog, privilege, role);
|
||||
catalog.addRoleGrantGroup(role_, TestSentryGroupMapper.SERVER_ADMIN);
|
||||
|
||||
// Create test users
|
||||
Set<String> groups = new HashSet<>(Arrays.asList(
|
||||
USER.getName(),
|
||||
TestSentryGroupMapper.AUTH_TO_LOCAL,
|
||||
TestSentryGroupMapper.TEST_USER));
|
||||
role_ = "testRole";
|
||||
role = catalog.addRole(role_);
|
||||
|
||||
|
||||
// Insert on functional.alltypes
|
||||
privilege = new TPrivilege(TPrivilegeLevel.INSERT, TPrivilegeScope.TABLE, false);
|
||||
privilege.setDb_name("functional");
|
||||
privilege.setTable_name("alltypes");
|
||||
addRolePrivilege(catalog, privilege, role);
|
||||
|
||||
// All on functional.[alltypesagg, alltypeserror, alltypestiny]
|
||||
List<String> tables = Arrays.asList(
|
||||
"alltypesagg",
|
||||
"alltypeserror",
|
||||
"alltypestiny");
|
||||
for (String table : tables) {
|
||||
privilege = new TPrivilege(TPrivilegeLevel.ALL, TPrivilegeScope.TABLE, false);
|
||||
privilege.setDb_name("functional");
|
||||
privilege.setTable_name(table);
|
||||
addRolePrivilege(catalog, privilege, role);
|
||||
}
|
||||
|
||||
// Columns [id, int_col, year] on functional.alltypessmall
|
||||
List<String> columns = Arrays.asList("id", "int_col", "year");
|
||||
for (String column : columns) {
|
||||
privilege = new TPrivilege(TPrivilegeLevel.SELECT, TPrivilegeScope.COLUMN, false);
|
||||
privilege.setDb_name("functional");
|
||||
privilege.setTable_name("alltypessmall");
|
||||
privilege.setColumn_name(column);
|
||||
addRolePrivilege(catalog, privilege, role);
|
||||
}
|
||||
|
||||
// Select on tpcds
|
||||
privilege = new TPrivilege(TPrivilegeLevel.SELECT, TPrivilegeScope.DATABASE, false);
|
||||
privilege.setDb_name("tpcds");
|
||||
addRolePrivilege(catalog, privilege, role);
|
||||
|
||||
// Select on tpch
|
||||
privilege = new TPrivilege(TPrivilegeLevel.SELECT, TPrivilegeScope.DATABASE, false);
|
||||
privilege.setDb_name("tpch");
|
||||
addRolePrivilege(catalog, privilege, role);
|
||||
|
||||
// Select on parent_table_2
|
||||
privilege = new TPrivilege(TPrivilegeLevel.SELECT, TPrivilegeScope.TABLE, false);
|
||||
privilege.setDb_name("functional");
|
||||
privilege.setTable_name("parent_table_2");
|
||||
addRolePrivilege(catalog, privilege, role);
|
||||
|
||||
// Add privilege on "year" for parent table.
|
||||
privilege = new TPrivilege(TPrivilegeLevel.SELECT, TPrivilegeScope.COLUMN, false);
|
||||
privilege.setDb_name("functional");
|
||||
privilege.setTable_name("parent_table");
|
||||
privilege.setColumn_name("year");
|
||||
addRolePrivilege(catalog, privilege, role);
|
||||
|
||||
// Add SELECT privilege on child_table
|
||||
privilege = new TPrivilege(TPrivilegeLevel.SELECT, TPrivilegeScope.TABLE, false);
|
||||
privilege.setDb_name("functional");
|
||||
privilege.setTable_name("child_table");
|
||||
addRolePrivilege(catalog, privilege, role);
|
||||
|
||||
for (String group: groups) {
|
||||
catalog.addRoleGrantGroup(role_, group);
|
||||
}
|
||||
}
|
||||
|
||||
private static void addRolePrivilege(ImpaladTestCatalog catalog, TPrivilege privilege,
|
||||
Role role) throws CatalogException {
|
||||
privilege.setServer_name("server1");
|
||||
privilege.setPrincipal_type(TPrincipalType.ROLE);
|
||||
privilege.setPrincipal_id(role.getId());
|
||||
catalog.addRolePrivilege(role.getName(), privilege);
|
||||
}
|
||||
|
||||
@After
|
||||
public void TestTPCHCleanup() throws AuthorizationException, AnalysisException {
|
||||
// Failure to cleanup TPCH can cause:
|
||||
// TestDropDatabase(org.apache.impala.analysis.AuthorizationTest):
|
||||
// Cannot drop non-empty database: tpch
|
||||
if (catalog_.getDb("tpch").numFunctions() != 0) {
|
||||
fail("Failed to clean up functions in tpch.");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestShowDbResultsFiltered() throws ImpalaException {
|
||||
// These are the only dbs that should show up because they are the only
|
||||
// dbs the user has any permissions on.
|
||||
List<String> expectedDbs = Lists.newArrayList("default", "functional", "tpcds",
|
||||
"tpch");
|
||||
|
||||
List<? extends FeDb> dbs = AUTHZ_FE.getDbs(
|
||||
PatternMatcher.createHivePatternMatcher("*"), USER);
|
||||
assertEquals(expectedDbs, extractDbNames(dbs));
|
||||
|
||||
dbs = AUTHZ_FE.getDbs(PatternMatcher.MATCHER_MATCH_ALL, USER);
|
||||
assertEquals(expectedDbs, extractDbNames(dbs));
|
||||
|
||||
dbs = AUTHZ_FE.getDbs(PatternMatcher.createHivePatternMatcher(null), USER);
|
||||
assertEquals(expectedDbs, extractDbNames(dbs));
|
||||
|
||||
dbs = AUTHZ_FE.getDbs(PatternMatcher.MATCHER_MATCH_NONE, USER);
|
||||
assertEquals(Collections.emptyList(), extractDbNames(dbs));
|
||||
|
||||
dbs = AUTHZ_FE.getDbs(PatternMatcher.createHivePatternMatcher(""), USER);
|
||||
assertEquals(Collections.emptyList(), extractDbNames(dbs));
|
||||
|
||||
dbs = AUTHZ_FE.getDbs(PatternMatcher.createHivePatternMatcher("functional_rc"), USER);
|
||||
assertEquals(Collections.emptyList(), extractDbNames(dbs));
|
||||
|
||||
dbs = AUTHZ_FE.getDbs(PatternMatcher.createHivePatternMatcher("tp*|de*"), USER);
|
||||
expectedDbs = Lists.newArrayList("default", "tpcds", "tpch");
|
||||
assertEquals(expectedDbs, extractDbNames(dbs));
|
||||
}
|
||||
|
||||
private List<String> extractDbNames(List<? extends FeDb> dbs) {
|
||||
List<String> names = Lists.newArrayListWithCapacity(dbs.size());
|
||||
for (FeDb db: dbs) names.add(db.getName());
|
||||
return names;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestShowTableResultsFiltered() throws ImpalaException {
|
||||
List<String> tables = AUTHZ_FE.getTableNames("functional",
|
||||
PatternMatcher.createHivePatternMatcher("*"), USER);
|
||||
Assert.assertEquals(FUNCTIONAL_VISIBLE_TABLES, tables);
|
||||
|
||||
tables = AUTHZ_FE.getTableNames("functional",
|
||||
PatternMatcher.MATCHER_MATCH_ALL, USER);
|
||||
Assert.assertEquals(FUNCTIONAL_VISIBLE_TABLES, tables);
|
||||
|
||||
tables = AUTHZ_FE.getTableNames("functional",
|
||||
PatternMatcher.createHivePatternMatcher(null), USER);
|
||||
Assert.assertEquals(FUNCTIONAL_VISIBLE_TABLES, tables);
|
||||
|
||||
tables = AUTHZ_FE.getTableNames("functional",
|
||||
PatternMatcher.MATCHER_MATCH_NONE, USER);
|
||||
Assert.assertEquals(Collections.emptyList(), tables);
|
||||
|
||||
tables = AUTHZ_FE.getTableNames("functional",
|
||||
PatternMatcher.createHivePatternMatcher(""), USER);
|
||||
Assert.assertEquals(Collections.emptyList(), tables);
|
||||
|
||||
tables = AUTHZ_FE.getTableNames("functional",
|
||||
PatternMatcher.createHivePatternMatcher("alltypes*"), USER);
|
||||
List<String> expectedTables = Lists.newArrayList(
|
||||
"alltypes", "alltypesagg", "alltypeserror", "alltypessmall", "alltypestiny");
|
||||
Assert.assertEquals(expectedTables, tables);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestHs2GetTables() throws ImpalaException {
|
||||
TMetadataOpRequest req = new TMetadataOpRequest();
|
||||
req.setSession(createSessionState("default", USER));
|
||||
req.opcode = TMetadataOpcode.GET_TABLES;
|
||||
req.get_tables_req = new TGetTablesReq();
|
||||
req.get_tables_req.setSchemaName("functional");
|
||||
// Get all tables
|
||||
req.get_tables_req.setTableName("%");
|
||||
TResultSet resp = AUTHZ_FE.execHiveServer2MetadataOp(req);
|
||||
assertEquals(FUNCTIONAL_VISIBLE_TABLES.size(), resp.rows.size());
|
||||
for (int i = 0; i < resp.rows.size(); ++i) {
|
||||
assertEquals(FUNCTIONAL_VISIBLE_TABLES.get(i),
|
||||
resp.rows.get(i).colVals.get(2).string_val.toLowerCase());
|
||||
}
|
||||
// Pattern "" and null is the same as "%" to match all
|
||||
req.get_tables_req.setTableName("");
|
||||
resp = AUTHZ_FE.execHiveServer2MetadataOp(req);
|
||||
assertEquals(FUNCTIONAL_VISIBLE_TABLES.size(), resp.rows.size());
|
||||
for (int i = 0; i < resp.rows.size(); ++i) {
|
||||
assertEquals(FUNCTIONAL_VISIBLE_TABLES.get(i),
|
||||
resp.rows.get(i).colVals.get(2).string_val.toLowerCase());
|
||||
}
|
||||
req.get_tables_req.setTableName(null);
|
||||
resp = AUTHZ_FE.execHiveServer2MetadataOp(req);
|
||||
assertEquals(FUNCTIONAL_VISIBLE_TABLES.size(), resp.rows.size());
|
||||
for (int i = 0; i < resp.rows.size(); ++i) {
|
||||
assertEquals(FUNCTIONAL_VISIBLE_TABLES.get(i),
|
||||
resp.rows.get(i).colVals.get(2).string_val.toLowerCase());
|
||||
}
|
||||
// Pattern ".*" matches all and "." is a wildcard that matches any single character
|
||||
req.get_tables_req.setTableName(".*");
|
||||
resp = AUTHZ_FE.execHiveServer2MetadataOp(req);
|
||||
assertEquals(FUNCTIONAL_VISIBLE_TABLES.size(), resp.rows.size());
|
||||
for (int i = 0; i < resp.rows.size(); ++i) {
|
||||
assertEquals(FUNCTIONAL_VISIBLE_TABLES.get(i),
|
||||
resp.rows.get(i).colVals.get(2).string_val.toLowerCase());
|
||||
}
|
||||
req.get_tables_req.setTableName("alltypesag.");
|
||||
resp = AUTHZ_FE.execHiveServer2MetadataOp(req);
|
||||
List<String> expectedTblNames = Lists.newArrayList("alltypesagg");
|
||||
assertEquals(expectedTblNames.size(), resp.rows.size());
|
||||
for (int i = 0; i < resp.rows.size(); ++i) {
|
||||
assertEquals(expectedTblNames.get(i),
|
||||
resp.rows.get(i).colVals.get(2).string_val.toLowerCase());
|
||||
}
|
||||
// "_" is a wildcard for a single character
|
||||
req.get_tables_req.setTableName("alltypesag_");
|
||||
resp = AUTHZ_FE.execHiveServer2MetadataOp(req);
|
||||
expectedTblNames = Lists.newArrayList("alltypesagg");
|
||||
assertEquals(expectedTblNames.size(), resp.rows.size());
|
||||
for (int i = 0; i < resp.rows.size(); ++i) {
|
||||
assertEquals(expectedTblNames.get(i),
|
||||
resp.rows.get(i).colVals.get(2).string_val.toLowerCase());
|
||||
}
|
||||
|
||||
// Get all tables of tpcds
|
||||
final int numTpcdsTables = 24;
|
||||
req.get_tables_req.setSchemaName("tpcds");
|
||||
req.get_tables_req.setTableName("%");
|
||||
resp = AUTHZ_FE.execHiveServer2MetadataOp(req);
|
||||
assertEquals(numTpcdsTables, resp.rows.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestHs2GetPrimaryKeys() throws ImpalaException {
|
||||
// Only one of the pk columns has privileges. This should not return anything.
|
||||
TMetadataOpRequest req = new TMetadataOpRequest();
|
||||
req.setSession(createSessionState("default", USER));
|
||||
req.opcode = TMetadataOpcode.GET_PRIMARY_KEYS;
|
||||
req.get_primary_keys_req = new TGetPrimaryKeysReq();
|
||||
req.get_primary_keys_req.setSchemaName("functional");
|
||||
req.get_primary_keys_req.setTableName("parent_table");
|
||||
|
||||
TResultSet resp = AUTHZ_FE.execHiveServer2MetadataOp(req);
|
||||
assertEquals(0, resp.rows.size());
|
||||
|
||||
// PK column has privileges, should return it.
|
||||
req = new TMetadataOpRequest();
|
||||
req.setSession(createSessionState("default", USER));
|
||||
req.opcode = TMetadataOpcode.GET_PRIMARY_KEYS;
|
||||
req.get_primary_keys_req = new TGetPrimaryKeysReq();
|
||||
req.get_primary_keys_req.setSchemaName("functional");
|
||||
req.get_primary_keys_req.setTableName("parent_table_2");
|
||||
|
||||
resp = AUTHZ_FE.execHiveServer2MetadataOp(req);
|
||||
assertEquals(1, resp.rows.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestHs2GetCrossReference() throws ImpalaException {
|
||||
// On parent_table, only one of the pk columns has privileges for USER, hence the
|
||||
// entire SQLPrimaryKey sequence will be filtered out.
|
||||
TMetadataOpRequest req = new TMetadataOpRequest();
|
||||
req.setSession(createSessionState("default", USER));
|
||||
req.opcode = TMetadataOpcode.GET_CROSS_REFERENCE;
|
||||
req.get_cross_reference_req = new TGetCrossReferenceReq();
|
||||
req.get_cross_reference_req.setForeignSchemaName("functional");
|
||||
req.get_cross_reference_req.setForeignTableName("child_table");
|
||||
|
||||
TResultSet resp = AUTHZ_FE.execHiveServer2MetadataOp(req);
|
||||
// Returns just 1 SQLForeignKeys instead of 3.
|
||||
assertEquals(1, resp.rows.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestHs2GetSchema() throws ImpalaException {
|
||||
TMetadataOpRequest req = new TMetadataOpRequest();
|
||||
req.setSession(createSessionState("default", USER));
|
||||
req.opcode = TMetadataOpcode.GET_SCHEMAS;
|
||||
req.get_schemas_req = new TGetSchemasReq();
|
||||
// Get all schema (databases).
|
||||
req.get_schemas_req.setSchemaName("%");
|
||||
TResultSet resp = AUTHZ_FE.execHiveServer2MetadataOp(req);
|
||||
List<String> expectedDbs = Lists.newArrayList("default", "functional", "tpcds",
|
||||
"tpch");
|
||||
assertEquals(expectedDbs.size(), resp.rows.size());
|
||||
for (int i = 0; i < resp.rows.size(); ++i) {
|
||||
assertEquals(expectedDbs.get(i),
|
||||
resp.rows.get(i).colVals.get(0).string_val.toLowerCase());
|
||||
}
|
||||
// Pattern "" and null is the same as "%" to match all
|
||||
req.get_schemas_req.setSchemaName("");
|
||||
resp = AUTHZ_FE.execHiveServer2MetadataOp(req);
|
||||
assertEquals(expectedDbs.size(), resp.rows.size());
|
||||
for (int i = 0; i < resp.rows.size(); ++i) {
|
||||
assertEquals(expectedDbs.get(i),
|
||||
resp.rows.get(i).colVals.get(0).string_val.toLowerCase());
|
||||
}
|
||||
req.get_schemas_req.setSchemaName(null);
|
||||
resp = AUTHZ_FE.execHiveServer2MetadataOp(req);
|
||||
assertEquals(expectedDbs.size(), resp.rows.size());
|
||||
for (int i = 0; i < resp.rows.size(); ++i) {
|
||||
assertEquals(expectedDbs.get(i),
|
||||
resp.rows.get(i).colVals.get(0).string_val.toLowerCase());
|
||||
}
|
||||
// Pattern ".*" matches all and "." is a wildcard that matches any single character
|
||||
req.get_schemas_req.setSchemaName(".*");
|
||||
resp = AUTHZ_FE.execHiveServer2MetadataOp(req);
|
||||
assertEquals(expectedDbs.size(), resp.rows.size());
|
||||
for (int i = 0; i < resp.rows.size(); ++i) {
|
||||
assertEquals(expectedDbs.get(i),
|
||||
resp.rows.get(i).colVals.get(0).string_val.toLowerCase());
|
||||
}
|
||||
req.get_schemas_req.setSchemaName("defaul.");
|
||||
resp = AUTHZ_FE.execHiveServer2MetadataOp(req);
|
||||
expectedDbs = Lists.newArrayList("default");
|
||||
assertEquals(expectedDbs.size(), resp.rows.size());
|
||||
for (int i = 0; i < resp.rows.size(); ++i) {
|
||||
assertEquals(expectedDbs.get(i),
|
||||
resp.rows.get(i).colVals.get(0).string_val.toLowerCase());
|
||||
}
|
||||
// "_" is a wildcard that matches any single character
|
||||
req.get_schemas_req.setSchemaName("defaul_");
|
||||
resp = AUTHZ_FE.execHiveServer2MetadataOp(req);
|
||||
expectedDbs = Lists.newArrayList("default");
|
||||
assertEquals(expectedDbs.size(), resp.rows.size());
|
||||
for (int i = 0; i < resp.rows.size(); ++i) {
|
||||
assertEquals(expectedDbs.get(i),
|
||||
resp.rows.get(i).colVals.get(0).string_val.toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestHs2GetColumns() throws ImpalaException {
|
||||
// It should return one column: alltypes.string_col.
|
||||
TMetadataOpRequest req = new TMetadataOpRequest();
|
||||
req.opcode = TMetadataOpcode.GET_COLUMNS;
|
||||
req.setSession(createSessionState("default", USER));
|
||||
req.get_columns_req = new TGetColumnsReq();
|
||||
req.get_columns_req.setSchemaName("functional");
|
||||
req.get_columns_req.setTableName("alltypes");
|
||||
req.get_columns_req.setColumnName("stri%");
|
||||
TResultSet resp = AUTHZ_FE.execHiveServer2MetadataOp(req);
|
||||
assertEquals(1, resp.rows.size());
|
||||
|
||||
// User does not have permission to access the table, no results should be returned.
|
||||
req.get_columns_req.setTableName("alltypesnopart");
|
||||
resp = AUTHZ_FE.execHiveServer2MetadataOp(req);
|
||||
assertEquals(0, resp.rows.size());
|
||||
|
||||
// User does not have permission to access db or table, no results should be
|
||||
// returned.
|
||||
req.get_columns_req.setSchemaName("functional_seq_gzip");
|
||||
req.get_columns_req.setTableName("alltypes");
|
||||
resp = AUTHZ_FE.execHiveServer2MetadataOp(req);
|
||||
assertEquals(0, resp.rows.size());
|
||||
|
||||
// User has SELECT privileges on all table columns but no table-level privileges.
|
||||
req.get_columns_req.setSchemaName("functional");
|
||||
req.get_columns_req.setTableName("alltypestiny");
|
||||
req.get_columns_req.setColumnName("%");
|
||||
resp = AUTHZ_FE.execHiveServer2MetadataOp(req);
|
||||
assertEquals(13, resp.rows.size());
|
||||
|
||||
// User has SELECT privileges on some columns but no table-level privileges.
|
||||
req.get_columns_req.setSchemaName("functional");
|
||||
req.get_columns_req.setTableName("alltypessmall");
|
||||
req.get_columns_req.setColumnName("%");
|
||||
resp = AUTHZ_FE.execHiveServer2MetadataOp(req);
|
||||
String[] expectedColumnNames = {"id", "int_col", "year"};
|
||||
assertEquals(expectedColumnNames.length, resp.rows.size());
|
||||
for (int i = 0; i < resp.rows.size(); ++i) {
|
||||
assertEquals(expectedColumnNames[i],
|
||||
resp.rows.get(i).colVals.get(3).string_val.toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestShortUsernameUsed() throws Exception {
|
||||
// Different long variations of the same username.
|
||||
List<User> users = Lists.newArrayList(
|
||||
// Historical note: Hadoop 2 accepts kerberos names missing a realm, but insists
|
||||
// on having a terminating '@' even when the default realm is intended. Hadoop 3
|
||||
// now has more normal name conventions, where to specify the default realm,
|
||||
// everything after and including the '@' character is omitted.
|
||||
new User(USER.getName() + "/abc.host.com"),
|
||||
new User(USER.getName() + "/abc.host.com@REAL.COM"),
|
||||
new User(USER.getName() + "@REAL.COM"));
|
||||
for (User user: users) {
|
||||
AnalysisContext ctx = createAnalysisCtx(
|
||||
new SentryAuthorizationFactory(AUTHZ_CONFIG), user.getName());
|
||||
|
||||
// Can select from table that user has privileges on.
|
||||
AuthzOk(ctx, "select * from functional.alltypesagg");
|
||||
|
||||
// Unqualified table name.
|
||||
AuthzError(ctx, "select * from alltypes",
|
||||
"User '%s' does not have privileges to execute 'SELECT' on: default.alltypes");
|
||||
}
|
||||
// If the first character is '/', the short username should be the same as
|
||||
// the full username.
|
||||
assertEquals("/" + USER.getName(), new User("/" + USER.getName()).getShortName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestConfigValidation() throws InternalException {
|
||||
String sentryConfig = AUTHZ_CONFIG.getSentryConfig().getConfigFile();
|
||||
// Valid configs pass validation.
|
||||
SentryAuthorizationConfig config =
|
||||
SentryAuthorizationConfig.createHadoopGroupAuthConfig("server1", sentryConfig);
|
||||
Assert.assertTrue(config.isEnabled());
|
||||
|
||||
config = SentryAuthorizationConfig.createHadoopGroupAuthConfig("server1",
|
||||
sentryConfig);
|
||||
Assert.assertTrue(config.isEnabled());
|
||||
|
||||
// Invalid configs
|
||||
// No sentry configuration file.
|
||||
try {
|
||||
config = SentryAuthorizationConfig.createHadoopGroupAuthConfig("server1", null);
|
||||
Assert.assertTrue(config.isEnabled());
|
||||
} catch (Exception e) {
|
||||
Assert.assertEquals("A valid path to a sentry-site.xml config " +
|
||||
"file must be set using --sentry_config to enable authorization.",
|
||||
e.getMessage());
|
||||
}
|
||||
|
||||
// Empty / null server name.
|
||||
try {
|
||||
config = SentryAuthorizationConfig.createHadoopGroupAuthConfig("", sentryConfig);
|
||||
Assert.assertTrue(config.isEnabled());
|
||||
fail("Expected configuration to fail.");
|
||||
} catch (IllegalArgumentException e) {
|
||||
Assert.assertEquals(
|
||||
"Authorization is enabled but the server name is null or empty. Set the " +
|
||||
"server name using the impalad --server_name flag.",
|
||||
e.getMessage());
|
||||
}
|
||||
try {
|
||||
config = SentryAuthorizationConfig.createHadoopGroupAuthConfig(null, sentryConfig);
|
||||
Assert.assertTrue(config.isEnabled());
|
||||
fail("Expected configuration to fail.");
|
||||
} catch (IllegalArgumentException e) {
|
||||
Assert.assertEquals(
|
||||
"Authorization is enabled but the server name is null or empty. Set the " +
|
||||
"server name using the impalad --server_name flag.",
|
||||
e.getMessage());
|
||||
}
|
||||
|
||||
// Sentry config file does not exist.
|
||||
try {
|
||||
config = SentryAuthorizationConfig.createHadoopGroupAuthConfig("server1",
|
||||
"/path/does/not/exist.xml");
|
||||
Assert.assertTrue(config.isEnabled());
|
||||
fail("Expected configuration to fail.");
|
||||
} catch (Exception e) {
|
||||
Assert.assertEquals(
|
||||
"Sentry configuration file does not exist: \"/path/does/not/exist.xml\"",
|
||||
e.getMessage());
|
||||
}
|
||||
|
||||
// Invalid ResourcePolicyProvider class name.
|
||||
try {
|
||||
config = new SentryAuthorizationConfig("server1", sentryConfig,
|
||||
"ClassDoesNotExist");
|
||||
Assert.assertTrue(config.isEnabled());
|
||||
fail("Expected configuration to fail.");
|
||||
} catch (IllegalArgumentException e) {
|
||||
Assert.assertEquals(
|
||||
"The authorization policy provider class 'ClassDoesNotExist' was not found.",
|
||||
e.getMessage());
|
||||
}
|
||||
|
||||
// Valid class name, but class is not derived from ResourcePolicyProvider
|
||||
try {
|
||||
config = new SentryAuthorizationConfig("server1", sentryConfig,
|
||||
this.getClass().getName());
|
||||
Assert.assertTrue(config.isEnabled()); fail("Expected configuration to fail.");
|
||||
} catch (IllegalArgumentException e) {
|
||||
Assert.assertEquals(String.format("The authorization policy " +
|
||||
"provider class '%s' must be a subclass of '%s'.", this.getClass().getName(),
|
||||
ResourceAuthorizationProvider.class.getName()),
|
||||
e.getMessage());
|
||||
}
|
||||
|
||||
// Config validations skipped if authorization disabled
|
||||
config = new SentryAuthorizationConfig("", "", "");
|
||||
Assert.assertFalse(config.isEnabled());
|
||||
config = new SentryAuthorizationConfig(null, "", null);
|
||||
Assert.assertFalse(config.isEnabled());
|
||||
config = new SentryAuthorizationConfig(null, null, null);
|
||||
Assert.assertFalse(config.isEnabled());
|
||||
}
|
||||
|
||||
private void TestWithIncorrectConfig(AuthorizationConfig authzConfig, User user)
|
||||
throws ImpalaException {
|
||||
SentryAuthorizationFactory authzFactory = new SentryAuthorizationFactory(authzConfig);
|
||||
Frontend fe = new Frontend(authzFactory, AUTHZ_CATALOG);
|
||||
AnalysisContext ctx = createAnalysisCtx(authzFactory, user.getName());
|
||||
AuthzError(fe, ctx, "select * from functional.alltypesagg",
|
||||
"User '%s' does not have privileges to execute 'SELECT' on: " +
|
||||
"functional.alltypesagg");
|
||||
AuthzError(fe, ctx, "drop table tpch.lineitem",
|
||||
"User '%s' does not have privileges to execute 'DROP' on: tpch.lineitem");
|
||||
AuthzError(fe, ctx, "show tables in functional",
|
||||
"User '%s' does not have privileges to access: functional.*");
|
||||
}
|
||||
|
||||
private void AuthzOk(String stmt) throws ImpalaException {
|
||||
AuthzOk(authzCtx, stmt);
|
||||
}
|
||||
|
||||
private void AuthzOk(AnalysisContext context, String stmt) throws ImpalaException {
|
||||
AuthzOk(AUTHZ_FE, context, stmt);
|
||||
}
|
||||
|
||||
private void AuthzOk(Frontend fe, AnalysisContext context, String stmt)
|
||||
throws ImpalaException {
|
||||
parseAndAnalyze(stmt, context, fe);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that a given statement fails authorization and the expected error
|
||||
* string matches.
|
||||
*/
|
||||
private void AuthzError(String stmt, String expectedErrorString)
|
||||
throws ImpalaException {
|
||||
AuthzError(authzCtx, stmt, expectedErrorString);
|
||||
}
|
||||
|
||||
private void AuthzError(AnalysisContext ctx, String stmt, String expectedErrorString)
|
||||
throws ImpalaException {
|
||||
AuthzError(AUTHZ_FE, ctx, stmt, expectedErrorString);
|
||||
}
|
||||
|
||||
private void AuthzError(Frontend fe, AnalysisContext ctx,
|
||||
String stmt, String expectedErrorString)
|
||||
throws ImpalaException {
|
||||
Preconditions.checkNotNull(expectedErrorString);
|
||||
try {
|
||||
parseAndAnalyze(stmt, ctx, fe);
|
||||
} catch (AuthorizationException e) {
|
||||
// Insert the username into the error.
|
||||
expectedErrorString = String.format(expectedErrorString, ctx.getUser());
|
||||
String errorString = e.getMessage();
|
||||
Assert.assertTrue(
|
||||
"got error:\n" + errorString + "\nexpected:\n" + expectedErrorString,
|
||||
errorString.startsWith(expectedErrorString));
|
||||
return;
|
||||
}
|
||||
fail("Stmt didn't result in authorization error: " + stmt);
|
||||
}
|
||||
|
||||
private static TSessionState createSessionState(String defaultDb, User user) {
|
||||
return new TSessionState(null, null,
|
||||
defaultDb, user.getName(), new TNetworkAddress("", 0));
|
||||
}
|
||||
}
|
||||
@@ -27,9 +27,6 @@ import org.apache.impala.authorization.ranger.RangerAuthorizationFactory;
|
||||
import org.apache.impala.authorization.ranger.RangerCatalogdAuthorizationManager;
|
||||
import org.apache.impala.authorization.ranger.RangerImpalaPlugin;
|
||||
import org.apache.impala.authorization.ranger.RangerImpalaResourceBuilder;
|
||||
import org.apache.impala.authorization.sentry.SentryAuthorizationConfig;
|
||||
import org.apache.impala.authorization.sentry.SentryAuthorizationFactory;
|
||||
import org.apache.impala.authorization.sentry.SentryPolicyService;
|
||||
import org.apache.impala.catalog.Role;
|
||||
import org.apache.impala.catalog.ScalarFunction;
|
||||
import org.apache.impala.catalog.Type;
|
||||
@@ -112,7 +109,6 @@ public abstract class AuthorizationTestBase extends FrontendTestBase {
|
||||
protected final AuthorizationFactory authzFactory_;
|
||||
protected final AuthorizationProvider authzProvider_;
|
||||
protected final AnalysisContext authzCtx_;
|
||||
protected final SentryPolicyService sentryService_;
|
||||
protected final ImpaladTestCatalog authzCatalog_;
|
||||
protected final Frontend authzFrontend_;
|
||||
protected final RangerImpalaPlugin rangerImpalaPlugin_;
|
||||
@@ -122,20 +118,6 @@ public abstract class AuthorizationTestBase extends FrontendTestBase {
|
||||
throws ImpalaException {
|
||||
authzProvider_ = authzProvider;
|
||||
switch (authzProvider) {
|
||||
case SENTRY:
|
||||
user_ = new User(System.getProperty("user.name"));
|
||||
authzConfig_ = SentryAuthorizationConfig.createHadoopGroupAuthConfig(
|
||||
"server1",
|
||||
System.getenv("IMPALA_HOME") + "/fe/src/test/resources/sentry-site.xml");
|
||||
authzFactory_ = createAuthorizationFactory(authzProvider);
|
||||
authzCtx_ = createAnalysisCtx(authzFactory_, user_.getName());
|
||||
authzCatalog_ = new ImpaladTestCatalog(authzFactory_);
|
||||
authzFrontend_ = new Frontend(authzFactory_, authzCatalog_);
|
||||
sentryService_ = new SentryPolicyService(
|
||||
((SentryAuthorizationConfig) authzConfig_).getSentryConfig());
|
||||
rangerImpalaPlugin_ = null;
|
||||
rangerRestClient_ = null;
|
||||
break;
|
||||
case RANGER:
|
||||
user_ = new User("non_owner");
|
||||
authzConfig_ = new RangerAuthorizationConfig(RANGER_SERVICE_TYPE, RANGER_APP_ID,
|
||||
@@ -148,7 +130,6 @@ public abstract class AuthorizationTestBase extends FrontendTestBase {
|
||||
((RangerAuthorizationChecker) authzFrontend_.getAuthzChecker())
|
||||
.getRangerImpalaPlugin();
|
||||
assertEquals("test-cluster", rangerImpalaPlugin_.getClusterName());
|
||||
sentryService_ = null;
|
||||
rangerRestClient_ = new RangerRESTClient(RANGER_ADMIN_URL, null,
|
||||
rangerImpalaPlugin_.getConfig());
|
||||
rangerRestClient_.setBasicAuthInfo(RANGER_USER, RANGER_PASSWORD);
|
||||
@@ -161,9 +142,7 @@ public abstract class AuthorizationTestBase extends FrontendTestBase {
|
||||
|
||||
protected AuthorizationFactory createAuthorizationFactory(
|
||||
AuthorizationProvider authzProvider) {
|
||||
return authzProvider == AuthorizationProvider.SENTRY ?
|
||||
new SentryAuthorizationFactory(authzConfig_) :
|
||||
new RangerAuthorizationFactory(authzConfig_);
|
||||
return new RangerAuthorizationFactory(authzConfig_);
|
||||
}
|
||||
|
||||
protected interface WithPrincipal {
|
||||
@@ -172,68 +151,6 @@ public abstract class AuthorizationTestBase extends FrontendTestBase {
|
||||
String getName();
|
||||
}
|
||||
|
||||
protected abstract class WithSentryPrincipal implements WithPrincipal {
|
||||
protected final String role_ = "authz_test_role";
|
||||
protected final String sentry_user_ = user_.getName();
|
||||
|
||||
protected void createRole(TPrivilege[]... privileges) throws ImpalaException {
|
||||
Role role = authzCatalog_.addRole(role_);
|
||||
authzCatalog_.addRoleGrantGroup(role_, sentry_user_);
|
||||
for (TPrivilege[] privs: privileges) {
|
||||
for (TPrivilege privilege: privs) {
|
||||
privilege.setPrincipal_id(role.getId());
|
||||
privilege.setPrincipal_type(TPrincipalType.ROLE);
|
||||
authzCatalog_.addRolePrivilege(role_, privilege);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void createUser(TPrivilege[]... privileges) throws ImpalaException {
|
||||
org.apache.impala.catalog.User user = authzCatalog_.addUser(sentry_user_);
|
||||
for (TPrivilege[] privs: privileges) {
|
||||
for (TPrivilege privilege: privs) {
|
||||
privilege.setPrincipal_id(user.getId());
|
||||
privilege.setPrincipal_type(TPrincipalType.USER);
|
||||
authzCatalog_.addUserPrivilege(sentry_user_, privilege);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void dropRole() throws ImpalaException {
|
||||
authzCatalog_.removeRole(role_);
|
||||
}
|
||||
|
||||
protected void dropUser() throws ImpalaException {
|
||||
authzCatalog_.removeUser(sentry_user_);
|
||||
}
|
||||
}
|
||||
|
||||
public class WithSentryUser extends WithSentryPrincipal {
|
||||
@Override
|
||||
public void init(TPrivilege[]... privileges) throws ImpalaException {
|
||||
createUser(privileges);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanUp() throws ImpalaException { dropUser(); }
|
||||
|
||||
@Override
|
||||
public String getName() { return sentry_user_; }
|
||||
}
|
||||
|
||||
public class WithSentryRole extends WithSentryPrincipal {
|
||||
@Override
|
||||
public void init(TPrivilege[]... privileges) throws ImpalaException {
|
||||
createRole(privileges);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanUp() throws ImpalaException { dropRole(); }
|
||||
|
||||
@Override
|
||||
public String getName() { return role_; }
|
||||
}
|
||||
|
||||
protected abstract class WithRanger implements WithPrincipal {
|
||||
private final List<GrantRevokeRequest> grants = new ArrayList<>();
|
||||
private final RangerCatalogdAuthorizationManager authzManager =
|
||||
@@ -375,10 +292,6 @@ public abstract class AuthorizationTestBase extends FrontendTestBase {
|
||||
protected List<WithPrincipal> buildWithPrincipals() {
|
||||
List<WithPrincipal> withPrincipals = new ArrayList<>();
|
||||
switch (authzProvider_) {
|
||||
case SENTRY:
|
||||
withPrincipals.add(new WithSentryRole());
|
||||
withPrincipals.add(new WithSentryUser());
|
||||
break;
|
||||
case RANGER:
|
||||
withPrincipals.add(new WithRangerUser());
|
||||
withPrincipals.add(new WithRangerGroup());
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
// 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.authorization.sentry;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import org.apache.impala.authorization.sentry.ImpalaAction;
|
||||
import org.apache.impala.authorization.sentry.ImpalaActionFactory;
|
||||
import org.apache.sentry.core.common.BitFieldAction;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class ImpalaActionFactoryTest {
|
||||
@Test
|
||||
public void testGetActionsByCode() {
|
||||
ImpalaActionFactory factory = new ImpalaActionFactory();
|
||||
|
||||
List<? extends BitFieldAction> actual = factory.getActionsByCode(
|
||||
ImpalaAction.SELECT.getCode() |
|
||||
ImpalaAction.INSERT.getCode() |
|
||||
ImpalaAction.CREATE.getCode());
|
||||
List<ImpalaAction> expected = Lists.newArrayList(
|
||||
ImpalaAction.SELECT,
|
||||
ImpalaAction.INSERT,
|
||||
ImpalaAction.CREATE);
|
||||
assertBitFieldActions(expected, actual);
|
||||
|
||||
actual = factory.getActionsByCode(
|
||||
ImpalaAction.SELECT.getCode() |
|
||||
ImpalaAction.INSERT.getCode() |
|
||||
ImpalaAction.ALTER.getCode() |
|
||||
ImpalaAction.CREATE.getCode() |
|
||||
ImpalaAction.DROP.getCode() |
|
||||
ImpalaAction.REFRESH.getCode());
|
||||
expected = Lists.newArrayList(
|
||||
ImpalaAction.SELECT,
|
||||
ImpalaAction.INSERT,
|
||||
ImpalaAction.ALTER,
|
||||
ImpalaAction.CREATE,
|
||||
ImpalaAction.DROP,
|
||||
ImpalaAction.REFRESH,
|
||||
ImpalaAction.ALL,
|
||||
ImpalaAction.OWNER);
|
||||
assertBitFieldActions(expected, actual);
|
||||
|
||||
actual = factory.getActionsByCode(ImpalaAction.ALL.getCode());
|
||||
expected = Lists.newArrayList(
|
||||
ImpalaAction.SELECT,
|
||||
ImpalaAction.INSERT,
|
||||
ImpalaAction.ALTER,
|
||||
ImpalaAction.CREATE,
|
||||
ImpalaAction.DROP,
|
||||
ImpalaAction.REFRESH,
|
||||
ImpalaAction.ALL,
|
||||
ImpalaAction.OWNER);
|
||||
assertBitFieldActions(expected, actual);
|
||||
|
||||
try {
|
||||
factory.getActionsByCode(Integer.MAX_VALUE);
|
||||
fail("IllegalArgumentException should be thrown.");
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertEquals(String.format("Action code must between 1 and %d.",
|
||||
ImpalaAction.ALL.getCode()), e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
factory.getActionsByCode(Integer.MIN_VALUE);
|
||||
fail("IllegalArgumentException should be thrown.");
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertEquals(String.format("Action code must between 1 and %d.",
|
||||
ImpalaAction.ALL.getCode()), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertBitFieldActions(List<ImpalaAction> expected,
|
||||
List<? extends BitFieldAction> actual) {
|
||||
assertEquals(expected.size(), actual.size());
|
||||
for (int i = 0; i < actual.size(); i++) {
|
||||
assertEquals(expected.get(i).getValue(), actual.get(i).getValue());
|
||||
assertEquals(expected.get(i).getCode(), actual.get(i).getActionCode());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetActionByName() {
|
||||
ImpalaActionFactory impala = new ImpalaActionFactory();
|
||||
|
||||
for (ImpalaAction action : ImpalaAction.values()) {
|
||||
testGetActionByName(impala, action, action.getValue());
|
||||
}
|
||||
assertNull(impala.getActionByName("foo"));
|
||||
}
|
||||
|
||||
private static void testGetActionByName(ImpalaActionFactory impala,
|
||||
ImpalaAction expected, String name) {
|
||||
assertEquals(toBitFieldAction(expected),
|
||||
impala.getActionByName(name.toUpperCase()));
|
||||
assertEquals(toBitFieldAction(expected),
|
||||
impala.getActionByName(name.toLowerCase()));
|
||||
assertEquals(toBitFieldAction(expected),
|
||||
impala.getActionByName(randomizeCaseSensitivity(name)));
|
||||
}
|
||||
|
||||
private static String randomizeCaseSensitivity(String str) {
|
||||
char[] chars = str.toCharArray();
|
||||
Random random = new Random(System.currentTimeMillis());
|
||||
for (int i = 0; i < chars.length; i++) {
|
||||
chars[i] = (random.nextBoolean()) ? Character.toUpperCase(chars[i]) : chars[i];
|
||||
}
|
||||
return new String(chars);
|
||||
}
|
||||
|
||||
private static BitFieldAction toBitFieldAction(ImpalaAction action) {
|
||||
return new BitFieldAction(action.getValue(), action.getCode());
|
||||
}
|
||||
}
|
||||
@@ -1,614 +0,0 @@
|
||||
// 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.authorization.sentry;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.apache.impala.catalog.CatalogServiceCatalog;
|
||||
import org.apache.impala.catalog.Role;
|
||||
import org.apache.impala.catalog.User;
|
||||
import org.apache.impala.common.ImpalaException;
|
||||
import org.apache.impala.common.Pair;
|
||||
import org.apache.impala.testutil.CatalogServiceTestCatalog;
|
||||
import org.apache.impala.thrift.TPrincipalType;
|
||||
import org.apache.impala.thrift.TPrivilege;
|
||||
import org.apache.impala.thrift.TPrivilegeLevel;
|
||||
import org.apache.impala.thrift.TPrivilegeScope;
|
||||
import org.apache.impala.authorization.sentry.SentryProxy.AuthorizationDelta;
|
||||
import org.apache.sentry.api.service.thrift.TSentryPrivilege;
|
||||
import org.apache.sentry.api.service.thrift.TSentryRole;
|
||||
import org.junit.After;
|
||||
import org.junit.Assume;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class SentryProxyTest {
|
||||
private final String PRINCIPAL_NAME_PREFIX = "sentry_proxy";
|
||||
private static final String SENTRY_SERVER = "server1";
|
||||
private static final org.apache.impala.authorization.User USER =
|
||||
new org.apache.impala.authorization.User(System.getProperty("user.name"));
|
||||
private final SentryPolicyService sentryService_;
|
||||
private final SentryAuthorizationConfig authzConfig_;
|
||||
|
||||
public SentryProxyTest() {
|
||||
String envDisableSentry = System.getenv("DISABLE_SENTRY");
|
||||
if (envDisableSentry != null) Assume.assumeTrue(envDisableSentry.equals("false"));
|
||||
authzConfig_ = SentryAuthorizationConfig.createHadoopGroupAuthConfig(
|
||||
SENTRY_SERVER,
|
||||
System.getenv("IMPALA_HOME") + "/fe/src/test/resources/sentry-site.xml");
|
||||
sentryService_ = new SentryPolicyService(authzConfig_.getSentryConfig());
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws ImpalaException {
|
||||
cleanUpRoles();
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanUp() throws ImpalaException {
|
||||
cleanUpRoles();
|
||||
}
|
||||
|
||||
private void cleanUpRoles() throws ImpalaException {
|
||||
for (TSentryRole role: sentryService_.listAllRoles(USER)) {
|
||||
if (role.getRoleName().startsWith(PRINCIPAL_NAME_PREFIX)) {
|
||||
sentryService_.dropRole(USER, role.getRoleName(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyToNonEmptyCatalog() {
|
||||
withAllPrincipalTypes(ctx -> {
|
||||
String[] principalNames = new String[3];
|
||||
for (int i = 0; i < principalNames.length; i++) {
|
||||
principalNames[i] = String.format("%s_add_%d", PRINCIPAL_NAME_PREFIX, i);
|
||||
}
|
||||
|
||||
for (String principalName: principalNames) {
|
||||
addSentryPrincipalPrivileges(ctx.type_, ctx.sentryService_, principalName,
|
||||
"functional");
|
||||
}
|
||||
|
||||
CatalogState noReset = refreshSentryAuthorization(ctx.catalog_, ctx.sentryService_,
|
||||
false);
|
||||
|
||||
for (String principalName: principalNames) {
|
||||
checkCatalogPrincipalPrivileges(ctx.type_, ctx.catalog_, principalName,
|
||||
"server=server1->db=functional->grantoption=false");
|
||||
}
|
||||
|
||||
CatalogState reset = refreshSentryAuthorization(ctx.catalog_, ctx.sentryService_,
|
||||
true);
|
||||
checkCatalogState(noReset, reset);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonEmptyToEmptyCatalog() {
|
||||
withAllPrincipalTypes(ctx -> {
|
||||
String[] principalNames = new String[3];
|
||||
for (int i = 0; i < principalNames.length; i++) {
|
||||
principalNames[i] = String.format("%s_add_%d", PRINCIPAL_NAME_PREFIX, i);
|
||||
}
|
||||
|
||||
for (String principalName: principalNames) {
|
||||
addCatalogPrincipalPrivileges(ctx.type_, ctx.catalog_, principalName,
|
||||
"functional");
|
||||
}
|
||||
|
||||
CatalogState noReset = refreshSentryAuthorization(ctx.catalog_, ctx.sentryService_,
|
||||
false);
|
||||
|
||||
for (String principalName: principalNames) {
|
||||
checkNoCatalogPrincipalPrivileges(ctx.type_, ctx.catalog_, principalName);
|
||||
}
|
||||
|
||||
CatalogState reset = refreshSentryAuthorization(ctx.catalog_, ctx.sentryService_,
|
||||
true);
|
||||
checkCatalogState(noReset, reset);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddCatalog() {
|
||||
withAllPrincipalTypes(ctx -> {
|
||||
String principalName = String.format("%s_add", PRINCIPAL_NAME_PREFIX);
|
||||
addCatalogPrincipalPrivileges(ctx.type_, ctx.catalog_, principalName, "functional");
|
||||
addSentryPrincipalPrivileges(ctx.type_, ctx.sentryService_, principalName,
|
||||
"functional");
|
||||
|
||||
CatalogState noReset = refreshSentryAuthorization(ctx.catalog_, ctx.sentryService_,
|
||||
false);
|
||||
|
||||
checkCatalogPrincipalPrivileges(ctx.type_, ctx.catalog_, principalName,
|
||||
"server=server1->db=functional->grantoption=false");
|
||||
|
||||
CatalogState reset = refreshSentryAuthorization(ctx.catalog_, ctx.sentryService_,
|
||||
true);
|
||||
checkCatalogState(noReset, reset);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateCatalog() {
|
||||
withAllPrincipalTypes(ctx -> {
|
||||
String principalName = String.format("%s_update", PRINCIPAL_NAME_PREFIX);
|
||||
addCatalogPrincipalPrivileges(ctx.type_, ctx.catalog_, principalName, "functional");
|
||||
addSentryPrincipalPrivileges(ctx.type_, ctx.sentryService_, principalName,
|
||||
"functional", "functional_kudu");
|
||||
|
||||
CatalogState noReset = refreshSentryAuthorization(ctx.catalog_, ctx.sentryService_,
|
||||
false);
|
||||
|
||||
checkCatalogPrincipalPrivileges(ctx.type_, ctx.catalog_, principalName,
|
||||
"server=server1->db=functional->grantoption=false",
|
||||
"server=server1->db=functional_kudu->grantoption=false");
|
||||
|
||||
CatalogState reset = refreshSentryAuthorization(ctx.catalog_, ctx.sentryService_,
|
||||
true);
|
||||
checkCatalogState(noReset, reset);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDropCatalog() {
|
||||
withAllPrincipalTypes(ctx -> {
|
||||
String principalName = String.format("%s_remove", PRINCIPAL_NAME_PREFIX);
|
||||
addCatalogPrincipalPrivileges(ctx.type_, ctx.catalog_, principalName, "functional");
|
||||
dropSentryPrincipalPrivileges(ctx.type_, ctx.sentryService_, principalName);
|
||||
|
||||
CatalogState noReset = refreshSentryAuthorization(ctx.catalog_, ctx.sentryService_,
|
||||
false);
|
||||
|
||||
checkNoCatalogPrincipalPrivileges(ctx.type_, ctx.catalog_, principalName);
|
||||
|
||||
CatalogState reset = refreshSentryAuthorization(ctx.catalog_, ctx.sentryService_,
|
||||
true);
|
||||
checkCatalogState(noReset, reset);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMergeCatalog() {
|
||||
withAllPrincipalTypes(ctx -> {
|
||||
String addPrincipal = String.format("%s_add", PRINCIPAL_NAME_PREFIX);
|
||||
String updatePrincipal = String.format("%s_update", PRINCIPAL_NAME_PREFIX);
|
||||
String removePrincipal = String.format("%s_remove", PRINCIPAL_NAME_PREFIX);
|
||||
|
||||
addCatalogPrincipalPrivileges(ctx.type_, ctx.catalog_, updatePrincipal,
|
||||
"functional");
|
||||
addCatalogPrincipalPrivileges(ctx.type_, ctx.catalog_, removePrincipal,
|
||||
"functional");
|
||||
|
||||
addSentryPrincipalPrivileges(ctx.type_, ctx.sentryService_, addPrincipal,
|
||||
"functional");
|
||||
addSentryPrincipalPrivileges(ctx.type_, ctx.sentryService_, updatePrincipal,
|
||||
"functional", "functional_kudu");
|
||||
dropSentryPrincipalPrivileges(ctx.type_, ctx.sentryService_, removePrincipal);
|
||||
|
||||
CatalogState noReset = refreshSentryAuthorization(ctx.catalog_, ctx.sentryService_,
|
||||
false);
|
||||
|
||||
checkCatalogPrincipalPrivileges(ctx.type_, ctx.catalog_, addPrincipal,
|
||||
"server=server1->db=functional->grantoption=false");
|
||||
checkCatalogPrincipalPrivileges(ctx.type_, ctx.catalog_, updatePrincipal,
|
||||
"server=server1->db=functional->grantoption=false",
|
||||
"server=server1->db=functional_kudu->grantoption=false");
|
||||
checkNoCatalogPrincipalPrivileges(ctx.type_, ctx.catalog_, removePrincipal);
|
||||
|
||||
CatalogState reset = refreshSentryAuthorization(ctx.catalog_, ctx.sentryService_,
|
||||
true);
|
||||
checkCatalogState(noReset, reset);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateSentryNotInCatalog() {
|
||||
withAllPrincipalTypes(ctx -> {
|
||||
String principalName = String.format("%s_update", PRINCIPAL_NAME_PREFIX);
|
||||
addSentryPrincipalPrivileges(ctx.type_, ctx.sentryService_, principalName,
|
||||
"functional", "functional_kudu");
|
||||
|
||||
CatalogState noReset = refreshSentryAuthorization(ctx.catalog_, ctx.sentryService_,
|
||||
false);
|
||||
|
||||
checkCatalogPrincipalPrivileges(ctx.type_, ctx.catalog_, principalName,
|
||||
"server=server1->db=functional->grantoption=false",
|
||||
"server=server1->db=functional_kudu->grantoption=false");
|
||||
|
||||
CatalogState reset = refreshSentryAuthorization(ctx.catalog_, ctx.sentryService_,
|
||||
true);
|
||||
checkCatalogState(noReset, reset);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDropSentryNotInCatalog() {
|
||||
withAllPrincipalTypes(ctx -> {
|
||||
String principalName = String.format("%s_remove", PRINCIPAL_NAME_PREFIX);
|
||||
dropSentryPrincipalPrivileges(ctx.type_, ctx.sentryService_, principalName);
|
||||
|
||||
CatalogState noReset = refreshSentryAuthorization(ctx.catalog_, ctx.sentryService_,
|
||||
false);
|
||||
|
||||
checkNoCatalogPrincipalPrivileges(ctx.type_, ctx.catalog_, principalName);
|
||||
|
||||
CatalogState reset = refreshSentryAuthorization(ctx.catalog_, ctx.sentryService_,
|
||||
true);
|
||||
checkCatalogState(noReset, reset);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoleNameCaseInsensitive() throws ImpalaException {
|
||||
String lowerCaseRoleName = String.format("%s_case_insensitive_role",
|
||||
PRINCIPAL_NAME_PREFIX);
|
||||
String upperCaseRoleName = lowerCaseRoleName.toUpperCase();
|
||||
String mixedCaseRoleName = lowerCaseRoleName.substring(0, 1).toUpperCase() +
|
||||
lowerCaseRoleName.substring(1);
|
||||
|
||||
try (CatalogServiceCatalog catalog = CatalogServiceTestCatalog.createWithAuth(
|
||||
new SentryAuthorizationFactory(authzConfig_))) {
|
||||
addSentryRolePrivileges(sentryService_, lowerCaseRoleName, "functional");
|
||||
CatalogState noReset = refreshSentryAuthorization(catalog, sentryService_, false);
|
||||
|
||||
// Impala stores the role name in case insensitive way.
|
||||
for (String roleName : new String[]{
|
||||
lowerCaseRoleName, upperCaseRoleName, mixedCaseRoleName}) {
|
||||
Role role = catalog.getAuthPolicy().getRole(roleName);
|
||||
assertEquals(lowerCaseRoleName, role.getName());
|
||||
assertEquals(1, role.getPrivileges().size());
|
||||
assertNotNull(role.getPrivilege(
|
||||
"server=server1->db=functional->grantoption=false"));
|
||||
}
|
||||
|
||||
try {
|
||||
sentryService_.createRole(USER, upperCaseRoleName, false);
|
||||
fail("Exception should be thrown when creating a duplicate role name.");
|
||||
} catch (Exception e) {
|
||||
assertTrue(e.getMessage().startsWith("Error creating role"));
|
||||
}
|
||||
|
||||
// No new role should be added.
|
||||
for (String roleName : new String[]{
|
||||
lowerCaseRoleName, upperCaseRoleName, mixedCaseRoleName}) {
|
||||
Role role = catalog.getAuthPolicy().getRole(roleName);
|
||||
assertEquals(lowerCaseRoleName, role.getName());
|
||||
assertEquals(1, role.getPrivileges().size());
|
||||
assertNotNull(role.getPrivilege(
|
||||
"server=server1->db=functional->grantoption=false"));
|
||||
}
|
||||
|
||||
CatalogState reset = refreshSentryAuthorization(catalog, sentryService_, true);
|
||||
checkCatalogState(noReset, reset);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUserNameCaseSensitive() throws ImpalaException {
|
||||
String lowerCaseUserName = String.format("%s_case_sensitive_user",
|
||||
PRINCIPAL_NAME_PREFIX);
|
||||
String upperCaseUserName = lowerCaseUserName.toUpperCase();
|
||||
String mixedCaseUserName = lowerCaseUserName.substring(0, 1).toUpperCase() +
|
||||
lowerCaseUserName.substring(1);
|
||||
|
||||
try (CatalogServiceCatalog catalog = CatalogServiceTestCatalog.createWithAuth(
|
||||
new SentryAuthorizationFactory(authzConfig_))) {
|
||||
SentryPolicyServiceStub sentryService = createSentryPolicyServiceStub(
|
||||
authzConfig_.getSentryConfig());
|
||||
// We grant different privileges to different users to ensure each user is
|
||||
// granted with a distinct privilege.
|
||||
addSentryUserPrivileges(sentryService, lowerCaseUserName, "functional");
|
||||
addSentryUserPrivileges(sentryService, upperCaseUserName, "functional_kudu");
|
||||
addSentryUserPrivileges(sentryService, mixedCaseUserName, "functional_parquet");
|
||||
|
||||
CatalogState noReset = refreshSentryAuthorization(catalog, sentryService, false);
|
||||
|
||||
// Impala stores the user name in case sensitive way.
|
||||
for (Pair<String, String> userPrivilege : new Pair[]{
|
||||
new Pair(lowerCaseUserName, "server=server1->db=functional->grantoption=false"),
|
||||
new Pair(upperCaseUserName, "server=server1->db=functional_kudu" +
|
||||
"->grantoption=false"),
|
||||
new Pair(mixedCaseUserName, "server=server1->db=functional_parquet" +
|
||||
"->grantoption=false")}) {
|
||||
User user = catalog.getAuthPolicy().getUser(userPrivilege.first);
|
||||
assertEquals(userPrivilege.first, user.getName());
|
||||
assertEquals(1, user.getPrivileges().size());
|
||||
assertNotNull(user.getPrivilege(userPrivilege.second));
|
||||
}
|
||||
|
||||
CatalogState reset = refreshSentryAuthorization(catalog, sentryService, true);
|
||||
checkCatalogState(noReset, reset);
|
||||
}
|
||||
}
|
||||
|
||||
private static void addCatalogPrincipalPrivileges(TPrincipalType type,
|
||||
CatalogServiceCatalog catalog, String principalName, String... dbNames) {
|
||||
try {
|
||||
switch (type) {
|
||||
case ROLE:
|
||||
addCatalogRolePrivileges(catalog, principalName, dbNames);
|
||||
break;
|
||||
case USER:
|
||||
addCatalogUserPrivileges(catalog, principalName, dbNames);
|
||||
break;
|
||||
}
|
||||
} catch (ImpalaException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void addCatalogRolePrivileges(CatalogServiceCatalog catalog,
|
||||
String roleName, String... dbNames) throws ImpalaException {
|
||||
Role role = catalog.addRole(roleName, Sets.newHashSet(USER.getShortName()));
|
||||
for (String dbName: dbNames) {
|
||||
catalog.addRolePrivilege(roleName, createRolePrivilege(role.getId(), dbName));
|
||||
}
|
||||
}
|
||||
|
||||
private static void addCatalogUserPrivileges(CatalogServiceCatalog catalog,
|
||||
String userName, String... dbNames) throws ImpalaException {
|
||||
User user = catalog.addUser(userName);
|
||||
for (String dbName: dbNames) {
|
||||
catalog.addUserPrivilege(userName, createUserPrivilege(user.getId(), dbName));
|
||||
}
|
||||
}
|
||||
|
||||
private static void addSentryPrincipalPrivileges(TPrincipalType type,
|
||||
SentryPolicyService sentryService, String principalName, String... dbNames) {
|
||||
try {
|
||||
switch (type) {
|
||||
case ROLE:
|
||||
addSentryRolePrivileges(sentryService, principalName, dbNames);
|
||||
break;
|
||||
case USER:
|
||||
addSentryUserPrivileges(sentryService, principalName, dbNames);
|
||||
break;
|
||||
}
|
||||
} catch (ImpalaException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void addSentryRolePrivileges(SentryPolicyService sentryService,
|
||||
String roleName, String... dbNames) throws ImpalaException {
|
||||
sentryService.createRole(USER, roleName, false);
|
||||
for (String dbName: dbNames) {
|
||||
sentryService.grantRolePrivilege(USER, roleName, createRolePrivilege(dbName));
|
||||
}
|
||||
}
|
||||
|
||||
private static void addSentryUserPrivileges(SentryPolicyService sentryService,
|
||||
String userName, String... dbNames) throws ImpalaException {
|
||||
Preconditions.checkArgument(sentryService instanceof SentryPolicyServiceStub);
|
||||
SentryPolicyServiceStub stub = (SentryPolicyServiceStub) sentryService;
|
||||
stub.createUser(userName);
|
||||
for (String dbName: dbNames) {
|
||||
stub.grantUserPrivilege(userName, createUserPrivilege(dbName));
|
||||
}
|
||||
}
|
||||
|
||||
private static void dropSentryPrincipalPrivileges(TPrincipalType type,
|
||||
SentryPolicyService sentryService, String principalName) {
|
||||
try {
|
||||
switch (type) {
|
||||
case ROLE:
|
||||
dropSentryRolePrivileges(sentryService, principalName);
|
||||
break;
|
||||
case USER:
|
||||
dropSentryUserPrivileges(sentryService, principalName);
|
||||
break;
|
||||
}
|
||||
} catch (ImpalaException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void dropSentryRolePrivileges(SentryPolicyService sentryService,
|
||||
String roleName) throws ImpalaException {
|
||||
sentryService.dropRole(USER, roleName, true);
|
||||
}
|
||||
|
||||
private static void dropSentryUserPrivileges(SentryPolicyService sentryService,
|
||||
String userName) throws ImpalaException {
|
||||
Preconditions.checkArgument(sentryService instanceof SentryPolicyServiceStub);
|
||||
SentryPolicyServiceStub stub = (SentryPolicyServiceStub) sentryService;
|
||||
stub.dropRole(USER, userName, true);
|
||||
}
|
||||
|
||||
private static void checkCatalogPrincipalPrivileges(TPrincipalType type,
|
||||
CatalogServiceCatalog catalog, String principalName, String... privileges) {
|
||||
switch (type) {
|
||||
case ROLE:
|
||||
checkCatalogRolePrivileges(catalog, principalName, privileges);
|
||||
break;
|
||||
case USER:
|
||||
checkCatalogUserPrivileges(catalog, principalName, privileges);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkCatalogRolePrivileges(CatalogServiceCatalog catalog,
|
||||
String roleName, String... privileges) {
|
||||
Role role = catalog.getAuthPolicy().getRole(roleName);
|
||||
assertEquals(role.getName(), roleName);
|
||||
assertEquals(privileges.length, role.getPrivileges().size());
|
||||
for (String privilege: privileges) {
|
||||
assertNotNull(role.getPrivilege(privilege));
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkCatalogUserPrivileges(CatalogServiceCatalog catalog,
|
||||
String userName, String... privileges) {
|
||||
User user = catalog.getAuthPolicy().getUser(userName);
|
||||
assertEquals(user.getName(), userName);
|
||||
assertEquals(privileges.length, user.getPrivileges().size());
|
||||
for (String privilege: privileges) {
|
||||
assertNotNull(user.getPrivilege(privilege));
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkNoCatalogPrincipalPrivileges(TPrincipalType type,
|
||||
CatalogServiceCatalog catalog, String principalName) {
|
||||
switch (type) {
|
||||
case ROLE:
|
||||
assertNull(catalog.getAuthPolicy().getRole(principalName));
|
||||
break;
|
||||
case USER:
|
||||
assertNull(catalog.getAuthPolicy().getUser(principalName));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static long getAuthCatalogSize(CatalogServiceCatalog catalog) {
|
||||
return catalog.getAuthPolicy().getAllRoles().size() +
|
||||
catalog.getAuthPolicy().getAllRoles().stream()
|
||||
.mapToInt(p -> p.getPrivileges().size()).sum() +
|
||||
catalog.getAuthPolicy().getAllUsers().size() +
|
||||
catalog.getAuthPolicy().getAllUsers().stream()
|
||||
.mapToInt(p -> p.getPrivileges().size()).sum();
|
||||
}
|
||||
|
||||
private static void checkCatalogState(CatalogState noReset, CatalogState reset) {
|
||||
// Resetting the version means the new version will be current version + the number
|
||||
// of all authorization catalog objects.
|
||||
assertEquals(noReset.catalogVersion_ + noReset.catalogSize_, reset.catalogVersion_);
|
||||
// Catalog size should be the same regardless whether or not the versions are reset.
|
||||
assertEquals(noReset.catalogSize_, reset.catalogSize_);
|
||||
}
|
||||
|
||||
private static class CatalogState {
|
||||
private final long catalogVersion_;
|
||||
private final long catalogSize_;
|
||||
|
||||
public CatalogState(long catalogVersion, long catalogSize) {
|
||||
catalogVersion_ = catalogVersion;
|
||||
catalogSize_ = catalogSize;
|
||||
}
|
||||
}
|
||||
|
||||
private static CatalogState refreshSentryAuthorization(CatalogServiceCatalog catalog,
|
||||
SentryPolicyService sentryService, boolean resetVersions) {
|
||||
SentryProxy.refreshSentryAuthorization(catalog, sentryService, USER, resetVersions,
|
||||
false, new AuthorizationDelta());
|
||||
return new CatalogState(catalog.getCatalogVersion(), getAuthCatalogSize(catalog));
|
||||
}
|
||||
|
||||
private static class SentryPolicyServiceStub extends SentryPolicyService {
|
||||
private final Map<String, Set<TSentryPrivilege>> allUserPrivileges_ =
|
||||
Maps.newHashMap();
|
||||
|
||||
public SentryPolicyServiceStub(SentryConfig sentryConfig) {
|
||||
super(sentryConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Set<TSentryPrivilege>> listAllUsersPrivileges(
|
||||
org.apache.impala.authorization.User requestingUser) throws ImpalaException {
|
||||
return allUserPrivileges_;
|
||||
}
|
||||
|
||||
public void createUser(String userName) {
|
||||
allUserPrivileges_.put(userName, Sets.newHashSet());
|
||||
}
|
||||
|
||||
public void grantUserPrivilege(String userName, TPrivilege privilege) {
|
||||
allUserPrivileges_.get(userName).add(createSentryPrivilege(privilege.getDb_name()));
|
||||
}
|
||||
|
||||
public void dropUser(String userName) {
|
||||
allUserPrivileges_.remove(userName);
|
||||
}
|
||||
}
|
||||
|
||||
private static SentryPolicyServiceStub createSentryPolicyServiceStub(
|
||||
SentryConfig sentryConfig) {
|
||||
return new SentryPolicyServiceStub(sentryConfig);
|
||||
}
|
||||
|
||||
private static TPrivilege createRolePrivilege(int roleId, String dbName) {
|
||||
return createPrincipalPrivilege(TPrincipalType.ROLE, roleId, dbName);
|
||||
}
|
||||
|
||||
private static TPrivilege createRolePrivilege(String dbName) {
|
||||
return createRolePrivilege(0, dbName);
|
||||
}
|
||||
|
||||
private static TPrivilege createUserPrivilege(int userId, String dbName) {
|
||||
return createPrincipalPrivilege(TPrincipalType.USER, userId, dbName);
|
||||
}
|
||||
|
||||
private static TPrivilege createUserPrivilege(String dbName) {
|
||||
return createPrincipalPrivilege(TPrincipalType.USER, 0, dbName);
|
||||
}
|
||||
|
||||
private static TPrivilege createPrincipalPrivilege(TPrincipalType principalType,
|
||||
int principalId, String dbName) {
|
||||
TPrivilege privilege = new TPrivilege(TPrivilegeLevel.ALL, TPrivilegeScope.DATABASE,
|
||||
false);
|
||||
privilege.setPrincipal_id(principalId);
|
||||
privilege.setPrincipal_type(principalType);
|
||||
privilege.setServer_name("server1");
|
||||
privilege.setDb_name(dbName);
|
||||
return privilege;
|
||||
}
|
||||
|
||||
private static TSentryPrivilege createSentryPrivilege(String dbName) {
|
||||
TSentryPrivilege privilege = new TSentryPrivilege("DATABASE", "server1", "*");
|
||||
privilege.setDbName(dbName);
|
||||
return privilege;
|
||||
}
|
||||
|
||||
private static class SentryProxyTestContext {
|
||||
private final TPrincipalType type_;
|
||||
private final CatalogServiceCatalog catalog_;
|
||||
private final SentryPolicyService sentryService_;
|
||||
|
||||
public SentryProxyTestContext(TPrincipalType type, CatalogServiceCatalog catalog,
|
||||
SentryPolicyService sentryService) {
|
||||
type_ = type;
|
||||
catalog_ = catalog;
|
||||
sentryService_ = sentryService;
|
||||
}
|
||||
}
|
||||
|
||||
private void withAllPrincipalTypes(Consumer<SentryProxyTestContext> consumer) {
|
||||
for (TPrincipalType type: TPrincipalType.values()) {
|
||||
try (CatalogServiceCatalog catalog = CatalogServiceTestCatalog.createWithAuth(
|
||||
new SentryAuthorizationFactory(authzConfig_))) {
|
||||
SentryPolicyService sentryService = sentryService_;
|
||||
if (type == TPrincipalType.USER) {
|
||||
sentryService = createSentryPolicyServiceStub(authzConfig_.getSentryConfig());
|
||||
}
|
||||
consumer.accept(new SentryProxyTestContext(type, catalog, sentryService));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -953,7 +953,7 @@ public class CatalogTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSentryCatalog() throws CatalogException {
|
||||
public void testAuthorizationCatalog() throws CatalogException {
|
||||
AuthorizationPolicy authPolicy = catalog_.getAuthPolicy();
|
||||
|
||||
User user = catalog_.addUser("user1");
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
// 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.testutil;
|
||||
|
||||
import org.apache.commons.cli.BasicParser;
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.commons.cli.OptionBuilder;
|
||||
import org.apache.commons.cli.Options;
|
||||
import org.apache.impala.authorization.sentry.SentryConfig;
|
||||
import org.apache.impala.authorization.User;
|
||||
import org.apache.impala.authorization.sentry.SentryPolicyService;
|
||||
import org.apache.log4j.Level;
|
||||
import org.apache.sentry.core.common.transport.SentryTransportFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Simple class that issues a read-only RPC to the Sentry Service to check if it
|
||||
* is online. Attempts to ping the Sentry Service a user specified number of times
|
||||
* and returns a non-zero error code if the RPC never succeeds, otherwise returns
|
||||
* 0.
|
||||
*/
|
||||
public class SentryServicePinger {
|
||||
private final static Logger LOG =
|
||||
LoggerFactory.getLogger(SentryServicePinger.class);
|
||||
|
||||
// Suppress warnings from OptionBuilder.
|
||||
@SuppressWarnings("static-access")
|
||||
public static void main(String[] args) throws Exception {
|
||||
// Programmatically disable Sentry Thrift logging since Sentry error logging can be
|
||||
// pretty noisy and verbose.
|
||||
org.apache.log4j.Logger logger4j = org.apache.log4j.Logger.getLogger(
|
||||
SentryTransportFactory.class.getPackage().getName());
|
||||
logger4j.setLevel(Level.OFF);
|
||||
|
||||
// Parse command line options to get config file path.
|
||||
Options options = new Options();
|
||||
options.addOption(OptionBuilder.withLongOpt("config_file")
|
||||
.withDescription("Absolute path to a sentry-site.xml config file (string)")
|
||||
.hasArg()
|
||||
.withArgName("CONFIG_FILE")
|
||||
.isRequired()
|
||||
.create('c'));
|
||||
options.addOption(OptionBuilder.withLongOpt("num_pings")
|
||||
.withDescription("Max number of pings to try before failing (int)")
|
||||
.hasArg()
|
||||
.isRequired()
|
||||
.withArgName("NUM_PINGS")
|
||||
.create('n'));
|
||||
options.addOption(OptionBuilder.withLongOpt("sleep_secs")
|
||||
.withDescription("Time (s) to sleep between pings (int)")
|
||||
.hasArg()
|
||||
.withArgName("SLEEP_SECS")
|
||||
.create('s'));
|
||||
BasicParser optionParser = new BasicParser();
|
||||
CommandLine cmdArgs = optionParser.parse(options, args);
|
||||
|
||||
SentryConfig sentryConfig = new SentryConfig(cmdArgs.getOptionValue("config_file"));
|
||||
int numPings = Integer.parseInt(cmdArgs.getOptionValue("num_pings"));
|
||||
int maxPings = numPings;
|
||||
int sleepSecs = Integer.parseInt(cmdArgs.getOptionValue("sleep_secs"));
|
||||
|
||||
sentryConfig.loadConfig();
|
||||
Exception exception = null;
|
||||
while (numPings > 0) {
|
||||
SentryPolicyService policyService = new SentryPolicyService(sentryConfig);
|
||||
try {
|
||||
policyService.listAllRoles(new User(System.getProperty("user.name")));
|
||||
LOG.info("Sentry Service ping succeeded.");
|
||||
System.exit(0);
|
||||
} catch (Exception e) {
|
||||
exception = e;
|
||||
LOG.error(String.format("Error issuing RPC to Sentry Service (attempt %d/%d)",
|
||||
maxPings - numPings + 1, maxPings));
|
||||
Thread.sleep(sleepSecs * 1000);
|
||||
}
|
||||
--numPings;
|
||||
}
|
||||
if (exception != null) {
|
||||
LOG.error("Error starting Sentry Service: ", exception);
|
||||
}
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
// 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.testutil;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.sentry.core.common.exception.SentryGroupNotFoundException;
|
||||
import org.apache.sentry.provider.common.GroupMappingService;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* This class is used for testing complex privileges where we don't create
|
||||
* users and groups on the system.
|
||||
*/
|
||||
public class TestSentryGroupMapper implements GroupMappingService {
|
||||
private final Map<String, Set<String>> groupsMap_ = Maps.newHashMap();
|
||||
|
||||
public static final String SERVER_ADMIN = "server_admin";
|
||||
public static final String TEST_USER = "test_user";
|
||||
public static final String AUTH_TO_LOCAL = "auth_to_local_group";
|
||||
|
||||
// Needed for sentry service to lookup groups.
|
||||
public TestSentryGroupMapper(Configuration conf, String resource) {
|
||||
this();
|
||||
}
|
||||
|
||||
public TestSentryGroupMapper() {
|
||||
// Need to make sure we can resolve the dev user.
|
||||
String devUser = System.getProperty("user.name");
|
||||
groupsMap_.put(devUser, Sets.newHashSet(devUser));
|
||||
|
||||
// User to groups for show_grant_user tests.
|
||||
groupsMap_.put("user_1group", Sets.newHashSet("group_1"));
|
||||
groupsMap_.put("user_2group", Sets.newHashSet("group_2a", "group_2b"));
|
||||
groupsMap_.put("user1_shared", Sets.newHashSet("group_3"));
|
||||
groupsMap_.put("user2_shared", Sets.newHashSet("group_3"));
|
||||
groupsMap_.put("user1_shared2", Sets.newHashSet("group_4a","group_4b"));
|
||||
groupsMap_.put("user2_shared2", Sets.newHashSet("group_4a"));
|
||||
|
||||
// User to groups for test_owner_privilege tests.
|
||||
groupsMap_.put("oo_user1", Sets.newHashSet("oo_group1"));
|
||||
groupsMap_.put("oo_user2", Sets.newHashSet("oo_group2"));
|
||||
groupsMap_.put("oo_user3", Sets.newHashSet("oo_group3"));
|
||||
|
||||
groupsMap_.put("foobar", Sets.newHashSet("foobar"));
|
||||
groupsMap_.put("FOOBAR", Sets.newHashSet("FOOBAR"));
|
||||
|
||||
// User to groups for AuthorizationTest tests.
|
||||
groupsMap_.put("auth_to_local_user", Sets.newHashSet(AUTH_TO_LOCAL));
|
||||
groupsMap_.put("test_user", Sets.newHashSet(TEST_USER));
|
||||
groupsMap_.put("admin_user", Sets.newHashSet(SERVER_ADMIN));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getGroups(String s) throws SentryGroupNotFoundException {
|
||||
Set<String> groups = groupsMap_.get(s);
|
||||
if (groups == null) {
|
||||
groups = Sets.newHashSet();
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
}
|
||||
@@ -18,8 +18,8 @@
|
||||
package org.apache.impala.util;
|
||||
|
||||
import org.apache.impala.authorization.AuthorizationFactory;
|
||||
import org.apache.impala.authorization.NoopAuthorizationFactory;
|
||||
import org.apache.impala.authorization.ranger.RangerAuthorizationFactory;
|
||||
import org.apache.impala.authorization.sentry.SentryAuthorizationFactory;
|
||||
import org.apache.impala.service.BackendConfig;
|
||||
import org.apache.impala.thrift.TBackendGflags;
|
||||
import org.junit.AfterClass;
|
||||
@@ -67,28 +67,15 @@ public class AuthorizationUtilTest {
|
||||
@Test
|
||||
public void testAuthorizationProviderFlag()
|
||||
throws Exception {
|
||||
// Test policy factory selection based on provider
|
||||
assertEquals(SentryAuthorizationFactory.class.getCanonicalName(),
|
||||
AuthorizationUtil.authzFactoryClassNameFrom(authCfg("", "sentry")));
|
||||
|
||||
// Use authorization provider if authorization factory class not set
|
||||
assertEquals(RangerAuthorizationFactory.class.getCanonicalName(),
|
||||
AuthorizationUtil.authzFactoryClassNameFrom(authCfg("", "ranger")));
|
||||
assertEquals(NoopAuthorizationFactory.class.getCanonicalName(),
|
||||
AuthorizationUtil.authzFactoryClassNameFrom(authCfg("", "")));
|
||||
|
||||
// Policy selection factory takes precedence over provider name
|
||||
assertEquals(SentryAuthorizationFactory.class.getCanonicalName(),
|
||||
AuthorizationUtil.authzFactoryClassNameFrom(
|
||||
authCfg(SentryAuthorizationFactory.class, "ranger")));
|
||||
|
||||
// Authorization factory class overrides authorization provider
|
||||
assertEquals(RangerAuthorizationFactory.class.getCanonicalName(),
|
||||
AuthorizationUtil.authzFactoryClassNameFrom(
|
||||
authCfg(RangerAuthorizationFactory.class, "sentry")));
|
||||
|
||||
assertEquals(SentryAuthorizationFactory.class.getCanonicalName(),
|
||||
AuthorizationUtil.authzFactoryClassNameFrom(
|
||||
authCfg(SentryAuthorizationFactory.class, "sentry")));
|
||||
|
||||
assertEquals(RangerAuthorizationFactory.class.getCanonicalName(),
|
||||
AuthorizationUtil.authzFactoryClassNameFrom(
|
||||
authCfg(RangerAuthorizationFactory.class, "ranger")));
|
||||
authCfg(RangerAuthorizationFactory.class, "")));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,6 @@ CONFIG.update({
|
||||
|
||||
# Location of Hive per-query log files of the form: hive_job_log_<hive_query_id>.txt
|
||||
'hive.querylog.location': '${IMPALA_CLUSTER_LOGS_DIR}/hive',
|
||||
'hive.sentry.conf.url': 'file:///${IMPALA_HOME}/fe/src/test/resources/sentry-site.xml',
|
||||
|
||||
# Change back to NOSASL when HIVE-4232 is fixed.
|
||||
# With NONE, Hive uses the plain SASL transport.
|
||||
@@ -144,10 +143,6 @@ if hive_major_version >= 3:
|
||||
'hive.compactor.wait.timeout': '2000'
|
||||
})
|
||||
else:
|
||||
if os.environ.get('DISABLE_SENTRY') == "false":
|
||||
CONFIG.update({
|
||||
'hive.metastore.event.listeners' : 'org.apache.sentry.binding.metastore.SentrySyncHMSNotificationsPostEventListener'
|
||||
})
|
||||
CONFIG.update({
|
||||
# HMS-2 based environments have a different set of expected configurations for event processor
|
||||
'hive.metastore.alter.notifications.basic': 'false',
|
||||
@@ -159,11 +154,10 @@ else:
|
||||
})
|
||||
|
||||
# Notifications-related configuration.
|
||||
# These are for enabling notification between Hive and Sentry as well as
|
||||
# metastore event processing in Impala (see IMPALA-7954)
|
||||
# These are for enabling notification for Hive as well as metastore event processing
|
||||
# in Impala (see IMPALA-7954)
|
||||
CONFIG.update({
|
||||
'hive.metastore.transactional.event.listeners': 'org.apache.hive.hcatalog.listener.DbNotificationListener,org.apache.kudu.hive.metastore.KuduMetastorePlugin',
|
||||
'hcatalog.message.factory.impl.json': 'org.apache.sentry.binding.metastore.messaging.json.SentryJSONMessageFactory',
|
||||
'hive.metastore.dml.events': 'true',
|
||||
})
|
||||
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
import os
|
||||
|
||||
_SETTINGS_BY_VARIANT = dict({
|
||||
'oo': 'all_with_grant',
|
||||
'oo_nogrant': 'all',
|
||||
'no_oo': 'none'})
|
||||
|
||||
variant = os.environ.get('SENTRY_VARIANT')
|
||||
|
||||
CONFIG = {
|
||||
'hive.sentry.server': 'server1',
|
||||
|
||||
# Use a space to mean allow all privileges. See SENTRY-2424
|
||||
'sentry.db.explicit.grants.permitted': ' ',
|
||||
|
||||
'sentry.service.admin.group': '${USER}',
|
||||
# TODO(IMPALA-5686): remove one of the following two properties when Sentry
|
||||
# standardizes on one.
|
||||
'sentry.service.client.server.rpc-address': '${INTERNAL_LISTEN_HOST}',
|
||||
'sentry.service.client.server.rpc-addresses': '${INTERNAL_LISTEN_HOST}',
|
||||
'sentry.service.client.server.rpc-port': '30911',
|
||||
|
||||
# Enable HMS follower.
|
||||
'sentry.service.security.mode': 'none',
|
||||
'sentry.service.server.rpc-address': '${INTERNAL_LISTEN_HOST}',
|
||||
'sentry.service.server.rpc-port': '30911',
|
||||
|
||||
'sentry.store.jdbc.driver': 'org.postgresql.Driver',
|
||||
'sentry.store.jdbc.password': 'password',
|
||||
'sentry.store.jdbc.url': 'jdbc:postgresql://localhost:5432/${SENTRY_POLICY_DB}',
|
||||
'sentry.store.jdbc.user': 'hiveuser',
|
||||
'sentry.verify.schema.version': 'false',
|
||||
}
|
||||
|
||||
if variant is not None:
|
||||
# TODO(todd): these settings seem generic, rather than related to the object ownership
|
||||
# feature, but they're only set in the "variants". Is that right?
|
||||
CONFIG.update({
|
||||
'sentry.hive.testing.mode': 'true',
|
||||
'sentry.policy.store.plugins': 'org.apache.sentry.hdfs.SentryPlugin',
|
||||
# Custom group mapping for custom cluster tests .
|
||||
'sentry.store.group.mapping': 'org.apache.impala.testutil.TestSentryGroupMapper',
|
||||
'sentry.service.processor.factories': '${SENTRY_PROCESSOR_FACTORIES}',
|
||||
})
|
||||
|
||||
CONFIG['sentry.db.policy.store.owner.as.privilege'] = _SETTINGS_BY_VARIANT[variant]
|
||||
@@ -37,7 +37,6 @@ under the License.
|
||||
<hudi.version>${env.IMPALA_HUDI_VERSION}</hudi.version>
|
||||
<ranger.version>${env.IMPALA_RANGER_VERSION}</ranger.version>
|
||||
<postgres.jdbc.version>${env.IMPALA_POSTGRES_JDBC_DRIVER_VERSION}</postgres.jdbc.version>
|
||||
<sentry.version>${env.IMPALA_SENTRY_VERSION}</sentry.version>
|
||||
<hbase.version>${env.IMPALA_HBASE_VERSION}</hbase.version>
|
||||
<orc.version>${env.IMPALA_ORC_JAVA_VERSION}</orc.version>
|
||||
<ozone.version>${env.IMPALA_OZONE_VERSION}</ozone.version>
|
||||
@@ -112,7 +111,8 @@ under the License.
|
||||
<!--
|
||||
This is a transitive dependency from Sentry. Disable downloading snapshots from here
|
||||
since we only want to consume actual releases and we can save Maven from reaching
|
||||
out to this repo unnecessarily to look for snapshot versions.
|
||||
out to this repo unnecessarily to look for snapshot versions. Sentry has been
|
||||
removed, so this may eventually be removed.
|
||||
-->
|
||||
<id>glassfish-repo-archive.repo</id>
|
||||
<name>Glassfish repository - Transitive from Sentry</name>
|
||||
|
||||
@@ -51,7 +51,6 @@ SERVICE_DEPENDENCIES = {
|
||||
"HIVE" : True,
|
||||
"YARN" : False,
|
||||
"HBASE" : False,
|
||||
"SENTRY" : False,
|
||||
"ZOOKEEPER" : False
|
||||
}
|
||||
|
||||
|
||||
14
testdata/bin/run-all.sh
vendored
14
testdata/bin/run-all.sh
vendored
@@ -74,19 +74,13 @@ if [[ ${DEFAULT_FS} == "hdfs://${INTERNAL_LISTEN_HOST}:20500" ]]; then
|
||||
$IMPALA_HOME/testdata/bin/run-hive-server.sh $HIVE_FLAGS 2>&1 | \
|
||||
tee ${IMPALA_CLUSTER_LOGS_DIR}/run-hive-server.log
|
||||
|
||||
if [[ "$DISABLE_SENTRY" != true ]]; then
|
||||
echo " --> Starting the Sentry Policy Server"
|
||||
$IMPALA_HOME/testdata/bin/run-sentry-service.sh 2>&1 | \
|
||||
tee ${IMPALA_CLUSTER_LOGS_DIR}/run-sentry-service.log
|
||||
fi
|
||||
|
||||
elif [[ ${DEFAULT_FS} == "${LOCAL_FS}" ]]; then
|
||||
# When the local file system is used as default, we only start the Hive metastore.
|
||||
# Impala can run locally without additional services.
|
||||
$IMPALA_HOME/testdata/bin/run-hive-server.sh -only_metastore 2>&1 | \
|
||||
tee ${IMPALA_CLUSTER_LOGS_DIR}/run-hive-server.log
|
||||
else
|
||||
# With Isilon, we only start the Hive metastore and Sentry Policy Server.
|
||||
# With Isilon, we only start the Hive metastore.
|
||||
# - HDFS is not started becuase Isilon is used as the defaultFs in core-site
|
||||
# - HBase is irrelevent for Impala testing with Isilon.
|
||||
# - We don't yet have a good way to start YARN using a different defaultFS. Moreoever,
|
||||
@@ -98,12 +92,6 @@ else
|
||||
echo " --> Starting Hive Metastore Service"
|
||||
$IMPALA_HOME/testdata/bin/run-hive-server.sh -only_metastore 2>&1 | \
|
||||
tee ${IMPALA_CLUSTER_LOGS_DIR}/run-hive-server.log
|
||||
|
||||
if [[ "$DISABLE_SENTRY" != true ]]; then
|
||||
echo " --> Starting the Sentry Policy Server"
|
||||
$IMPALA_HOME/testdata/bin/run-sentry-service.sh 2>&1 | \
|
||||
tee ${IMPALA_CLUSTER_LOGS_DIR}/run-sentry-service.log
|
||||
fi
|
||||
fi
|
||||
|
||||
echo " --> Starting Ranger Server"
|
||||
|
||||
15
testdata/bin/run-hive-server.sh
vendored
15
testdata/bin/run-hive-server.sh
vendored
@@ -70,21 +70,6 @@ ${CLUSTER_BIN}/kill-hive-server.sh &> /dev/null
|
||||
export HIVE_METASTORE_HADOOP_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,\
|
||||
suspend=n,address=30010"
|
||||
|
||||
# If this is CDP Hive we need to manually add the sentry jars in the classpath since
|
||||
# CDH Hive metastore scripts do not do so. This is currently to make sure that we can run
|
||||
# all the tests including sentry tests
|
||||
# TODO: This can be removed when we move to Ranger completely
|
||||
if [[ -n "$SENTRY_HOME" ]]; then
|
||||
for f in ${SENTRY_HOME}/lib/sentry-binding-hive*.jar; do
|
||||
FILE_NAME=$(basename $f)
|
||||
# exclude all the hive jars from being included in the classpath since Sentry
|
||||
# depends on Hive 2.1.1
|
||||
if [[ ! $FILE_NAME == hive* ]]; then
|
||||
export HADOOP_CLASSPATH=${HADOOP_CLASSPATH}:${f}
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Add Ranger dependencies if we are starting with Ranger authorization enabled.
|
||||
if [[ $ENABLE_RANGER_AUTH -eq 1 ]]; then
|
||||
export HIVE_CONF_DIR="$HADOOP_CONF_DIR/hive-site-ranger-auth/"
|
||||
|
||||
55
testdata/bin/run-sentry-service.sh
vendored
55
testdata/bin/run-sentry-service.sh
vendored
@@ -1,55 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# 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.
|
||||
|
||||
set -euo pipefail
|
||||
. $IMPALA_HOME/bin/report_build_error.sh
|
||||
setup_report_build_error
|
||||
|
||||
. ${IMPALA_HOME}/bin/set-classpath.sh
|
||||
|
||||
SENTRY_SERVICE_CONFIG=${SENTRY_SERVICE_CONFIG:-}
|
||||
SENTRY_LOG_DIR=${SENTRY_LOG_DIR:-}
|
||||
|
||||
if [ -z ${SENTRY_SERVICE_CONFIG} ]
|
||||
then
|
||||
SENTRY_SERVICE_CONFIG=${SENTRY_CONF_DIR}/sentry-site.xml
|
||||
fi
|
||||
|
||||
if [ -z ${SENTRY_LOG_DIR} ]
|
||||
then
|
||||
LOGDIR="${IMPALA_CLUSTER_LOGS_DIR}"/sentry
|
||||
else
|
||||
LOGDIR=${SENTRY_LOG_DIR}
|
||||
fi
|
||||
|
||||
mkdir -p "${LOGDIR}" || true
|
||||
|
||||
# First kill any running instances of the service.
|
||||
$IMPALA_HOME/testdata/bin/kill-sentry-service.sh
|
||||
|
||||
export HADOOP_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=30020"
|
||||
|
||||
# Sentry picks up JARs from the HADOOP_CLASSPATH and not the CLASSPATH.
|
||||
export HADOOP_CLASSPATH=${POSTGRES_JDBC_DRIVER}:$IMPALA_HOME/fe/target/test-classes
|
||||
# Start the service.
|
||||
${SENTRY_HOME}/bin/sentry --command service -c ${SENTRY_SERVICE_CONFIG} > "${LOGDIR}"/sentry.out 2>&1 &
|
||||
|
||||
# Wait for the service to come online
|
||||
"$JAVA" -cp $CLASSPATH org.apache.impala.testutil.SentryServicePinger \
|
||||
--config_file "${SENTRY_SERVICE_CONFIG}" -n 30 -s 2
|
||||
@@ -1,845 +0,0 @@
|
||||
====
|
||||
---- QUERY
|
||||
create role grant_revoke_test_ALL_SERVER
|
||||
---- RESULTS
|
||||
'Role has been created.'
|
||||
====
|
||||
---- QUERY
|
||||
create role grant_revoke_test_ALL_TEST_DB
|
||||
---- RESULTS
|
||||
'Role has been created.'
|
||||
====
|
||||
---- QUERY
|
||||
create role grant_revoke_test_SELECT_INSERT_TEST_TBL
|
||||
---- RESULTS
|
||||
'Role has been created.'
|
||||
====
|
||||
---- QUERY
|
||||
create role grant_revoke_test_ALL_URI
|
||||
---- RESULTS
|
||||
'Role has been created.'
|
||||
====
|
||||
---- QUERY
|
||||
# Shows all roles in the system
|
||||
show roles
|
||||
---- RESULTS: VERIFY_IS_SUBSET
|
||||
'grant_revoke_test_ALL_SERVER'
|
||||
'grant_revoke_test_ALL_TEST_DB'
|
||||
'grant_revoke_test_SELECT_INSERT_TEST_TBL'
|
||||
'grant_revoke_test_ALL_URI'
|
||||
---- TYPES
|
||||
STRING
|
||||
====
|
||||
---- QUERY
|
||||
create database grant_rev_db location '$FILESYSTEM_PREFIX/test-warehouse/grant_rev_db.db'
|
||||
---- CATCH
|
||||
does not have privileges to execute 'CREATE' on: grant_rev_db
|
||||
====
|
||||
---- QUERY
|
||||
grant all on server to grant_revoke_test_ALL_SERVER
|
||||
====
|
||||
---- QUERY
|
||||
# Group name will be replaced with the actual user's group in the test
|
||||
# framework.
|
||||
grant role grant_revoke_test_ALL_SERVER to group `$GROUP_NAME`
|
||||
====
|
||||
---- QUERY
|
||||
show current roles
|
||||
---- RESULTS: VERIFY_IS_SUBSET
|
||||
'grant_revoke_test_ALL_SERVER'
|
||||
---- TYPES
|
||||
STRING
|
||||
====
|
||||
---- USER
|
||||
does_not_exist
|
||||
---- QUERY
|
||||
# Run this query as a different user and verify no roles show up but the
|
||||
# stmt does not fail with an authorization error.
|
||||
show current roles
|
||||
---- RESULTS: VERIFY_IS_SUBSET
|
||||
---- TYPES
|
||||
STRING
|
||||
====
|
||||
---- QUERY
|
||||
show grant role grant_revoke_test_ALL_SERVER
|
||||
---- RESULTS
|
||||
'server','','','','','all',false,regex:.+
|
||||
---- LABELS
|
||||
scope, database, table, column, uri, privilege, grant_option, create_time
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- QUERY
|
||||
show grant role grant_revoke_test_ALL_SERVER on server
|
||||
---- RESULTS
|
||||
'server','','','','','all',false,regex:.+
|
||||
---- LABELS
|
||||
scope, database, table, column, uri, privilege, grant_option, create_time
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
does_not_exist
|
||||
---- QUERY
|
||||
# User should not have privileges to execute SHOW ROLES
|
||||
show roles
|
||||
---- RESULTS: VERIFY_IS_SUBSET
|
||||
---- TYPES
|
||||
STRING
|
||||
---- CATCH
|
||||
User 'does_not_exist' does not have privileges to access the requested policy metadata
|
||||
====
|
||||
---- USER
|
||||
does_not_exist
|
||||
---- QUERY
|
||||
# User should not have privileges to execute SHOW ROLE GRANT GROUP for a group they do not
|
||||
# belong to.
|
||||
show role grant group root
|
||||
---- RESULTS: VERIFY_IS_SUBSET
|
||||
---- TYPES
|
||||
STRING
|
||||
---- CATCH
|
||||
User 'does_not_exist' does not have privileges to access the requested policy metadata
|
||||
====
|
||||
---- USER
|
||||
root
|
||||
---- QUERY
|
||||
# The 'root' user doesn't have any roles granted to them, but since they are part of the
|
||||
# 'root' group, they should have privileges to execute this statement.
|
||||
show role grant group root
|
||||
---- RESULTS: VERIFY_IS_SUBSET
|
||||
---- TYPES
|
||||
STRING
|
||||
====
|
||||
---- QUERY
|
||||
drop database if exists grant_rev_db
|
||||
====
|
||||
---- QUERY
|
||||
create database grant_rev_db location '$FILESYSTEM_PREFIX/test-warehouse/grant_rev_db.db'
|
||||
====
|
||||
---- QUERY
|
||||
show tables in grant_rev_db
|
||||
---- RESULTS
|
||||
---- TYPES
|
||||
STRING
|
||||
====
|
||||
---- QUERY
|
||||
create table grant_rev_db.test_tbl1(i int)
|
||||
====
|
||||
---- QUERY
|
||||
show tables in grant_rev_db
|
||||
---- RESULTS
|
||||
'test_tbl1'
|
||||
---- TYPES
|
||||
STRING
|
||||
====
|
||||
---- QUERY
|
||||
create function grant_rev_db.fn() RETURNS int
|
||||
LOCATION '$FILESYSTEM_PREFIX/test-warehouse/libTestUdfs.so' SYMBOL='Fn'
|
||||
====
|
||||
---- QUERY
|
||||
show functions in grant_rev_db
|
||||
---- RESULTS
|
||||
'INT','fn()','NATIVE','true'
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING
|
||||
====
|
||||
---- QUERY
|
||||
show create function grant_rev_db.fn
|
||||
---- RESULTS: MULTI_LINE
|
||||
['CREATE FUNCTION grant_rev_db.fn()
|
||||
RETURNS INT
|
||||
LOCATION ''$NAMENODE/test-warehouse/libTestUdfs.so''
|
||||
SYMBOL=''_Z2FnPN10impala_udf15FunctionContextE''
|
||||
']
|
||||
---- TYPES
|
||||
STRING
|
||||
====
|
||||
---- QUERY
|
||||
revoke role grant_revoke_test_ALL_SERVER from group `$GROUP_NAME`
|
||||
====
|
||||
---- QUERY
|
||||
create database grant_rev_db location '$FILESYSTEM_PREFIX/test-warehouse/grant_rev_db.db'
|
||||
---- CATCH
|
||||
does not have privileges to execute 'CREATE' on: grant_rev_db
|
||||
====
|
||||
---- QUERY
|
||||
show tables in grant_rev_db
|
||||
---- CATCH
|
||||
does not have privileges to access: grant_rev_db.*
|
||||
====
|
||||
---- QUERY
|
||||
show functions in grant_rev_db
|
||||
---- CATCH
|
||||
does not have privileges to access: grant_rev_db
|
||||
====
|
||||
---- QUERY
|
||||
show create function grant_rev_db.fn
|
||||
---- CATCH
|
||||
does not have privileges to access: grant_rev_db
|
||||
====
|
||||
---- QUERY
|
||||
show create function _impala_builtins.sin
|
||||
---- RESULTS: MULTI_LINE
|
||||
['CREATE FUNCTION _impala_builtins.sin(DOUBLE)
|
||||
RETURNS DOUBLE
|
||||
SYMBOL=''_ZN6impala13MathFunctions3SinEPN10impala_udf15FunctionContextERKNS1_9DoubleValE''
|
||||
']
|
||||
---- TYPES
|
||||
STRING
|
||||
====
|
||||
---- QUERY
|
||||
grant role grant_revoke_test_ALL_TEST_DB to group `$GROUP_NAME`
|
||||
====
|
||||
---- QUERY
|
||||
# Should now have all privileges on the test db
|
||||
grant all on database grant_rev_db to grant_revoke_test_ALL_TEST_DB
|
||||
====
|
||||
---- QUERY
|
||||
show tables in grant_rev_db
|
||||
---- RESULTS
|
||||
'test_tbl1'
|
||||
---- TYPES
|
||||
STRING
|
||||
====
|
||||
---- QUERY
|
||||
# Even though the user has all privileges on the database, they do not have privileges
|
||||
# on any URIs. The FE tests have additional error message verification.
|
||||
create table grant_rev_db.test_tbl2(i int) location '$FILESYSTEM_PREFIX/test-warehouse/grant_rev_test_tbl2';
|
||||
---- CATCH
|
||||
does not have privileges to access: $NAMENODE/test-warehouse/grant_rev_test_tbl2
|
||||
====
|
||||
---- QUERY
|
||||
grant role grant_revoke_test_ALL_URI to group `$GROUP_NAME`
|
||||
====
|
||||
---- QUERY
|
||||
grant all on uri '$FILESYSTEM_PREFIX/test-warehouse/grant_rev_test_tbl2' to grant_revoke_test_ALL_URI
|
||||
====
|
||||
---- QUERY
|
||||
# Should now have privileges to create the table.
|
||||
create table grant_rev_db.test_tbl2(i int) location '$FILESYSTEM_PREFIX/test-warehouse/grant_rev_test_tbl2';
|
||||
====
|
||||
---- QUERY
|
||||
# Running grant on a URI with upper case letters
|
||||
grant all on uri '$FILESYSTEM_PREFIX/test-warehouse/GRANT_REV_TEST_TBL3' to grant_revoke_test_ALL_URI
|
||||
====
|
||||
---- QUERY
|
||||
# Should now have privileges to create the table.
|
||||
create table grant_rev_db.test_tbl_uppercase(i int) location '$FILESYSTEM_PREFIX/test-warehouse/GRANT_REV_TEST_TBL3/test';
|
||||
====
|
||||
---- QUERY
|
||||
show tables in grant_rev_db
|
||||
---- RESULTS
|
||||
'test_tbl1'
|
||||
'test_tbl2'
|
||||
'test_tbl_uppercase'
|
||||
---- TYPES
|
||||
STRING
|
||||
====
|
||||
---- QUERY
|
||||
# IMPALA-1670: User does not have privileges to access URI when adding partitions
|
||||
create table grant_rev_db.test_tbl_partitioned(i int) partitioned by (j int);
|
||||
alter table grant_rev_db.test_tbl_partitioned add
|
||||
partition (j=1)
|
||||
partition (j=2) location '$FILESYSTEM_PREFIX/test-warehouse/grant_rev_test_prt';
|
||||
---- CATCH
|
||||
does not have privileges to access: $NAMENODE/test-warehouse/grant_rev_test_prt
|
||||
====
|
||||
---- QUERY
|
||||
grant all on uri '$FILESYSTEM_PREFIX/test-warehouse/grant_rev_test_prt'
|
||||
to grant_revoke_test_ALL_URI;
|
||||
====
|
||||
---- QUERY
|
||||
# Should now have privileges to add partitions
|
||||
alter table grant_rev_db.test_tbl_partitioned add
|
||||
partition (j=1)
|
||||
partition (j=2) location '$FILESYSTEM_PREFIX/test-warehouse/grant_rev_test_prt';
|
||||
show partitions grant_rev_db.test_tbl_partitioned;
|
||||
---- RESULTS
|
||||
'1',-1,0,'0B','NOT CACHED','NOT CACHED','TEXT','false',regex:.*/j=1
|
||||
'2',-1,0,'0B','NOT CACHED','NOT CACHED','TEXT','false','$NAMENODE/test-warehouse/grant_rev_test_prt'
|
||||
'Total',-1,0,'0B','0B','','','',''
|
||||
---- TYPES
|
||||
STRING, BIGINT, BIGINT, STRING, STRING, STRING, STRING, STRING, STRING
|
||||
====
|
||||
---- QUERY
|
||||
show grant role grant_revoke_test_ALL_URI
|
||||
---- RESULTS
|
||||
'uri','','','','$NAMENODE/test-warehouse/grant_rev_test_tbl2','all',false,regex:.+
|
||||
'uri','','','','$NAMENODE/test-warehouse/GRANT_REV_TEST_TBL3','all',false,regex:.+
|
||||
'uri','','','','$NAMENODE/test-warehouse/grant_rev_test_prt','all',false,regex:.+
|
||||
---- LABELS
|
||||
scope, database, table, column, uri, privilege, grant_option, create_time
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- QUERY
|
||||
# To create a database server-level privileges are required.
|
||||
create database grant_rev_db location '$FILESYSTEM_PREFIX/test-warehouse/grant_rev_db.db'
|
||||
---- CATCH
|
||||
does not have privileges to execute 'CREATE' on: grant_rev_db
|
||||
====
|
||||
---- QUERY
|
||||
# Dropping the role should remove the privileges
|
||||
drop role grant_revoke_test_ALL_TEST_DB
|
||||
====
|
||||
---- QUERY
|
||||
show tables in grant_rev_db
|
||||
---- CATCH
|
||||
does not have privileges to access: grant_rev_db.*
|
||||
====
|
||||
---- QUERY
|
||||
grant role grant_revoke_test_SELECT_INSERT_TEST_TBL to group `$GROUP_NAME`
|
||||
====
|
||||
---- QUERY
|
||||
GRANT SELECT ON TABLE grant_rev_db.test_tbl1 TO grant_revoke_test_SELECT_INSERT_TEST_TBL
|
||||
====
|
||||
---- QUERY
|
||||
select * from grant_rev_db.test_tbl1
|
||||
---- RESULTS
|
||||
---- TYPES
|
||||
INT
|
||||
====
|
||||
---- QUERY
|
||||
select * from grant_rev_db.test_tbl2
|
||||
---- CATCH
|
||||
does not have privileges to execute 'SELECT' on: grant_rev_db.test_tbl2
|
||||
====
|
||||
---- QUERY
|
||||
insert overwrite grant_rev_db.test_tbl1 select 1
|
||||
---- CATCH
|
||||
does not have privileges to execute 'INSERT' on: grant_rev_db.test_tbl1
|
||||
====
|
||||
---- QUERY
|
||||
GRANT INSERT ON TABLE grant_rev_db.test_tbl1 TO grant_revoke_test_SELECT_INSERT_TEST_TBL
|
||||
====
|
||||
---- QUERY
|
||||
show grant role grant_revoke_test_SELECT_INSERT_TEST_TBL on table grant_rev_db.test_tbl1
|
||||
---- RESULTS
|
||||
'table','grant_rev_db','test_tbl1','','','select',false,regex:.+
|
||||
'table','grant_rev_db','test_tbl1','','','insert',false,regex:.+
|
||||
---- LABELS
|
||||
scope, database, table, column, uri, privilege, grant_option, create_time
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- QUERY
|
||||
insert overwrite grant_rev_db.test_tbl1 select 1
|
||||
---- RESULTS
|
||||
: 1
|
||||
====
|
||||
---- QUERY
|
||||
select * from grant_rev_db.test_tbl1
|
||||
---- RESULTS
|
||||
1
|
||||
---- TYPES
|
||||
INT
|
||||
====
|
||||
---- USER
|
||||
test_user
|
||||
---- QUERY
|
||||
create role some_test_role
|
||||
---- CATCH
|
||||
User 'test_user' does not have privileges to execute: CREATE_ROLE
|
||||
====
|
||||
---- USER
|
||||
test_user
|
||||
---- QUERY
|
||||
drop role grant_revoke_test_ALL_SERVER
|
||||
---- CATCH
|
||||
User 'test_user' does not have privileges to execute: DROP_ROLE
|
||||
====
|
||||
---- USER
|
||||
test_user
|
||||
---- QUERY
|
||||
grant role grant_revoke_test_ALL_SERVER to group `$GROUP_NAME`
|
||||
---- CATCH
|
||||
User 'test_user' does not have privileges to execute: GRANT_ROLE
|
||||
====
|
||||
---- USER
|
||||
test_user
|
||||
---- QUERY
|
||||
revoke role grant_revoke_test_ALL_SERVER from group `$GROUP_NAME`
|
||||
---- CATCH
|
||||
User 'test_user' does not have privileges to execute: REVOKE_ROLE
|
||||
====
|
||||
---- USER
|
||||
test_user
|
||||
---- QUERY
|
||||
grant all on server to grant_revoke_test_ALL_SERVER
|
||||
---- CATCH
|
||||
User 'test_user' does not have privileges to execute: GRANT_PRIVILEGE
|
||||
====
|
||||
---- USER
|
||||
test_user
|
||||
---- QUERY
|
||||
revoke all on server from grant_revoke_test_ALL_SERVER
|
||||
---- CATCH
|
||||
User 'test_user' does not have privileges to execute: REVOKE_PRIVILEGE
|
||||
====
|
||||
---- QUERY
|
||||
# Set up a role to test the WITH GRANT OPTION. Assumes that tests are not running as
|
||||
# 'root' and that 'root' exists on all machines.
|
||||
create role grant_revoke_test_ROOT;
|
||||
grant role grant_revoke_test_ROOT to group root;
|
||||
grant all on database functional to grant_revoke_test_ROOT WITH GRANT OPTION;
|
||||
====
|
||||
---- USER
|
||||
root
|
||||
---- QUERY
|
||||
# There should only be one role that exists for root
|
||||
show current roles
|
||||
---- RESULTS
|
||||
'grant_revoke_test_ROOT'
|
||||
---- TYPES
|
||||
STRING
|
||||
====
|
||||
---- USER
|
||||
root
|
||||
---- QUERY
|
||||
# This privilege actually active
|
||||
show databases
|
||||
---- RESULTS
|
||||
'default','Default Hive database'
|
||||
'functional',''
|
||||
---- TYPES
|
||||
STRING,STRING
|
||||
====
|
||||
---- USER
|
||||
root
|
||||
---- QUERY
|
||||
# The root user should be able to grant/revoke child privileges.
|
||||
# Due to SENTRY-445 they cannot grant SELECT/INSERT even though they have been granted
|
||||
# ALL.
|
||||
grant all on table functional.alltypes to grant_revoke_test_ROOT
|
||||
====
|
||||
---- USER
|
||||
root
|
||||
---- QUERY
|
||||
show grant role grant_revoke_test_ROOT
|
||||
---- RESULTS
|
||||
'database','functional','','','','all',true,regex:.+
|
||||
'table','functional','alltypes','','','all',false,regex:.+
|
||||
---- LABELS
|
||||
scope, database, table, column, uri, privilege, grant_option, create_time
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
---- USER
|
||||
root
|
||||
====
|
||||
---- QUERY
|
||||
revoke all on table functional.alltypes from grant_revoke_test_ROOT
|
||||
====
|
||||
---- USER
|
||||
root
|
||||
---- QUERY
|
||||
# User should not be able to grant privileges outside of this scope.
|
||||
grant all on table functional_seq.alltypes to grant_revoke_test_ROOT
|
||||
---- CATCH
|
||||
User 'root' does not have privileges to execute: GRANT_PRIVILEGE
|
||||
====
|
||||
---- USER
|
||||
root
|
||||
---- QUERY
|
||||
# Also cannot create/drop/grant roles
|
||||
create role grant_revoke_test_ROOT2
|
||||
---- CATCH
|
||||
User 'root' does not have privileges to execute: CREATE_ROLE
|
||||
====
|
||||
---- USER
|
||||
root
|
||||
---- QUERY
|
||||
# Also cannot create/drop/grant roles
|
||||
grant role grant_revoke_test_ROOT to group root
|
||||
---- CATCH
|
||||
User 'root' does not have privileges to execute: GRANT_ROLE
|
||||
====
|
||||
---- QUERY
|
||||
# Revoke the GRANT OPTION and verify the user can no longer GRANT or REVOKE
|
||||
revoke grant option for all on database functional from grant_revoke_test_ROOT
|
||||
====
|
||||
---- USER
|
||||
root
|
||||
---- QUERY
|
||||
grant all on table functional.alltypes to grant_revoke_test_ROOT
|
||||
---- CATCH
|
||||
User 'root' does not have privileges to execute: GRANT_PRIVILEGE
|
||||
====
|
||||
---- USER
|
||||
root
|
||||
---- QUERY
|
||||
# The privilege is still active
|
||||
show databases
|
||||
---- RESULTS
|
||||
'default','Default Hive database'
|
||||
'functional',''
|
||||
---- TYPES
|
||||
STRING,STRING
|
||||
====
|
||||
---- QUERY
|
||||
# Privilege still exists, but grant option is set to false
|
||||
show grant role grant_revoke_test_ROOT
|
||||
---- RESULTS
|
||||
'database','functional','','','','all',false,regex:.+
|
||||
---- LABELS
|
||||
scope, database, table, column, uri, privilege, grant_option, create_time
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
---- USER
|
||||
root
|
||||
====
|
||||
---- QUERY
|
||||
REVOKE ROLE grant_revoke_test_ALL_URI FROM GROUP `$GROUP_NAME`;
|
||||
REVOKE ROLE grant_revoke_test_SELECT_INSERT_TEST_TBL FROM GROUP `$GROUP_NAME`;
|
||||
---- RESULTS
|
||||
'Role has been revoked.'
|
||||
====
|
||||
---- QUERY
|
||||
GRANT ROLE grant_revoke_test_ALL_SERVER TO GROUP `$GROUP_NAME`
|
||||
---- RESULTS
|
||||
'Role has been granted.'
|
||||
====
|
||||
---- QUERY
|
||||
show current roles
|
||||
---- RESULTS: VERIFY_IS_SUBSET
|
||||
'grant_revoke_test_ALL_SERVER'
|
||||
---- TYPES
|
||||
STRING
|
||||
====
|
||||
---- QUERY
|
||||
# Create a table with multiple columns to test column-level security.
|
||||
create table grant_rev_db.test_tbl3(a int, b int, c int, d int, e int) partitioned by (x int, y int)
|
||||
---- RESULTS
|
||||
'Table has been created.'
|
||||
====
|
||||
---- QUERY
|
||||
GRANT SELECT (a, b, x) ON TABLE grant_rev_db.test_tbl3 TO grant_revoke_test_ALL_SERVER
|
||||
---- RESULTS
|
||||
'Privilege(s) have been granted.'
|
||||
====
|
||||
---- QUERY
|
||||
show grant role grant_revoke_test_ALL_SERVER
|
||||
---- RESULTS: VERIFY_IS_EQUAL_SORTED
|
||||
'server','','','','','all',false,regex:.+
|
||||
'column','grant_rev_db','test_tbl3','a','','select',false,regex:.+
|
||||
'column','grant_rev_db','test_tbl3','b','','select',false,regex:.+
|
||||
'column','grant_rev_db','test_tbl3','x','','select',false,regex:.+
|
||||
---- LABELS
|
||||
scope, database, table, column, uri, privilege, grant_option, create_time
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- QUERY
|
||||
GRANT SELECT (c, d, y) ON TABLE grant_rev_db.test_tbl3 TO grant_revoke_test_ALL_SERVER
|
||||
---- RESULTS
|
||||
'Privilege(s) have been granted.'
|
||||
====
|
||||
---- QUERY
|
||||
show grant role grant_revoke_test_ALL_SERVER
|
||||
---- RESULTS: VERIFY_IS_EQUAL_SORTED
|
||||
'server','','','','','all',false,regex:.+
|
||||
'column','grant_rev_db','test_tbl3','a','','select',false,regex:.+
|
||||
'column','grant_rev_db','test_tbl3','b','','select',false,regex:.+
|
||||
'column','grant_rev_db','test_tbl3','c','','select',false,regex:.+
|
||||
'column','grant_rev_db','test_tbl3','d','','select',false,regex:.+
|
||||
'column','grant_rev_db','test_tbl3','x','','select',false,regex:.+
|
||||
'column','grant_rev_db','test_tbl3','y','','select',false,regex:.+
|
||||
---- LABELS
|
||||
scope, database, table, column, uri, privilege, grant_option, create_time
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- QUERY
|
||||
GRANT SELECT (a, a, e, x) ON TABLE grant_rev_db.test_tbl3 TO grant_revoke_test_ALL_SERVER
|
||||
---- RESULTS
|
||||
'Privilege(s) have been granted.'
|
||||
====
|
||||
---- QUERY
|
||||
show grant role grant_revoke_test_ALL_SERVER
|
||||
---- RESULTS: VERIFY_IS_EQUAL_SORTED
|
||||
'server','','','','','all',false,regex:.+
|
||||
'column','grant_rev_db','test_tbl3','a','','select',false,regex:.+
|
||||
'column','grant_rev_db','test_tbl3','b','','select',false,regex:.+
|
||||
'column','grant_rev_db','test_tbl3','c','','select',false,regex:.+
|
||||
'column','grant_rev_db','test_tbl3','d','','select',false,regex:.+
|
||||
'column','grant_rev_db','test_tbl3','e','','select',false,regex:.+
|
||||
'column','grant_rev_db','test_tbl3','x','','select',false,regex:.+
|
||||
'column','grant_rev_db','test_tbl3','y','','select',false,regex:.+
|
||||
---- LABELS
|
||||
scope, database, table, column, uri, privilege, grant_option, create_time
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- QUERY
|
||||
# Revoke SELECT privileges from columns
|
||||
REVOKE SELECT (a, b, b, y) ON TABLE grant_rev_db.test_tbl3 FROM grant_revoke_test_ALL_SERVER
|
||||
---- RESULTS
|
||||
'Privilege(s) have been revoked.'
|
||||
====
|
||||
---- QUERY
|
||||
show grant role grant_revoke_test_ALL_SERVER
|
||||
---- RESULTS: VERIFY_IS_EQUAL_SORTED
|
||||
'server','','','','','all',false,regex:.+
|
||||
'column','grant_rev_db','test_tbl3','c','','select',false,regex:.+
|
||||
'column','grant_rev_db','test_tbl3','d','','select',false,regex:.+
|
||||
'column','grant_rev_db','test_tbl3','e','','select',false,regex:.+
|
||||
'column','grant_rev_db','test_tbl3','x','','select',false,regex:.+
|
||||
---- LABELS
|
||||
scope, database, table, column, uri, privilege, grant_option, create_time
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- QUERY
|
||||
REVOKE SELECT (a, b, c, x) ON TABLE grant_rev_db.test_tbl3 FROM grant_revoke_test_ALL_SERVER
|
||||
---- RESULTS
|
||||
'Privilege(s) have been revoked.'
|
||||
====
|
||||
---- QUERY
|
||||
show grant role grant_revoke_test_ALL_SERVER
|
||||
---- RESULTS: VERIFY_IS_EQUAL_SORTED
|
||||
'server','','','','','all',false,regex:.+
|
||||
'column','grant_rev_db','test_tbl3','d','','select',false,regex:.+
|
||||
'column','grant_rev_db','test_tbl3','e','','select',false,regex:.+
|
||||
---- LABELS
|
||||
scope, database, table, column, uri, privilege, grant_option, create_time
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- QUERY
|
||||
REVOKE SELECT (a, b, c, d, e) ON TABLE grant_rev_db.test_tbl3 FROM grant_revoke_test_ALL_SERVER;
|
||||
---- RESULTS
|
||||
'Privilege(s) have been revoked.'
|
||||
====
|
||||
---- QUERY
|
||||
show grant role grant_revoke_test_ALL_SERVER
|
||||
---- RESULTS: VERIFY_IS_EQUAL_SORTED
|
||||
'server','','','','','all',false,regex:.+
|
||||
---- LABELS
|
||||
scope, database, table, column, uri, privilege, grant_option, create_time
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- QUERY
|
||||
# Grant SELECT on table to 'root' without 'WITH GRANT' option.
|
||||
GRANT ROLE grant_revoke_test_ROOT TO GROUP root;
|
||||
GRANT SELECT ON TABLE grant_rev_db.test_tbl3 TO grant_revoke_test_ROOT;
|
||||
REVOKE ALL ON DATABASE functional FROM grant_revoke_test_ROOT;
|
||||
---- RESULTS
|
||||
'Privilege(s) have been revoked.'
|
||||
====
|
||||
---- USER
|
||||
root
|
||||
---- QUERY
|
||||
show grant role grant_revoke_test_ROOT
|
||||
---- RESULTS: VERIFY_IS_EQUAL_SORTED
|
||||
'table','grant_rev_db','test_tbl3','','','select',false,regex:.+
|
||||
---- LABELS
|
||||
scope, database, table, column, uri, privilege, grant_option, create_time
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
root
|
||||
---- QUERY
|
||||
GRANT SELECT (a) ON TABLE grant_rev_db.test_tbl3 TO grant_revoke_test_ROOT
|
||||
---- CATCH
|
||||
User 'root' does not have privileges to execute: GRANT_PRIVILEGE
|
||||
====
|
||||
---- QUERY
|
||||
REVOKE SELECT ON TABLE grant_rev_db.test_tbl3 FROM grant_revoke_test_ROOT
|
||||
---- RESULTS
|
||||
'Privilege(s) have been revoked.'
|
||||
====
|
||||
---- QUERY
|
||||
# Grant SELECT on table to 'root' with 'WITH GRANT' option.
|
||||
GRANT SELECT ON TABLE grant_rev_db.test_tbl3 TO grant_revoke_test_ROOT WITH GRANT OPTION
|
||||
---- RESULTS
|
||||
'Privilege(s) have been granted.'
|
||||
====
|
||||
---- USER
|
||||
root
|
||||
---- QUERY
|
||||
GRANT SELECT (a) ON TABLE grant_rev_db.test_tbl3 TO grant_revoke_test_ROOT
|
||||
---- RESULTS
|
||||
'Privilege(s) have been granted.'
|
||||
====
|
||||
---- USER
|
||||
root
|
||||
---- QUERY
|
||||
show grant role grant_revoke_test_ROOT
|
||||
---- RESULTS: VERIFY_IS_EQUAL_SORTED
|
||||
'table','grant_rev_db','test_tbl3','','','select',true,regex:.+
|
||||
'column','grant_rev_db','test_tbl3','a','','select',false,regex:.+
|
||||
---- LABELS
|
||||
scope, database, table, column, uri, privilege, grant_option, create_time
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- QUERY
|
||||
GRANT SELECT (a, c, e) ON TABLE grant_rev_db.test_tbl3 TO grant_revoke_test_ALL_SERVER WITH GRANT OPTION
|
||||
---- RESULTS
|
||||
'Privilege(s) have been granted.'
|
||||
====
|
||||
---- QUERY
|
||||
show grant role grant_revoke_test_ALL_SERVER
|
||||
---- RESULTS: VERIFY_IS_EQUAL_SORTED
|
||||
'server','','','','','all',false,regex:.+
|
||||
'column','grant_rev_db','test_tbl3','a','','select',true,regex:.+
|
||||
'column','grant_rev_db','test_tbl3','c','','select',true,regex:.+
|
||||
'column','grant_rev_db','test_tbl3','e','','select',true,regex:.+
|
||||
---- LABELS
|
||||
scope, database, table, column, uri, privilege, grant_option, create_time
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- QUERY
|
||||
REVOKE GRANT OPTION FOR SELECT (a, c) ON TABLE grant_rev_db.test_tbl3 FROM grant_revoke_test_ALL_SERVER
|
||||
---- RESULTS
|
||||
'Privilege(s) have been revoked.'
|
||||
====
|
||||
---- QUERY
|
||||
# TODO: Add a test case that exercises the cascading effect of REVOKE ALL.
|
||||
show grant role grant_revoke_test_ALL_SERVER
|
||||
---- RESULTS: VERIFY_IS_EQUAL_SORTED
|
||||
'server','','','','','all',false,regex:.+
|
||||
'column','grant_rev_db','test_tbl3','a','','select',false,regex:.+
|
||||
'column','grant_rev_db','test_tbl3','c','','select',false,regex:.+
|
||||
'column','grant_rev_db','test_tbl3','e','','select',true,regex:.+
|
||||
---- LABELS
|
||||
scope, database, table, column, uri, privilege, grant_option, create_time
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- QUERY
|
||||
revoke role grant_revoke_test_ALL_SERVER from group `$GROUP_NAME`
|
||||
====
|
||||
---- QUERY
|
||||
# Test 'grant all on server' with explicit server name specified.
|
||||
create role grant_revoke_test_ALL_SERVER1
|
||||
---- RESULTS
|
||||
'Role has been created.'
|
||||
====
|
||||
---- QUERY
|
||||
grant all on server server1 to grant_revoke_test_ALL_SERVER1
|
||||
====
|
||||
---- QUERY
|
||||
grant role grant_revoke_test_ALL_SERVER1 to group `$GROUP_NAME`
|
||||
====
|
||||
---- QUERY
|
||||
drop database grant_rev_db cascade
|
||||
====
|
||||
---- QUERY
|
||||
create database grant_rev_db location '$FILESYSTEM_PREFIX/test-warehouse/grant_rev_db.db'
|
||||
====
|
||||
---- QUERY
|
||||
revoke role grant_revoke_test_ALL_SERVER1 from group `$GROUP_NAME`
|
||||
====
|
||||
---- QUERY
|
||||
create database grant_rev_db location '$FILESYSTEM_PREFIX/test-warehouse/grant_rev_db.db'
|
||||
---- CATCH
|
||||
does not have privileges to execute 'CREATE' on: grant_rev_db
|
||||
====
|
||||
---- QUERY
|
||||
show grant role grant_revoke_test_ALL_SERVER1
|
||||
---- RESULTS: VERIFY_IS_EQUAL_SORTED
|
||||
'server','','','','','all',false,regex:.+
|
||||
---- LABELS
|
||||
scope, database, table, column, uri, privilege, grant_option, create_time
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- QUERY
|
||||
revoke all on server server1 from grant_revoke_test_ALL_SERVER1
|
||||
====
|
||||
---- QUERY
|
||||
show grant role grant_revoke_test_ALL_SERVER1
|
||||
---- RESULTS: VERIFY_IS_EQUAL_SORTED
|
||||
---- LABELS
|
||||
scope, database, table, column, uri, privilege, grant_option, create_time
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- QUERY
|
||||
# IMPALA-4951: make sure database is visible to a user having only column level access
|
||||
# to a table in the database
|
||||
grant role grant_revoke_test_ALL_SERVER to group `$GROUP_NAME`
|
||||
---- RESULTS
|
||||
'Role has been granted.'
|
||||
====
|
||||
---- QUERY
|
||||
create role grant_revoke_test_COLUMN_PRIV
|
||||
====
|
||||
---- QUERY
|
||||
grant role grant_revoke_test_COLUMN_PRIV to group `$GROUP_NAME`;
|
||||
====
|
||||
---- QUERY
|
||||
create database if not exists grant_rev_db;
|
||||
====
|
||||
---- QUERY
|
||||
create table grant_rev_db.test_tbl4 (col1 int, col2 int);
|
||||
====
|
||||
---- QUERY
|
||||
revoke role grant_revoke_test_ALL_SERVER from group `$GROUP_NAME`
|
||||
====
|
||||
---- QUERY
|
||||
show grant role grant_revoke_test_COLUMN_PRIV
|
||||
---- RESULTS
|
||||
---- LABELS
|
||||
scope, database, table, column, uri, privilege, grant_option, create_time
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- QUERY
|
||||
# grant_rev_db is not visible as user does not have any level of access to it
|
||||
show databases
|
||||
---- RESULTS
|
||||
'default','Default Hive database'
|
||||
---- TYPES
|
||||
STRING,STRING
|
||||
====
|
||||
---- QUERY
|
||||
grant select(col1) on table grant_rev_db.test_tbl4 to role grant_revoke_test_COLUMN_PRIV
|
||||
====
|
||||
---- QUERY
|
||||
show grant role grant_revoke_test_COLUMN_PRIV
|
||||
---- RESULTS: VERIFY_IS_EQUAL_SORTED
|
||||
'column','grant_rev_db','test_tbl4','col1','','select',false,regex:.+
|
||||
---- LABELS
|
||||
scope, database, table, column, uri, privilege, grant_option, create_time
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- QUERY
|
||||
show databases
|
||||
---- RESULTS
|
||||
'default','Default Hive database'
|
||||
'grant_rev_db',''
|
||||
---- TYPES
|
||||
STRING,STRING
|
||||
====
|
||||
---- QUERY
|
||||
grant role grant_revoke_test_ALL_SERVER to group `$GROUP_NAME`
|
||||
---- RESULTS
|
||||
'Role has been granted.'
|
||||
====
|
||||
---- QUERY
|
||||
drop database if exists grant_rev_db cascade
|
||||
====
|
||||
---- QUERY
|
||||
revoke role grant_revoke_test_ALL_SERVER from group `$GROUP_NAME`
|
||||
---- RESULTS
|
||||
'Role has been revoked.'
|
||||
====
|
||||
---- QUERY
|
||||
revoke role grant_revoke_test_COLUMN_PRIV from group `$GROUP_NAME`
|
||||
====
|
||||
---- QUERY
|
||||
# Cleanup test roles
|
||||
drop role grant_revoke_test_ALL_SERVER;
|
||||
drop role grant_revoke_test_SELECT_INSERT_TEST_TBL;
|
||||
drop role grant_revoke_test_ALL_URI;
|
||||
drop role grant_revoke_test_ROOT;
|
||||
drop role grant_revoke_test_COLUMN_PRIV;
|
||||
---- RESULTS
|
||||
'Role has been dropped.'
|
||||
====
|
||||
@@ -1,191 +0,0 @@
|
||||
====
|
||||
---- QUERY
|
||||
create role grant_revoke_test_ALL_SERVER
|
||||
---- RESULTS
|
||||
'Role has been created.'
|
||||
====
|
||||
---- QUERY
|
||||
create role grant_revoke_test_ALL_TEST_DB
|
||||
---- RESULTS
|
||||
'Role has been created.'
|
||||
====
|
||||
---- QUERY
|
||||
show roles
|
||||
---- RESULTS: VERIFY_IS_SUBSET
|
||||
'grant_revoke_test_ALL_SERVER'
|
||||
'grant_revoke_test_ALL_TEST_DB'
|
||||
---- TYPES
|
||||
STRING
|
||||
====
|
||||
---- QUERY
|
||||
grant role grant_revoke_test_ALL_SERVER to group `$GROUP_NAME`
|
||||
====
|
||||
---- QUERY
|
||||
grant all on server to grant_revoke_test_ALL_SERVER
|
||||
====
|
||||
---- QUERY
|
||||
create database grant_rev_db
|
||||
====
|
||||
---- QUERY
|
||||
grant role grant_revoke_test_ALL_TEST_DB to group `$GROUP_NAME`
|
||||
====
|
||||
---- QUERY
|
||||
# Should now have all privileges on the test db
|
||||
grant all on database grant_rev_db to grant_revoke_test_ALL_TEST_DB
|
||||
====
|
||||
---- QUERY
|
||||
revoke role grant_revoke_test_ALL_SERVER from group `$GROUP_NAME`
|
||||
====
|
||||
---- QUERY
|
||||
show current roles
|
||||
---- RESULTS
|
||||
'grant_revoke_test_ALL_TEST_DB'
|
||||
---- TYPES
|
||||
STRING
|
||||
====
|
||||
---- QUERY
|
||||
# Even though the user has all privileges on the database, they do not have privileges
|
||||
# to set a Kudu table to be EXTERNAL as that requires ALL on the server. Create a
|
||||
# managed table with the EXTERNAL property explicitly set.
|
||||
create table grant_rev_db.kudu_tbl_with_ext (i int primary key, a string)
|
||||
partition by hash(i) partitions 3 stored as kudu
|
||||
tblproperties('EXTERNAL'='TRUE')
|
||||
---- CATCH
|
||||
does not have privileges to access:
|
||||
====
|
||||
---- QUERY
|
||||
# Check 'external' case-insensitive (see IMPALA-5637).
|
||||
create table grant_rev_db.kudu_tbl_with_ext (i int primary key, a string)
|
||||
partition by hash(i) partitions 3 stored as kudu
|
||||
tblproperties('external'='true')
|
||||
---- CATCH
|
||||
does not have privileges to access:
|
||||
====
|
||||
---- QUERY
|
||||
# Similarly, a managed table with explicit master addresses requires ALL on server.
|
||||
create table grant_rev_db.kudu_tbl_with_addr (i int primary key, a string)
|
||||
partition by hash(i) partitions 3 stored as kudu
|
||||
tblproperties('kudu.master_addresses'='foo')
|
||||
---- CATCH
|
||||
does not have privileges to access:
|
||||
====
|
||||
---- QUERY
|
||||
create table grant_rev_db.kudu_tbl (i int primary key, a string)
|
||||
partition by hash(i) partitions 3 stored as kudu;
|
||||
====
|
||||
---- QUERY
|
||||
# Similarly, the table properties cannot be set via alter table set tblproperties.
|
||||
alter table grant_rev_db.kudu_tbl set tblproperties('kudu.master_addresses'='foo');
|
||||
---- CATCH
|
||||
does not have privileges to access:
|
||||
====
|
||||
---- QUERY
|
||||
alter table grant_rev_db.kudu_tbl set tblproperties('EXTERNAL'='TRUE');
|
||||
---- CATCH
|
||||
does not have privileges to access:
|
||||
====
|
||||
---- QUERY
|
||||
alter table grant_rev_db.kudu_tbl set tblproperties('external'='true');
|
||||
---- CATCH
|
||||
does not have privileges to access:
|
||||
====
|
||||
---- QUERY
|
||||
grant role grant_revoke_test_ALL_SERVER to group `$GROUP_NAME`
|
||||
====
|
||||
---- QUERY
|
||||
# Now the alter table succeeds
|
||||
alter table grant_rev_db.kudu_tbl set tblproperties('EXTERNAL'='TRUE');
|
||||
====
|
||||
---- QUERY
|
||||
# Set it back to FALSE
|
||||
alter table grant_rev_db.kudu_tbl set tblproperties('EXTERNAL'='FALSE');
|
||||
====
|
||||
---- QUERY
|
||||
create role grant_revoke_test_KUDU
|
||||
====
|
||||
---- QUERY
|
||||
grant role grant_revoke_test_KUDU to group `$GROUP_NAME`;
|
||||
====
|
||||
---- QUERY
|
||||
revoke role grant_revoke_test_ALL_SERVER from group `$GROUP_NAME`
|
||||
====
|
||||
---- QUERY
|
||||
revoke role grant_revoke_test_ALL_TEST_DB from group `$GROUP_NAME`
|
||||
====
|
||||
---- QUERY
|
||||
insert into grant_rev_db.kudu_tbl values (1, "foo");
|
||||
---- CATCH
|
||||
does not have privileges to execute 'INSERT' on: grant_rev_db.kudu_tbl
|
||||
====
|
||||
---- QUERY
|
||||
grant insert on table grant_rev_db.kudu_tbl to grant_revoke_test_KUDU
|
||||
====
|
||||
---- QUERY
|
||||
insert into grant_rev_db.kudu_tbl values (1, "foo");
|
||||
====
|
||||
---- QUERY
|
||||
# UPSERT requires ALL
|
||||
upsert into grant_rev_db.kudu_tbl values (1, "bar");
|
||||
---- CATCH
|
||||
does not have privileges to access: grant_rev_db.kudu_tbl
|
||||
====
|
||||
---- QUERY
|
||||
select * from grant_rev_db.kudu_tbl
|
||||
---- CATCH
|
||||
does not have privileges to execute 'SELECT' on: grant_rev_db.kudu_tbl
|
||||
====
|
||||
---- QUERY
|
||||
grant select(i) on table grant_rev_db.kudu_tbl to grant_revoke_test_KUDU
|
||||
====
|
||||
---- QUERY
|
||||
select i from grant_rev_db.kudu_tbl
|
||||
---- RESULTS
|
||||
1
|
||||
---- TYPES
|
||||
INT
|
||||
====
|
||||
---- QUERY
|
||||
# UPDATE/DELETE requires ALL privileges
|
||||
update grant_rev_db.kudu_tbl set a = "zzz"
|
||||
---- CATCH
|
||||
does not have privileges to access: grant_rev_db.kudu_tbl
|
||||
====
|
||||
---- QUERY
|
||||
delete from grant_rev_db.kudu_tbl
|
||||
---- CATCH
|
||||
does not have privileges to access: grant_rev_db.kudu_tbl
|
||||
====
|
||||
---- QUERY
|
||||
grant select(a) on table grant_rev_db.kudu_tbl to grant_revoke_test_KUDU
|
||||
---- RESULTS
|
||||
'Privilege(s) have been granted.'
|
||||
====
|
||||
---- QUERY
|
||||
grant ALL on table grant_rev_db.kudu_tbl to grant_revoke_test_KUDU
|
||||
====
|
||||
---- QUERY
|
||||
update grant_rev_db.kudu_tbl set a = "zzz"
|
||||
---- RESULTS
|
||||
====
|
||||
---- QUERY
|
||||
upsert into grant_rev_db.kudu_tbl values (1, "mom");
|
||||
---- RESULTS
|
||||
====
|
||||
---- QUERY
|
||||
select * from grant_rev_db.kudu_tbl
|
||||
---- RESULTS
|
||||
1,'mom'
|
||||
---- TYPES
|
||||
INT, STRING
|
||||
====
|
||||
---- QUERY
|
||||
drop table grant_rev_db.kudu_tbl
|
||||
====
|
||||
---- QUERY
|
||||
# Cleanup test roles
|
||||
drop role grant_revoke_test_ALL_SERVER;
|
||||
drop role grant_revoke_test_ALL_TEST_DB;
|
||||
drop role grant_revoke_test_KUDU;
|
||||
---- RESULTS
|
||||
'Role has been dropped.'
|
||||
====
|
||||
@@ -1,867 +0,0 @@
|
||||
====
|
||||
---- QUERY
|
||||
show grant user $USER on database $DATABASE
|
||||
---- RESULTS
|
||||
'USER','$USER','database','$DATABASE','','','','owner',true,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
does_not_exist
|
||||
---- QUERY
|
||||
show grant user does_not_exist
|
||||
---- CATCH
|
||||
User 'does_not_exist' does not exist
|
||||
====
|
||||
---- USER
|
||||
user_1group
|
||||
---- QUERY
|
||||
show grant user user_2group
|
||||
---- CATCH
|
||||
User 'user_1group' does not have privileges to access
|
||||
====
|
||||
---- QUERY
|
||||
show grant user user_1group
|
||||
---- RESULTS
|
||||
====
|
||||
---- QUERY
|
||||
show grant user user_2group
|
||||
---- RESULTS
|
||||
====
|
||||
---- QUERY
|
||||
show grant user user1_shared
|
||||
---- RESULTS
|
||||
====
|
||||
---- QUERY
|
||||
show grant user user2_shared
|
||||
---- RESULTS
|
||||
====
|
||||
---- QUERY
|
||||
show grant user user1_shared2
|
||||
---- RESULTS
|
||||
====
|
||||
---- QUERY
|
||||
show grant user user2_shared2
|
||||
---- RESULTS
|
||||
====
|
||||
---- QUERY
|
||||
create role sgu_test_create_role;
|
||||
grant create on database $DATABASE to role sgu_test_create_role;
|
||||
grant role sgu_test_create_role to group group_1;
|
||||
grant role sgu_test_create_role to group group_2a;
|
||||
grant role sgu_test_create_role to group group_2b;
|
||||
grant role sgu_test_create_role to group group_3;
|
||||
grant role sgu_test_create_role to group group_4a;
|
||||
grant role sgu_test_create_role to group group_4b;
|
||||
====
|
||||
---- USER
|
||||
user_1group
|
||||
---- QUERY
|
||||
create table $DATABASE.user_1group_tbl (col1 int);
|
||||
====
|
||||
---- USER
|
||||
user_2group
|
||||
---- QUERY
|
||||
create table $DATABASE.user_2group_tbl (col1 int);
|
||||
====
|
||||
---- USER
|
||||
user1_shared
|
||||
---- QUERY
|
||||
create table $DATABASE.user1_shared_tbl (col1 int);
|
||||
====
|
||||
---- USER
|
||||
user2_shared
|
||||
---- QUERY
|
||||
create table $DATABASE.user2_shared_tbl (col1 int);
|
||||
====
|
||||
---- USER
|
||||
user1_shared2
|
||||
---- QUERY
|
||||
create table $DATABASE.user1_shared2_tbl (col1 int);
|
||||
====
|
||||
---- USER
|
||||
user2_shared2
|
||||
---- QUERY
|
||||
create table $DATABASE.user2_shared2_tbl (col1 int);
|
||||
====
|
||||
---- QUERY
|
||||
drop role sgu_test_create_role
|
||||
====
|
||||
---- USER
|
||||
user_1group
|
||||
---- QUERY
|
||||
show grant user user_1group
|
||||
---- RESULTS
|
||||
'USER','user_1group','table','$DATABASE','user_1group_tbl','','','owner',true,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user_2group
|
||||
---- QUERY
|
||||
show grant user user_2group
|
||||
---- RESULTS
|
||||
'USER','user_2group','table','$DATABASE','user_2group_tbl','','','owner',true,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user1_shared
|
||||
---- QUERY
|
||||
show grant user user1_shared
|
||||
---- RESULTS
|
||||
'USER','user1_shared','table','$DATABASE','user1_shared_tbl','','','owner',true,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user2_shared
|
||||
---- QUERY
|
||||
show grant user user2_shared
|
||||
---- RESULTS
|
||||
'USER','user2_shared','table','$DATABASE','user2_shared_tbl','','','owner',true,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user1_shared2
|
||||
---- QUERY
|
||||
show grant user user1_shared2
|
||||
---- RESULTS
|
||||
'USER','user1_shared2','table','$DATABASE','user1_shared2_tbl','','','owner',true,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user2_shared2
|
||||
---- QUERY
|
||||
show grant user user2_shared2
|
||||
---- RESULTS
|
||||
'USER','user2_shared2','table','$DATABASE','user2_shared2_tbl','','','owner',true,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user_1group
|
||||
---- QUERY
|
||||
show grant user user_1group on table $DATABASE.user_1group_tbl
|
||||
---- RESULTS
|
||||
'USER','user_1group','table','$DATABASE','user_1group_tbl','','','owner',true,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- QUERY
|
||||
create role sgu_test_role1_group1;
|
||||
grant role sgu_test_role1_group1 to group group_1;
|
||||
create role sgu_test_role1_group2a;
|
||||
grant role sgu_test_role1_group2a to group group_2a;
|
||||
create role sgu_test_role1_group2b;
|
||||
grant role sgu_test_role1_group2b to group group_2b;
|
||||
create role sgu_test_role1_group3;
|
||||
grant role sgu_test_role1_group3 to group group_3;
|
||||
create role sgu_test_role1_group4a;
|
||||
grant role sgu_test_role1_group4a to group group_4a;
|
||||
create role sgu_test_role1_group4b;
|
||||
grant role sgu_test_role1_group4b to group group_4b;
|
||||
====
|
||||
---- USER
|
||||
user_1group
|
||||
---- QUERY
|
||||
show grant user user_1group
|
||||
---- RESULTS
|
||||
'USER','user_1group','table','$DATABASE','user_1group_tbl','','','owner',true,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user_2group
|
||||
---- QUERY
|
||||
show grant user user_2group
|
||||
---- RESULTS
|
||||
'USER','user_2group','table','$DATABASE','user_2group_tbl','','','owner',true,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user1_shared
|
||||
---- QUERY
|
||||
show grant user user1_shared
|
||||
---- RESULTS
|
||||
'USER','user1_shared','table','$DATABASE','user1_shared_tbl','','','owner',true,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user2_shared
|
||||
---- QUERY
|
||||
show grant user user2_shared
|
||||
---- RESULTS
|
||||
'USER','user2_shared','table','$DATABASE','user2_shared_tbl','','','owner',true,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user1_shared2
|
||||
---- QUERY
|
||||
show grant user user1_shared2
|
||||
---- RESULTS
|
||||
'USER','user1_shared2','table','$DATABASE','user1_shared2_tbl','','','owner',true,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user2_shared2
|
||||
---- QUERY
|
||||
show grant user user2_shared2
|
||||
---- RESULTS
|
||||
'USER','user2_shared2','table','$DATABASE','user2_shared2_tbl','','','owner',true,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- QUERY
|
||||
grant select on table $DATABASE.user_1group_tbl to role sgu_test_role1_group1;
|
||||
grant insert on table $DATABASE.user_2group_tbl to role sgu_test_role1_group2a;
|
||||
grant alter on table $DATABASE.user_2group_tbl to role sgu_test_role1_group2b;
|
||||
grant drop on table $DATABASE.user1_shared_tbl to role sgu_test_role1_group3;
|
||||
grant refresh on table $DATABASE.user2_shared_tbl to role sgu_test_role1_group3;
|
||||
grant all on table $DATABASE.user1_shared2_tbl to role sgu_test_role1_group4a;
|
||||
grant select on table $DATABASE.user2_shared2_tbl to role sgu_test_role1_group4b;
|
||||
====
|
||||
---- USER
|
||||
user_1group
|
||||
---- QUERY
|
||||
show grant user user_1group
|
||||
---- RESULTS
|
||||
'USER','user_1group','table','$DATABASE','user_1group_tbl','','','owner',true,regex:.+
|
||||
'ROLE','sgu_test_role1_group1','table','$DATABASE','user_1group_tbl','','','select',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user_2group
|
||||
---- QUERY
|
||||
show grant user user_2group
|
||||
---- RESULTS
|
||||
'USER','user_2group','table','$DATABASE','user_2group_tbl','','','owner',true,regex:.+
|
||||
'ROLE','sgu_test_role1_group2a','table','$DATABASE','user_2group_tbl','','','insert',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group2b','table','$DATABASE','user_2group_tbl','','','alter',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user1_shared
|
||||
---- QUERY
|
||||
show grant user user1_shared
|
||||
---- RESULTS
|
||||
'USER','user1_shared','table','$DATABASE','user1_shared_tbl','','','owner',true,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user1_shared_tbl','','','drop',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user2_shared_tbl','','','refresh',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user2_shared
|
||||
---- QUERY
|
||||
show grant user user2_shared
|
||||
---- RESULTS
|
||||
'USER','user2_shared','table','$DATABASE','user2_shared_tbl','','','owner',true,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user1_shared_tbl','','','drop',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user2_shared_tbl','','','refresh',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user1_shared2
|
||||
---- QUERY
|
||||
show grant user user1_shared2
|
||||
---- RESULTS
|
||||
'USER','user1_shared2','table','$DATABASE','user1_shared2_tbl','','','owner',true,regex:.+
|
||||
'ROLE','sgu_test_role1_group4a','table','$DATABASE','user1_shared2_tbl','','','all',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group4b','table','$DATABASE','user2_shared2_tbl','','','select',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user2_shared2
|
||||
---- QUERY
|
||||
show grant user user2_shared2
|
||||
---- RESULTS
|
||||
'USER','user2_shared2','table','$DATABASE','user2_shared2_tbl','','','owner',true,regex:.+
|
||||
'ROLE','sgu_test_role1_group4a','table','$DATABASE','user1_shared2_tbl','','','all',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- QUERY
|
||||
create table $DATABASE.user_1group_tbl2 (col1 int);
|
||||
create table $DATABASE.user_2group_tbl2 (col1 int);
|
||||
create table $DATABASE.user1_shared_tbl2 (col1 int);
|
||||
create table $DATABASE.user2_shared_tbl2 (col1 int);
|
||||
create table $DATABASE.user1_shared2_tbl2 (col1 int);
|
||||
create table $DATABASE.user2_shared2_tbl2 (col1 int);
|
||||
grant insert on table $DATABASE.user_1group_tbl2 to role sgu_test_role1_group1;
|
||||
grant select on table $DATABASE.user_2group_tbl2 to role sgu_test_role1_group2a;
|
||||
grant drop on table $DATABASE.user_2group_tbl2 to role sgu_test_role1_group2b;
|
||||
grant alter on table $DATABASE.user1_shared_tbl2 to role sgu_test_role1_group3;
|
||||
grant select on table $DATABASE.user2_shared_tbl2 to role sgu_test_role1_group3;
|
||||
grant select on table $DATABASE.user1_shared2_tbl2 to role sgu_test_role1_group4a;
|
||||
grant insert on table $DATABASE.user2_shared2_tbl2 to role sgu_test_role1_group4b;
|
||||
====
|
||||
---- USER
|
||||
user_1group
|
||||
---- QUERY
|
||||
show grant user user_1group
|
||||
---- RESULTS
|
||||
'USER','user_1group','table','$DATABASE','user_1group_tbl','','','owner',true,regex:.+
|
||||
'ROLE','sgu_test_role1_group1','table','$DATABASE','user_1group_tbl','','','select',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group1','table','$DATABASE','user_1group_tbl2','','','insert',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user_2group
|
||||
---- QUERY
|
||||
show grant user user_2group
|
||||
---- RESULTS
|
||||
'USER','user_2group','table','$DATABASE','user_2group_tbl','','','owner',true,regex:.+
|
||||
'ROLE','sgu_test_role1_group2a','table','$DATABASE','user_2group_tbl','','','insert',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group2b','table','$DATABASE','user_2group_tbl','','','alter',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group2a','table','$DATABASE','user_2group_tbl2','','','select',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group2b','table','$DATABASE','user_2group_tbl2','','','drop',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user1_shared
|
||||
---- QUERY
|
||||
show grant user user1_shared
|
||||
---- RESULTS
|
||||
'USER','user1_shared','table','$DATABASE','user1_shared_tbl','','','owner',true,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user1_shared_tbl','','','drop',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user2_shared_tbl','','','refresh',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user1_shared_tbl2','','','alter',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user2_shared_tbl2','','','select',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user2_shared
|
||||
---- QUERY
|
||||
show grant user user2_shared
|
||||
---- RESULTS
|
||||
'USER','user2_shared','table','$DATABASE','user2_shared_tbl','','','owner',true,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user1_shared_tbl','','','drop',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user2_shared_tbl','','','refresh',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user1_shared_tbl2','','','alter',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user2_shared_tbl2','','','select',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user1_shared2
|
||||
---- QUERY
|
||||
show grant user user1_shared2
|
||||
---- RESULTS
|
||||
'USER','user1_shared2','table','$DATABASE','user1_shared2_tbl','','','owner',true,regex:.+
|
||||
'ROLE','sgu_test_role1_group4a','table','$DATABASE','user1_shared2_tbl','','','all',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group4b','table','$DATABASE','user2_shared2_tbl','','','select',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group4a','table','$DATABASE','user1_shared2_tbl2','','','select',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group4b','table','$DATABASE','user2_shared2_tbl2','','','insert',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user2_shared2
|
||||
---- QUERY
|
||||
show grant user user2_shared2
|
||||
---- RESULTS
|
||||
'USER','user2_shared2','table','$DATABASE','user2_shared2_tbl','','','owner',true,regex:.+
|
||||
'ROLE','sgu_test_role1_group4a','table','$DATABASE','user1_shared2_tbl','','','all',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group4a','table','$DATABASE','user1_shared2_tbl2','','','select',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- QUERY
|
||||
create role sgu_test_role2_group1;
|
||||
grant role sgu_test_role2_group1 to group group_1;
|
||||
create role sgu_test_role2_group2a;
|
||||
grant role sgu_test_role2_group2a to group group_2a;
|
||||
create role sgu_test_role2_group2b;
|
||||
grant role sgu_test_role2_group2b to group group_2b;
|
||||
create role sgu_test_role2_group3;
|
||||
grant role sgu_test_role2_group3 to group group_3;
|
||||
create role sgu_test_role2_group4a;
|
||||
grant role sgu_test_role2_group4a to group group_4a;
|
||||
create role sgu_test_role2_group4b;
|
||||
grant role sgu_test_role2_group4b to group group_4b;
|
||||
create table $DATABASE.user_1group_tbl3 (col1 int);
|
||||
create table $DATABASE.user_2group_tbl3 (col1 int);
|
||||
create table $DATABASE.user1_shared_tbl3 (col1 int);
|
||||
create table $DATABASE.user2_shared_tbl3 (col1 int);
|
||||
create table $DATABASE.user1_shared2_tbl3 (col1 int);
|
||||
create table $DATABASE.user2_shared2_tbl3 (col1 int);
|
||||
grant select on table $DATABASE.user_1group_tbl3 to role sgu_test_role2_group1;
|
||||
grant insert on table $DATABASE.user_2group_tbl3 to role sgu_test_role2_group2a;
|
||||
grant alter on table $DATABASE.user_2group_tbl3 to role sgu_test_role2_group2b;
|
||||
grant drop on table $DATABASE.user1_shared_tbl3 to role sgu_test_role2_group3;
|
||||
grant insert on table $DATABASE.user2_shared_tbl3 to role sgu_test_role2_group3;
|
||||
grant select on table $DATABASE.user1_shared2_tbl3 to role sgu_test_role2_group4a;
|
||||
grant alter on table $DATABASE.user2_shared2_tbl3 to role sgu_test_role2_group4b;
|
||||
====
|
||||
---- USER
|
||||
user_1group
|
||||
---- QUERY
|
||||
show grant user user_1group
|
||||
---- RESULTS
|
||||
'USER','user_1group','table','$DATABASE','user_1group_tbl','','','owner',true,regex:.+
|
||||
'ROLE','sgu_test_role1_group1','table','$DATABASE','user_1group_tbl','','','select',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group1','table','$DATABASE','user_1group_tbl2','','','insert',false,regex:.+
|
||||
'ROLE','sgu_test_role2_group1','table','$DATABASE','user_1group_tbl3','','','select',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user_2group
|
||||
---- QUERY
|
||||
show grant user user_2group
|
||||
---- RESULTS
|
||||
'USER','user_2group','table','$DATABASE','user_2group_tbl','','','owner',true,regex:.+
|
||||
'ROLE','sgu_test_role1_group2a','table','$DATABASE','user_2group_tbl','','','insert',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group2b','table','$DATABASE','user_2group_tbl','','','alter',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group2a','table','$DATABASE','user_2group_tbl2','','','select',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group2b','table','$DATABASE','user_2group_tbl2','','','drop',false,regex:.+
|
||||
'ROLE','sgu_test_role2_group2a','table','$DATABASE','user_2group_tbl3','','','insert',false,regex:.+
|
||||
'ROLE','sgu_test_role2_group2b','table','$DATABASE','user_2group_tbl3','','','alter',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user1_shared
|
||||
---- QUERY
|
||||
show grant user user1_shared
|
||||
---- RESULTS
|
||||
'USER','user1_shared','table','$DATABASE','user1_shared_tbl','','','owner',true,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user1_shared_tbl','','','drop',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user2_shared_tbl','','','refresh',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user1_shared_tbl2','','','alter',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user2_shared_tbl2','','','select',false,regex:.+
|
||||
'ROLE','sgu_test_role2_group3','table','$DATABASE','user1_shared_tbl3','','','drop',false,regex:.+
|
||||
'ROLE','sgu_test_role2_group3','table','$DATABASE','user2_shared_tbl3','','','insert',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user2_shared
|
||||
---- QUERY
|
||||
show grant user user2_shared
|
||||
---- RESULTS
|
||||
'USER','user2_shared','table','$DATABASE','user2_shared_tbl','','','owner',true,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user1_shared_tbl','','','drop',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user2_shared_tbl','','','refresh',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user1_shared_tbl2','','','alter',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user2_shared_tbl2','','','select',false,regex:.+
|
||||
'ROLE','sgu_test_role2_group3','table','$DATABASE','user1_shared_tbl3','','','drop',false,regex:.+
|
||||
'ROLE','sgu_test_role2_group3','table','$DATABASE','user2_shared_tbl3','','','insert',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user1_shared2
|
||||
---- QUERY
|
||||
show grant user user1_shared2
|
||||
---- RESULTS
|
||||
'USER','user1_shared2','table','$DATABASE','user1_shared2_tbl','','','owner',true,regex:.+
|
||||
'ROLE','sgu_test_role1_group4a','table','$DATABASE','user1_shared2_tbl','','','all',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group4b','table','$DATABASE','user2_shared2_tbl','','','select',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group4a','table','$DATABASE','user1_shared2_tbl2','','','select',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group4b','table','$DATABASE','user2_shared2_tbl2','','','insert',false,regex:.+
|
||||
'ROLE','sgu_test_role2_group4a','table','$DATABASE','user1_shared2_tbl3','','','select',false,regex:.+
|
||||
'ROLE','sgu_test_role2_group4b','table','$DATABASE','user2_shared2_tbl3','','','alter',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user2_shared2
|
||||
---- QUERY
|
||||
show grant user user2_shared2
|
||||
---- RESULTS
|
||||
'USER','user2_shared2','table','$DATABASE','user2_shared2_tbl','','','owner',true,regex:.+
|
||||
'ROLE','sgu_test_role1_group4a','table','$DATABASE','user1_shared2_tbl','','','all',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group4a','table','$DATABASE','user1_shared2_tbl2','','','select',false,regex:.+
|
||||
'ROLE','sgu_test_role2_group4a','table','$DATABASE','user1_shared2_tbl3','','','select',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- QUERY
|
||||
create table $DATABASE.user_1group_tbl4 (col1 int);
|
||||
create table $DATABASE.user_2group_tbl4 (col1 int);
|
||||
create table $DATABASE.user1_shared_tbl4 (col1 int);
|
||||
create table $DATABASE.user2_shared_tbl4 (col1 int);
|
||||
create table $DATABASE.user1_shared2_tbl4 (col1 int);
|
||||
create table $DATABASE.user2_shared2_tbl4 (col1 int);
|
||||
grant select on table $DATABASE.user_1group_tbl4 to role sgu_test_role2_group1;
|
||||
grant insert on table $DATABASE.user_2group_tbl4 to role sgu_test_role2_group2a;
|
||||
grant alter on table $DATABASE.user_2group_tbl4 to role sgu_test_role2_group2b;
|
||||
grant drop on table $DATABASE.user1_shared_tbl4 to role sgu_test_role2_group3;
|
||||
grant insert on table $DATABASE.user2_shared_tbl4 to role sgu_test_role2_group3;
|
||||
grant select on table $DATABASE.user1_shared2_tbl4 to role sgu_test_role2_group4a;
|
||||
grant alter on table $DATABASE.user2_shared2_tbl4 to role sgu_test_role2_group4b;
|
||||
====
|
||||
---- USER
|
||||
user_1group
|
||||
---- QUERY
|
||||
show grant user user_1group
|
||||
---- RESULTS
|
||||
'USER','user_1group','table','$DATABASE','user_1group_tbl','','','owner',true,regex:.+
|
||||
'ROLE','sgu_test_role1_group1','table','$DATABASE','user_1group_tbl','','','select',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group1','table','$DATABASE','user_1group_tbl2','','','insert',false,regex:.+
|
||||
'ROLE','sgu_test_role2_group1','table','$DATABASE','user_1group_tbl3','','','select',false,regex:.+
|
||||
'ROLE','sgu_test_role2_group1','table','$DATABASE','user_1group_tbl4','','','select',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user_2group
|
||||
---- QUERY
|
||||
show grant user user_2group
|
||||
---- RESULTS
|
||||
'USER','user_2group','table','$DATABASE','user_2group_tbl','','','owner',true,regex:.+
|
||||
'ROLE','sgu_test_role1_group2a','table','$DATABASE','user_2group_tbl','','','insert',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group2b','table','$DATABASE','user_2group_tbl','','','alter',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group2a','table','$DATABASE','user_2group_tbl2','','','select',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group2b','table','$DATABASE','user_2group_tbl2','','','drop',false,regex:.+
|
||||
'ROLE','sgu_test_role2_group2a','table','$DATABASE','user_2group_tbl3','','','insert',false,regex:.+
|
||||
'ROLE','sgu_test_role2_group2b','table','$DATABASE','user_2group_tbl3','','','alter',false,regex:.+
|
||||
'ROLE','sgu_test_role2_group2a','table','$DATABASE','user_2group_tbl4','','','insert',false,regex:.+
|
||||
'ROLE','sgu_test_role2_group2b','table','$DATABASE','user_2group_tbl4','','','alter',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user1_shared
|
||||
---- QUERY
|
||||
show grant user user1_shared
|
||||
---- RESULTS
|
||||
'USER','user1_shared','table','$DATABASE','user1_shared_tbl','','','owner',true,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user1_shared_tbl','','','drop',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user2_shared_tbl','','','refresh',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user1_shared_tbl2','','','alter',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user2_shared_tbl2','','','select',false,regex:.+
|
||||
'ROLE','sgu_test_role2_group3','table','$DATABASE','user1_shared_tbl3','','','drop',false,regex:.+
|
||||
'ROLE','sgu_test_role2_group3','table','$DATABASE','user2_shared_tbl3','','','insert',false,regex:.+
|
||||
'ROLE','sgu_test_role2_group3','table','$DATABASE','user1_shared_tbl4','','','drop',false,regex:.+
|
||||
'ROLE','sgu_test_role2_group3','table','$DATABASE','user2_shared_tbl4','','','insert',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user2_shared
|
||||
---- QUERY
|
||||
show grant user user2_shared
|
||||
---- RESULTS
|
||||
'USER','user2_shared','table','$DATABASE','user2_shared_tbl','','','owner',true,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user1_shared_tbl','','','drop',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user2_shared_tbl','','','refresh',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user1_shared_tbl2','','','alter',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user2_shared_tbl2','','','select',false,regex:.+
|
||||
'ROLE','sgu_test_role2_group3','table','$DATABASE','user1_shared_tbl3','','','drop',false,regex:.+
|
||||
'ROLE','sgu_test_role2_group3','table','$DATABASE','user2_shared_tbl3','','','insert',false,regex:.+
|
||||
'ROLE','sgu_test_role2_group3','table','$DATABASE','user1_shared_tbl4','','','drop',false,regex:.+
|
||||
'ROLE','sgu_test_role2_group3','table','$DATABASE','user2_shared_tbl4','','','insert',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user1_shared2
|
||||
---- QUERY
|
||||
show grant user user1_shared2
|
||||
---- RESULTS
|
||||
'USER','user1_shared2','table','$DATABASE','user1_shared2_tbl','','','owner',true,regex:.+
|
||||
'ROLE','sgu_test_role1_group4a','table','$DATABASE','user1_shared2_tbl','','','all',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group4b','table','$DATABASE','user2_shared2_tbl','','','select',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group4a','table','$DATABASE','user1_shared2_tbl2','','','select',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group4b','table','$DATABASE','user2_shared2_tbl2','','','insert',false,regex:.+
|
||||
'ROLE','sgu_test_role2_group4a','table','$DATABASE','user1_shared2_tbl3','','','select',false,regex:.+
|
||||
'ROLE','sgu_test_role2_group4b','table','$DATABASE','user2_shared2_tbl3','','','alter',false,regex:.+
|
||||
'ROLE','sgu_test_role2_group4a','table','$DATABASE','user1_shared2_tbl4','','','select',false,regex:.+
|
||||
'ROLE','sgu_test_role2_group4b','table','$DATABASE','user2_shared2_tbl4','','','alter',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user2_shared2
|
||||
---- QUERY
|
||||
show grant user user2_shared2
|
||||
---- RESULTS
|
||||
'USER','user2_shared2','table','$DATABASE','user2_shared2_tbl','','','owner',true,regex:.+
|
||||
'ROLE','sgu_test_role1_group4a','table','$DATABASE','user1_shared2_tbl','','','all',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group4a','table','$DATABASE','user1_shared2_tbl2','','','select',false,regex:.+
|
||||
'ROLE','sgu_test_role2_group4a','table','$DATABASE','user1_shared2_tbl3','','','select',false,regex:.+
|
||||
'ROLE','sgu_test_role2_group4a','table','$DATABASE','user1_shared2_tbl4','','','select',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- QUERY
|
||||
drop role sgu_test_role2_group1;
|
||||
drop role sgu_test_role2_group2a;
|
||||
drop role sgu_test_role2_group2b;
|
||||
drop role sgu_test_role2_group3;
|
||||
drop role sgu_test_role2_group4a;
|
||||
drop role sgu_test_role2_group4b;
|
||||
====
|
||||
---- USER
|
||||
user_1group
|
||||
---- QUERY
|
||||
show grant user user_1group
|
||||
---- RESULTS
|
||||
'USER','user_1group','table','$DATABASE','user_1group_tbl','','','owner',true,regex:.+
|
||||
'ROLE','sgu_test_role1_group1','table','$DATABASE','user_1group_tbl','','','select',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group1','table','$DATABASE','user_1group_tbl2','','','insert',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user_2group
|
||||
---- QUERY
|
||||
show grant user user_2group
|
||||
---- RESULTS
|
||||
'USER','user_2group','table','$DATABASE','user_2group_tbl','','','owner',true,regex:.+
|
||||
'ROLE','sgu_test_role1_group2a','table','$DATABASE','user_2group_tbl','','','insert',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group2b','table','$DATABASE','user_2group_tbl','','','alter',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group2a','table','$DATABASE','user_2group_tbl2','','','select',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group2b','table','$DATABASE','user_2group_tbl2','','','drop',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user1_shared
|
||||
---- QUERY
|
||||
show grant user user1_shared
|
||||
---- RESULTS
|
||||
'USER','user1_shared','table','$DATABASE','user1_shared_tbl','','','owner',true,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user1_shared_tbl','','','drop',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user2_shared_tbl','','','refresh',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user1_shared_tbl2','','','alter',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user2_shared_tbl2','','','select',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user2_shared
|
||||
---- QUERY
|
||||
show grant user user2_shared
|
||||
---- RESULTS
|
||||
'USER','user2_shared','table','$DATABASE','user2_shared_tbl','','','owner',true,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user1_shared_tbl','','','drop',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user2_shared_tbl','','','refresh',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user1_shared_tbl2','','','alter',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user2_shared_tbl2','','','select',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user1_shared2
|
||||
---- QUERY
|
||||
show grant user user1_shared2
|
||||
---- RESULTS
|
||||
'USER','user1_shared2','table','$DATABASE','user1_shared2_tbl','','','owner',true,regex:.+
|
||||
'ROLE','sgu_test_role1_group4a','table','$DATABASE','user1_shared2_tbl','','','all',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group4b','table','$DATABASE','user2_shared2_tbl','','','select',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group4a','table','$DATABASE','user1_shared2_tbl2','','','select',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group4b','table','$DATABASE','user2_shared2_tbl2','','','insert',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user2_shared2
|
||||
---- QUERY
|
||||
show grant user user2_shared2
|
||||
---- RESULTS
|
||||
'USER','user2_shared2','table','$DATABASE','user2_shared2_tbl','','','owner',true,regex:.+
|
||||
'ROLE','sgu_test_role1_group4a','table','$DATABASE','user1_shared2_tbl','','','all',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group4a','table','$DATABASE','user1_shared2_tbl2','','','select',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- QUERY
|
||||
revoke insert on table $DATABASE.user_1group_tbl2 from role sgu_test_role1_group1;
|
||||
revoke select on table $DATABASE.user_2group_tbl2 from role sgu_test_role1_group2a;
|
||||
revoke drop on table $DATABASE.user_2group_tbl2 from role sgu_test_role1_group2b;
|
||||
revoke alter on table $DATABASE.user1_shared_tbl2 from role sgu_test_role1_group3;
|
||||
revoke select on table $DATABASE.user2_shared_tbl2 from role sgu_test_role1_group3;
|
||||
revoke select on table $DATABASE.user1_shared2_tbl2 from role sgu_test_role1_group4a;
|
||||
revoke insert on table $DATABASE.user2_shared2_tbl2 from role sgu_test_role1_group4b;
|
||||
====
|
||||
---- USER
|
||||
user_1group
|
||||
---- QUERY
|
||||
show grant user user_1group
|
||||
---- RESULTS
|
||||
'USER','user_1group','table','$DATABASE','user_1group_tbl','','','owner',true,regex:.+
|
||||
'ROLE','sgu_test_role1_group1','table','$DATABASE','user_1group_tbl','','','select',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user_2group
|
||||
---- QUERY
|
||||
show grant user user_2group
|
||||
---- RESULTS
|
||||
'USER','user_2group','table','$DATABASE','user_2group_tbl','','','owner',true,regex:.+
|
||||
'ROLE','sgu_test_role1_group2a','table','$DATABASE','user_2group_tbl','','','insert',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group2b','table','$DATABASE','user_2group_tbl','','','alter',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user1_shared
|
||||
---- QUERY
|
||||
show grant user user1_shared
|
||||
---- RESULTS
|
||||
'USER','user1_shared','table','$DATABASE','user1_shared_tbl','','','owner',true,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user1_shared_tbl','','','drop',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user2_shared_tbl','','','refresh',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user2_shared
|
||||
---- QUERY
|
||||
show grant user user2_shared
|
||||
---- RESULTS
|
||||
'USER','user2_shared','table','$DATABASE','user2_shared_tbl','','','owner',true,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user1_shared_tbl','','','drop',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user2_shared_tbl','','','refresh',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user1_shared2
|
||||
---- QUERY
|
||||
show grant user user1_shared2
|
||||
---- RESULTS
|
||||
'USER','user1_shared2','table','$DATABASE','user1_shared2_tbl','','','owner',true,regex:.+
|
||||
'ROLE','sgu_test_role1_group4a','table','$DATABASE','user1_shared2_tbl','','','all',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group4b','table','$DATABASE','user2_shared2_tbl','','','select',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user2_shared2
|
||||
---- QUERY
|
||||
show grant user user2_shared2
|
||||
---- RESULTS
|
||||
'USER','user2_shared2','table','$DATABASE','user2_shared2_tbl','','','owner',true,regex:.+
|
||||
'ROLE','sgu_test_role1_group4a','table','$DATABASE','user1_shared2_tbl','','','all',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- QUERY
|
||||
drop table $DATABASE.user_1group_tbl;
|
||||
drop table $DATABASE.user_2group_tbl;
|
||||
drop table $DATABASE.user1_shared_tbl;
|
||||
drop table $DATABASE.user2_shared_tbl;
|
||||
drop table $DATABASE.user1_shared2_tbl;
|
||||
drop table $DATABASE.user2_shared2_tbl;
|
||||
====
|
||||
---- USER
|
||||
user_1group
|
||||
---- QUERY
|
||||
show grant user user_1group
|
||||
---- RESULTS
|
||||
'ROLE','sgu_test_role1_group1','table','$DATABASE','user_1group_tbl','','','select',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user_2group
|
||||
---- QUERY
|
||||
show grant user user_2group
|
||||
---- RESULTS
|
||||
'ROLE','sgu_test_role1_group2a','table','$DATABASE','user_2group_tbl','','','insert',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group2b','table','$DATABASE','user_2group_tbl','','','alter',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user1_shared
|
||||
---- QUERY
|
||||
show grant user user1_shared
|
||||
---- RESULTS
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user1_shared_tbl','','','drop',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user2_shared_tbl','','','refresh',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user2_shared
|
||||
---- QUERY
|
||||
show grant user user2_shared
|
||||
---- RESULTS
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user1_shared_tbl','','','drop',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group3','table','$DATABASE','user2_shared_tbl','','','refresh',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user1_shared2
|
||||
---- QUERY
|
||||
show grant user user1_shared2
|
||||
---- RESULTS
|
||||
'ROLE','sgu_test_role1_group4a','table','$DATABASE','user1_shared2_tbl','','','all',false,regex:.+
|
||||
'ROLE','sgu_test_role1_group4b','table','$DATABASE','user2_shared2_tbl','','','select',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- USER
|
||||
user2_shared2
|
||||
---- QUERY
|
||||
show grant user user2_shared2
|
||||
---- RESULTS
|
||||
'ROLE','sgu_test_role1_group4a','table','$DATABASE','user1_shared2_tbl','','','all',false,regex:.+
|
||||
---- TYPES
|
||||
STRING, STRING, STRING, STRING, STRING, STRING, STRING, STRING, BOOLEAN, STRING
|
||||
====
|
||||
---- QUERY
|
||||
drop role sgu_test_role1_group1;
|
||||
drop role sgu_test_role1_group2a;
|
||||
drop role sgu_test_role1_group2b;
|
||||
drop role sgu_test_role1_group3;
|
||||
drop role sgu_test_role1_group4a;
|
||||
drop role sgu_test_role1_group4b;
|
||||
====
|
||||
---- USER
|
||||
user_1group
|
||||
---- QUERY
|
||||
show grant user user_1group
|
||||
---- RESULTS
|
||||
====
|
||||
---- USER
|
||||
user_2group
|
||||
---- QUERY
|
||||
show grant user user_2group
|
||||
---- RESULTS
|
||||
====
|
||||
---- USER
|
||||
user1_shared
|
||||
---- QUERY
|
||||
show grant user user1_shared
|
||||
---- RESULTS
|
||||
====
|
||||
---- USER
|
||||
user2_shared
|
||||
---- QUERY
|
||||
show grant user user2_shared
|
||||
---- RESULTS
|
||||
====
|
||||
---- USER
|
||||
user1_shared2
|
||||
---- QUERY
|
||||
show grant user user1_shared2
|
||||
---- RESULTS
|
||||
====
|
||||
---- USER
|
||||
user2_shared2
|
||||
---- QUERY
|
||||
show grant user user2_shared2
|
||||
---- RESULTS
|
||||
====
|
||||
@@ -38,10 +38,7 @@ from tests.common.file_utils import assert_file_in_dir_contains,\
|
||||
assert_no_files_in_dir_contain
|
||||
from tests.common.skip import SkipIf
|
||||
|
||||
SENTRY_CONFIG_DIR = os.getenv('IMPALA_HOME') + '/fe/src/test/resources/'
|
||||
SENTRY_BASE_LOG_DIR = os.getenv('IMPALA_CLUSTER_LOGS_DIR') + "/sentry"
|
||||
SENTRY_CONFIG_FILE = SENTRY_CONFIG_DIR + 'sentry-site.xml'
|
||||
SENTRY_CONFIG_FILE_OO = SENTRY_CONFIG_DIR + 'sentry-site_oo.xml'
|
||||
|
||||
PRIVILEGES = ['all', 'alter', 'drop', 'insert', 'refresh', 'select']
|
||||
ADMIN = "admin"
|
||||
|
||||
@@ -94,129 +91,6 @@ class TestAuthorization(CustomClusterTestSuite):
|
||||
TestHS2.check_response(resp)
|
||||
return resp
|
||||
|
||||
@SkipIf.sentry_disabled
|
||||
@pytest.mark.execute_serially
|
||||
@CustomClusterTestSuite.with_args(
|
||||
impalad_args="--server_name=server1 "
|
||||
"--authorized_proxy_user_config=hue={0}".format(getuser()),
|
||||
catalogd_args="--sentry_config=" + SENTRY_CONFIG_FILE,
|
||||
sentry_config=SENTRY_CONFIG_FILE)
|
||||
def test_access_runtime_profile(self, unique_role, unique_name):
|
||||
unique_db = unique_name + "_db"
|
||||
|
||||
try:
|
||||
self.session_handle = self.__open_hs2(getuser(), dict()).sessionHandle
|
||||
self.__execute_hs2_stmt("create role {0}".format(unique_role))
|
||||
self.__execute_hs2_stmt("grant create on server to role {0}".format(unique_role))
|
||||
self.__execute_hs2_stmt("grant all on database tpch to role {0}"
|
||||
.format(unique_role))
|
||||
self.__execute_hs2_stmt("grant select on table functional.complex_view to role {0}"
|
||||
.format(unique_role))
|
||||
self.__execute_hs2_stmt("grant role {0} to group {1}"
|
||||
.format(unique_role, grp.getgrnam(getuser()).gr_name))
|
||||
# Create db with permissions
|
||||
self.__execute_hs2_stmt("create database {0}".format(unique_db))
|
||||
self.__execute_hs2_stmt("grant all on database {0} to role {1}"
|
||||
.format(unique_db, unique_role))
|
||||
|
||||
# Current user can't access view's underlying tables
|
||||
bad_resp = self.__execute_hs2_stmt("explain select * from functional.complex_view",
|
||||
False)
|
||||
assert 'User \'%s\' does not have privileges to EXPLAIN' % getuser() in \
|
||||
str(bad_resp)
|
||||
# User should not have access to the runtime profile
|
||||
self.__run_stmt_and_verify_profile_access("select * from functional.complex_view",
|
||||
False, False)
|
||||
self.__run_stmt_and_verify_profile_access("select * from functional.complex_view",
|
||||
False, True)
|
||||
|
||||
# Repeat as a delegated user
|
||||
self.session_handle = \
|
||||
self.__open_hs2('hue', {'impala.doas.user': getuser()}).sessionHandle
|
||||
# User should not have access to the runtime profile
|
||||
self.__run_stmt_and_verify_profile_access("select * from functional.complex_view",
|
||||
False, False)
|
||||
self.__run_stmt_and_verify_profile_access("select * from functional.complex_view",
|
||||
False, True)
|
||||
|
||||
# Create a view for which the user has access to the underlying tables.
|
||||
self.session_handle = self.__open_hs2(getuser(), dict()).sessionHandle
|
||||
self.__execute_hs2_stmt(
|
||||
"create view if not exists {0}.customer_view as select * from tpch.customer "
|
||||
"limit 1".format(unique_db))
|
||||
|
||||
# User should be able to run EXPLAIN
|
||||
self.__execute_hs2_stmt("explain select * from {0}.customer_view"
|
||||
.format(unique_db))
|
||||
|
||||
# User should have access to the runtime profile and exec summary
|
||||
self.__run_stmt_and_verify_profile_access("select * from {0}.customer_view"
|
||||
.format(unique_db), True, False)
|
||||
self.__run_stmt_and_verify_profile_access("select * from {0}.customer_view"
|
||||
.format(unique_db), True, True)
|
||||
|
||||
# Repeat as a delegated user
|
||||
self.session_handle = \
|
||||
self.__open_hs2('hue', {'impala.doas.user': getuser()}).sessionHandle
|
||||
# Delegated user is the current user
|
||||
self.__run_stmt_and_verify_profile_access("select * from {0}.customer_view"
|
||||
.format(unique_db), True, False)
|
||||
self.__run_stmt_and_verify_profile_access("select * from {0}.customer_view"
|
||||
.format(unique_db), True, True)
|
||||
finally:
|
||||
self.__execute_hs2_stmt("grant all on server to role {0}".format(unique_role))
|
||||
self.__execute_hs2_stmt("drop view if exists {0}.customer_view".format(unique_db))
|
||||
self.__execute_hs2_stmt("drop table if exists {0}.customer".format(unique_db))
|
||||
self.__execute_hs2_stmt("drop database if exists {0}".format(unique_db))
|
||||
self.__execute_hs2_stmt("drop role {0}".format(unique_role))
|
||||
|
||||
def __run_stmt_and_verify_profile_access(self, stmt, has_access, close_operation):
|
||||
"""Runs 'stmt' and retrieves the runtime profile and exec summary. If
|
||||
'has_access' is true, it verifies that no runtime profile or exec summary are
|
||||
returned. If 'close_operation' is true, make sure the operation is closed before
|
||||
retrieving the profile and exec summary."""
|
||||
from tests.hs2.test_hs2 import TestHS2
|
||||
execute_statement_resp = self.__execute_hs2_stmt(stmt, False)
|
||||
|
||||
if close_operation:
|
||||
close_operation_req = TCLIService.TCloseOperationReq()
|
||||
close_operation_req.operationHandle = execute_statement_resp.operationHandle
|
||||
TestHS2.check_response(self.hs2_client.CloseOperation(close_operation_req))
|
||||
|
||||
get_profile_req = ImpalaHiveServer2Service.TGetRuntimeProfileReq()
|
||||
get_profile_req.operationHandle = execute_statement_resp.operationHandle
|
||||
get_profile_req.sessionHandle = self.session_handle
|
||||
get_profile_resp = self.hs2_client.GetRuntimeProfile(get_profile_req)
|
||||
|
||||
if has_access:
|
||||
TestHS2.check_response(get_profile_resp)
|
||||
assert "Plan: " in get_profile_resp.profile
|
||||
else:
|
||||
assert "User %s is not authorized to access the runtime profile or "\
|
||||
"execution summary." % (getuser()) in str(get_profile_resp)
|
||||
|
||||
exec_summary_req = ImpalaHiveServer2Service.TGetExecSummaryReq()
|
||||
exec_summary_req.operationHandle = execute_statement_resp.operationHandle
|
||||
exec_summary_req.sessionHandle = self.session_handle
|
||||
exec_summary_resp = self.hs2_client.GetExecSummary(exec_summary_req)
|
||||
|
||||
if has_access:
|
||||
TestHS2.check_response(exec_summary_resp)
|
||||
else:
|
||||
assert "User %s is not authorized to access the runtime profile or "\
|
||||
"execution summary." % (getuser()) in str(exec_summary_resp)
|
||||
|
||||
@SkipIf.sentry_disabled
|
||||
@pytest.mark.execute_serially
|
||||
@CustomClusterTestSuite.with_args(
|
||||
impalad_args="--server_name=server1 --sentry_config=" + SENTRY_CONFIG_FILE,
|
||||
catalogd_args="--sentry_config=" + SENTRY_CONFIG_FILE,
|
||||
impala_log_dir=tempfile.mkdtemp(prefix="test_deprecated_none_",
|
||||
dir=os.getenv("LOG_DIR")))
|
||||
def test_deprecated_flag_doesnt_show(self):
|
||||
assert_no_files_in_dir_contain(self.impala_log_dir, "Ignoring removed flag "
|
||||
"authorization_policy_file")
|
||||
|
||||
@pytest.mark.execute_serially
|
||||
@CustomClusterTestSuite.with_args("--server_name=server1\
|
||||
--authorization_policy_file=ignored_file",
|
||||
@@ -226,246 +100,6 @@ class TestAuthorization(CustomClusterTestSuite):
|
||||
assert_file_in_dir_contains(self.impala_log_dir, "Ignoring removed flag "
|
||||
"authorization_policy_file")
|
||||
|
||||
@SkipIf.sentry_disabled
|
||||
@pytest.mark.execute_serially
|
||||
@CustomClusterTestSuite.with_args(
|
||||
impalad_args="--server_name=server1 --sentry_config=%s" % SENTRY_CONFIG_FILE,
|
||||
catalogd_args="--sentry_config=%s" % SENTRY_CONFIG_FILE,
|
||||
impala_log_dir=tempfile.mkdtemp(prefix="test_catalog_restart_",
|
||||
dir=os.getenv("LOG_DIR")))
|
||||
def test_catalog_restart(self, unique_role):
|
||||
"""IMPALA-7713: Tests that a catalogd restart when authorization is enabled should
|
||||
reset the previous privileges stored in impalad's catalog to avoid stale privilege
|
||||
data in the impalad's catalog."""
|
||||
def assert_privileges():
|
||||
result = self.client.execute("show grant role %s_foo" % unique_role)
|
||||
TestAuthorization._check_privileges(result, [["database", "functional",
|
||||
"", "", "", "all", "false"]])
|
||||
|
||||
result = self.client.execute("show grant role %s_bar" % unique_role)
|
||||
TestAuthorization._check_privileges(result, [["database", "functional_kudu",
|
||||
"", "", "", "all", "false"]])
|
||||
|
||||
result = self.client.execute("show grant role %s_baz" % unique_role)
|
||||
TestAuthorization._check_privileges(result, [["database", "functional_avro",
|
||||
"", "", "", "all", "false"]])
|
||||
|
||||
self.role_cleanup(unique_role)
|
||||
try:
|
||||
self.client.execute("create role %s_foo" % unique_role)
|
||||
self.client.execute("create role %s_bar" % unique_role)
|
||||
self.client.execute("create role %s_baz" % unique_role)
|
||||
self.client.execute("grant all on database functional to role %s_foo" %
|
||||
unique_role)
|
||||
self.client.execute("grant all on database functional_kudu to role %s_bar" %
|
||||
unique_role)
|
||||
self.client.execute("grant all on database functional_avro to role %s_baz" %
|
||||
unique_role)
|
||||
|
||||
assert_privileges()
|
||||
self._start_impala_cluster(["--catalogd_args=--sentry_config=%s" %
|
||||
SENTRY_CONFIG_FILE, "--restart_catalogd_only"])
|
||||
assert_privileges()
|
||||
finally:
|
||||
self.role_cleanup(unique_role)
|
||||
|
||||
def role_cleanup(self, role_name_match):
|
||||
"""Cleans up any roles that match the given role name."""
|
||||
for role_name in self.client.execute("show roles").data:
|
||||
if role_name_match in role_name:
|
||||
self.client.execute("drop role %s" % role_name)
|
||||
|
||||
@staticmethod
|
||||
def _check_privileges(result, expected):
|
||||
def columns(row):
|
||||
cols = row.split("\t")
|
||||
return cols[0:len(cols) - 1]
|
||||
assert map(columns, result.data) == expected
|
||||
|
||||
@SkipIf.sentry_disabled
|
||||
@pytest.mark.execute_serially
|
||||
@CustomClusterTestSuite.with_args(
|
||||
impalad_args="--server_name=server1 --sentry_config=%s" % SENTRY_CONFIG_FILE,
|
||||
catalogd_args="--sentry_config=%s" % SENTRY_CONFIG_FILE,
|
||||
impala_log_dir=tempfile.mkdtemp(prefix="test_catalog_restart_",
|
||||
dir=os.getenv("LOG_DIR")))
|
||||
def test_catalog_object(self, unique_role):
|
||||
"""IMPALA-7721: Tests /catalog_object web API for principal and privilege"""
|
||||
self.role_cleanup(unique_role)
|
||||
try:
|
||||
self.client.execute("create role %s" % unique_role)
|
||||
self.client.execute("grant select on database functional to role %s" % unique_role)
|
||||
for service in [self.cluster.catalogd.service,
|
||||
self.cluster.get_first_impalad().service]:
|
||||
obj_dump = service.get_catalog_object_dump("PRINCIPAL", "%s.ROLE" % unique_role)
|
||||
assert "catalog_version" in obj_dump
|
||||
|
||||
# Get the privilege associated with that principal ID.
|
||||
principal_id = re.search(r"principal_id \(i32\) = (\d+)", obj_dump)
|
||||
assert principal_id is not None
|
||||
obj_dump = service.get_catalog_object_dump("PRIVILEGE", urllib.quote(
|
||||
"server=server1->db=functional->action=select->grantoption=false.%s.ROLE" %
|
||||
principal_id.group(1)))
|
||||
assert "catalog_version" in obj_dump
|
||||
|
||||
# Get the principal that does not exist.
|
||||
obj_dump = service.get_catalog_object_dump("PRINCIPAL", "doesnotexist.ROLE")
|
||||
assert "CatalogException" in obj_dump
|
||||
|
||||
# Get the privilege that does not exist.
|
||||
obj_dump = service.get_catalog_object_dump("PRIVILEGE", urllib.quote(
|
||||
"server=server1->db=doesntexist->action=select->grantoption=false.%s.ROLE" %
|
||||
principal_id.group(1)))
|
||||
assert "CatalogException" in obj_dump
|
||||
finally:
|
||||
self.role_cleanup(unique_role)
|
||||
|
||||
@SkipIf.sentry_disabled
|
||||
@pytest.mark.execute_serially
|
||||
@CustomClusterTestSuite.with_args(
|
||||
impalad_args="--server_name=server1 --sentry_config=%s" % SENTRY_CONFIG_FILE,
|
||||
catalogd_args="--sentry_config=%s --sentry_catalog_polling_frequency_s=3600" %
|
||||
SENTRY_CONFIG_FILE,
|
||||
impala_log_dir=tempfile.mkdtemp(prefix="test_invalidate_metadata_sentry_unavailable_",
|
||||
dir=os.getenv("LOG_DIR")))
|
||||
def test_invalidate_metadata_sentry_unavailable(self, unique_role):
|
||||
"""IMPALA-7824: Tests that running INVALIDATE METADATA when Sentry is unavailable
|
||||
should not cause Impala to hang."""
|
||||
self.role_cleanup(unique_role)
|
||||
try:
|
||||
group_name = grp.getgrnam(getuser()).gr_name
|
||||
self.client.execute("create role %s" % unique_role)
|
||||
self.client.execute("grant all on server to role %s" % unique_role)
|
||||
self.client.execute("grant role %s to group `%s`" % (unique_role, group_name))
|
||||
|
||||
self._stop_sentry_service()
|
||||
# Calling INVALIDATE METADATA when Sentry is unavailable should return an error.
|
||||
result = self.execute_query_expect_failure(self.client, "invalidate metadata")
|
||||
result_str = str(result)
|
||||
assert "MESSAGE: CatalogException: Error refreshing authorization policy:" \
|
||||
in result_str
|
||||
assert "CAUSED BY: ImpalaRuntimeException: Error refreshing authorization policy." \
|
||||
" Sentry is unavailable. Ensure Sentry is up:" in result_str
|
||||
|
||||
self._start_sentry_service(SENTRY_CONFIG_FILE)
|
||||
# Calling INVALIDATE METADATA after Sentry is up should not return an error.
|
||||
self.execute_query_expect_success(self.client, "invalidate metadata")
|
||||
finally:
|
||||
self.role_cleanup(unique_role)
|
||||
|
||||
@SkipIf.sentry_disabled
|
||||
@pytest.mark.execute_serially
|
||||
@CustomClusterTestSuite.with_args(
|
||||
impalad_args="--server_name=server1 --sentry_config=%s" % SENTRY_CONFIG_FILE,
|
||||
catalogd_args="--sentry_config=%s --sentry_catalog_polling_frequency_s=3600 " %
|
||||
SENTRY_CONFIG_FILE,
|
||||
impala_log_dir=tempfile.mkdtemp(prefix="test_refresh_authorization_",
|
||||
dir=os.getenv("LOG_DIR")))
|
||||
def test_refresh_authorization(self, unique_role):
|
||||
"""Tests refresh authorization statement by adding and removing roles and privileges
|
||||
externally. The long Sentry polling is used so that any authorization metadata
|
||||
updated externally does not get polled by Impala in order to test an an explicit
|
||||
call to refresh authorization statement."""
|
||||
group_name = grp.getgrnam(getuser()).gr_name
|
||||
self.role_cleanup(unique_role)
|
||||
for sync_ddl in [1, 0]:
|
||||
query_options = {'sync_ddl': sync_ddl}
|
||||
clients = []
|
||||
if sync_ddl:
|
||||
# When sync_ddl is True, we want to ensure the changes are propagated to all
|
||||
# coordinators.
|
||||
for impalad in self.cluster.impalads:
|
||||
clients.append(impalad.service.create_beeswax_client())
|
||||
else:
|
||||
clients.append(self.client)
|
||||
try:
|
||||
self.client.execute("create role %s" % unique_role)
|
||||
self.client.execute("grant role %s to group `%s`" % (unique_role, group_name))
|
||||
self.client.execute("grant refresh on server to %s" % unique_role)
|
||||
|
||||
self.validate_refresh_authorization_roles(unique_role, query_options, clients)
|
||||
self.validate_refresh_authorization_privileges(unique_role, query_options,
|
||||
clients)
|
||||
finally:
|
||||
self.role_cleanup(unique_role)
|
||||
|
||||
def validate_refresh_authorization_roles(self, unique_role, query_options, clients):
|
||||
"""This method tests refresh authorization statement by adding and removing
|
||||
roles externally."""
|
||||
try:
|
||||
# Create two roles inside Impala.
|
||||
self.client.execute("create role %s_internal1" % unique_role)
|
||||
self.client.execute("create role %s_internal2" % unique_role)
|
||||
# Drop an existing role (_internal1) outside Impala.
|
||||
role = "%s_internal1" % unique_role
|
||||
subprocess.check_call(
|
||||
["/bin/bash", "-c",
|
||||
"%s/bin/sentryShell --conf %s/sentry-site.xml -dr -r %s" %
|
||||
(os.getenv("SENTRY_HOME"), os.getenv("SENTRY_CONF_DIR"), role)],
|
||||
stdout=sys.stdout, stderr=sys.stderr)
|
||||
|
||||
result = self.execute_query_expect_success(self.client, "show roles")
|
||||
assert any(role in x for x in result.data)
|
||||
self.execute_query_expect_success(self.client, "refresh authorization",
|
||||
query_options=query_options)
|
||||
for client in clients:
|
||||
result = self.execute_query_expect_success(client, "show roles")
|
||||
assert not any(role in x for x in result.data)
|
||||
|
||||
# Add a new role outside Impala.
|
||||
role = "%s_external" % unique_role
|
||||
subprocess.check_call(
|
||||
["/bin/bash", "-c",
|
||||
"%s/bin/sentryShell --conf %s/sentry-site.xml -cr -r %s" %
|
||||
(os.getenv("SENTRY_HOME"), os.getenv("SENTRY_CONF_DIR"), role)],
|
||||
stdout=sys.stdout, stderr=sys.stderr)
|
||||
|
||||
result = self.execute_query_expect_success(self.client, "show roles")
|
||||
assert not any(role in x for x in result.data)
|
||||
self.execute_query_expect_success(self.client, "refresh authorization",
|
||||
query_options=query_options)
|
||||
for client in clients:
|
||||
result = self.execute_query_expect_success(client, "show roles")
|
||||
assert any(role in x for x in result.data)
|
||||
finally:
|
||||
for suffix in ["internal1", "internal2", "external"]:
|
||||
self.role_cleanup("%s_%s" % (unique_role, suffix))
|
||||
|
||||
def validate_refresh_authorization_privileges(self, unique_role, query_options,
|
||||
clients):
|
||||
"""This method tests refresh authorization statement by adding and removing
|
||||
privileges externally."""
|
||||
# Grant select privilege outside Impala.
|
||||
subprocess.check_call(
|
||||
["/bin/bash", "-c",
|
||||
"%s/bin/sentryShell --conf %s/sentry-site.xml -gpr -p "
|
||||
"'server=server1->db=functional->table=alltypes->action=select' -r %s" %
|
||||
(os.getenv("SENTRY_HOME"), os.getenv("SENTRY_CONF_DIR"), unique_role)],
|
||||
stdout=sys.stdout, stderr=sys.stderr)
|
||||
|
||||
# Before refresh authorization, there should only be one refresh privilege.
|
||||
result = self.execute_query_expect_success(self.client, "show grant role %s" %
|
||||
unique_role)
|
||||
assert len(result.data) == 1
|
||||
assert any("refresh" in x for x in result.data)
|
||||
|
||||
for client in clients:
|
||||
self.execute_query_expect_failure(client,
|
||||
"select * from functional.alltypes limit 1")
|
||||
|
||||
self.execute_query_expect_success(self.client, "refresh authorization",
|
||||
query_options=query_options)
|
||||
|
||||
for client in clients:
|
||||
# Ensure select privilege was granted after refresh authorization.
|
||||
result = self.execute_query_expect_success(client, "show grant role %s" %
|
||||
unique_role)
|
||||
assert len(result.data) == 2
|
||||
assert any("select" in x for x in result.data)
|
||||
assert any("refresh" in x for x in result.data)
|
||||
self.execute_query_expect_success(client,
|
||||
"select * from functional.alltypes limit 1")
|
||||
|
||||
@staticmethod
|
||||
def _verify_show_dbs(result, unique_name, visibility_privileges=PRIVILEGES):
|
||||
""" Helper function for verifying the results of SHOW DATABASES below.
|
||||
@@ -479,98 +113,6 @@ class TestAuthorization(CustomClusterTestSuite):
|
||||
else:
|
||||
assert db_name in result.data
|
||||
|
||||
def _test_sentry_show_stmts_helper(self, unique_role, unique_name,
|
||||
visibility_privileges):
|
||||
unique_db = unique_name + "_db"
|
||||
# TODO: can we create and use a temp username instead of using root?
|
||||
another_user = 'root'
|
||||
another_user_grp = 'root'
|
||||
self.role_cleanup(unique_role)
|
||||
try:
|
||||
self.client.execute("create role %s" % unique_role)
|
||||
self.client.execute("grant create on server to role %s" % unique_role)
|
||||
self.client.execute("grant drop on server to role %s" % unique_role)
|
||||
self.client.execute("grant role %s to group %s" %
|
||||
(unique_role, grp.getgrnam(getuser()).gr_name))
|
||||
|
||||
self.client.execute("drop database if exists %s cascade" % unique_db)
|
||||
self.client.execute("create database %s" % unique_db)
|
||||
for priv in PRIVILEGES:
|
||||
self.client.execute("create database db_%s_%s" % (unique_name, priv))
|
||||
self.client.execute("grant {0} on database db_{1}_{2} to role {3}"
|
||||
.format(priv, unique_name, priv, unique_role))
|
||||
self.client.execute("create table %s.tbl_%s (i int)" % (unique_db, priv))
|
||||
self.client.execute("grant {0} on table {1}.tbl_{2} to role {3}"
|
||||
.format(priv, unique_db, priv, unique_role))
|
||||
self.client.execute("grant role %s to group %s" %
|
||||
(unique_role, another_user_grp))
|
||||
|
||||
# Owner (current user) can still see all the owned databases and tables
|
||||
result = self.client.execute("show databases")
|
||||
TestAuthorization._verify_show_dbs(result, unique_name)
|
||||
result = self.client.execute("show tables in %s" % unique_db)
|
||||
assert result.data == ["tbl_%s" % p for p in PRIVILEGES]
|
||||
|
||||
# Check SHOW DATABASES and SHOW TABLES using another username
|
||||
# Create another client so we can user another username
|
||||
root_impalad_client = self.create_impala_client()
|
||||
result = self.execute_query_expect_success(
|
||||
root_impalad_client, "show databases", user=another_user)
|
||||
TestAuthorization._verify_show_dbs(result, unique_name, visibility_privileges)
|
||||
result = self.execute_query_expect_success(
|
||||
root_impalad_client, "show tables in %s" % unique_db, user=another_user)
|
||||
# Only show tables with privileges implying any of the visibility privileges
|
||||
assert 'tbl_all' in result.data # ALL can imply to any privilege
|
||||
for p in visibility_privileges:
|
||||
assert 'tbl_%s' % p in result.data
|
||||
finally:
|
||||
self.client.execute("drop database if exists %s cascade" % unique_db)
|
||||
for priv in PRIVILEGES:
|
||||
self.client.execute(
|
||||
"drop database if exists db_%s_%s cascade" % (unique_name, priv))
|
||||
self.role_cleanup(unique_role)
|
||||
|
||||
@SkipIf.sentry_disabled
|
||||
@pytest.mark.execute_serially
|
||||
@CustomClusterTestSuite.with_args(
|
||||
impalad_args="--server_name=server1 --sentry_config=%s "
|
||||
"--authorized_proxy_user_config=%s=* "
|
||||
"--min_privilege_set_for_show_stmts=select" %
|
||||
(SENTRY_CONFIG_FILE, getuser()),
|
||||
catalogd_args="--sentry_config={0}".format(SENTRY_CONFIG_FILE),
|
||||
sentry_config=SENTRY_CONFIG_FILE_OO, # Enable Sentry Object Ownership
|
||||
sentry_log_dir="{0}/test_sentry_show_stmts_with_select".format(SENTRY_BASE_LOG_DIR))
|
||||
def test_sentry_show_stmts_with_select(self, unique_role, unique_name):
|
||||
self._test_sentry_show_stmts_helper(unique_role, unique_name, ['select'])
|
||||
|
||||
@SkipIf.sentry_disabled
|
||||
@pytest.mark.execute_serially
|
||||
@CustomClusterTestSuite.with_args(
|
||||
impalad_args="--server_name=server1 --sentry_config=%s "
|
||||
"--authorized_proxy_user_config=%s=* "
|
||||
"--min_privilege_set_for_show_stmts=select,insert" %
|
||||
(SENTRY_CONFIG_FILE, getuser()),
|
||||
catalogd_args="--sentry_config={0}".format(SENTRY_CONFIG_FILE),
|
||||
sentry_config=SENTRY_CONFIG_FILE_OO, # Enable Sentry Object Ownership
|
||||
sentry_log_dir="{0}/test_sentry_show_stmts_with_select_insert"
|
||||
.format(SENTRY_BASE_LOG_DIR))
|
||||
def test_sentry_show_stmts_with_select_insert(self, unique_role, unique_name):
|
||||
self._test_sentry_show_stmts_helper(unique_role, unique_name,
|
||||
['select', 'insert'])
|
||||
|
||||
@SkipIf.sentry_disabled
|
||||
@pytest.mark.execute_serially
|
||||
@CustomClusterTestSuite.with_args(
|
||||
impalad_args="--server_name=server1 --sentry_config=%s "
|
||||
"--authorized_proxy_user_config=%s=* "
|
||||
"--min_privilege_set_for_show_stmts=any" %
|
||||
(SENTRY_CONFIG_FILE, getuser()),
|
||||
catalogd_args="--sentry_config={0}".format(SENTRY_CONFIG_FILE),
|
||||
sentry_config=SENTRY_CONFIG_FILE_OO, # Enable Sentry Object Ownership
|
||||
sentry_log_dir="{0}/test_sentry_show_stmts_with_any".format(SENTRY_BASE_LOG_DIR))
|
||||
def test_sentry_show_stmts_with_any(self, unique_role, unique_name):
|
||||
self._test_sentry_show_stmts_helper(unique_role, unique_name, PRIVILEGES)
|
||||
|
||||
def _test_ranger_show_stmts_helper(self, unique_name, visibility_privileges):
|
||||
unique_db = unique_name + "_db"
|
||||
admin_client = self.create_impala_client()
|
||||
@@ -644,17 +186,3 @@ class TestAuthorization(CustomClusterTestSuite):
|
||||
"--ranger_app_id=impala --authorization_provider=ranger")
|
||||
def test_num_check_authorization_threads_with_ranger(self, unique_name):
|
||||
self._test_ranger_show_stmts_helper(unique_name, PRIVILEGES)
|
||||
|
||||
@SkipIf.sentry_disabled
|
||||
@pytest.mark.execute_serially
|
||||
@CustomClusterTestSuite.with_args(
|
||||
impalad_args="--server_name=server1 --sentry_config=%s "
|
||||
"--authorized_proxy_user_config=%s=* "
|
||||
"--num_check_authorization_threads=%d" %
|
||||
(SENTRY_CONFIG_FILE, getuser(), random.randint(2, 128)),
|
||||
catalogd_args="--sentry_config={0}".format(SENTRY_CONFIG_FILE),
|
||||
sentry_config=SENTRY_CONFIG_FILE_OO, # Enable Sentry Object Ownership
|
||||
sentry_log_dir="{0}/test_num_check_authorization_threads_with_sentry"
|
||||
.format(SENTRY_BASE_LOG_DIR))
|
||||
def test_num_check_authorization_threads_with_sentry(self, unique_role, unique_name):
|
||||
self._test_sentry_show_stmts_helper(unique_role, unique_name, PRIVILEGES)
|
||||
|
||||
@@ -35,13 +35,6 @@ from tests.hs2.hs2_test_suite import operation_id_to_query_id
|
||||
|
||||
AUDIT_LOG_DIR = tempfile.mkdtemp(dir=os.getenv("LOG_DIR"))
|
||||
|
||||
SENTRY_CONFIG_FILE = "{0}/fe/src/test/resources/sentry-site.xml" \
|
||||
.format(os.getenv("IMPALA_HOME"))
|
||||
SENTRY_IMPALAD_ARGS = "--server-name=server1 " \
|
||||
"--abort_on_failed_audit_event=false " \
|
||||
"--audit_event_log_dir={0}".format(AUDIT_LOG_DIR)
|
||||
SENTRY_CATALOGD_ARGS = "--sentry_config={0}".format(SENTRY_CONFIG_FILE)
|
||||
|
||||
RANGER_IMPALAD_ARGS = "--server-name=server1 " \
|
||||
"--ranger_service_type=hive " \
|
||||
"--ranger_app_id=impala " \
|
||||
@@ -105,17 +98,6 @@ class TestAuthorizedProxy(CustomClusterTestSuite):
|
||||
TestHS2.check_response(resp)
|
||||
return resp
|
||||
|
||||
@SkipIf.sentry_disabled
|
||||
@pytest.mark.execute_serially
|
||||
@CustomClusterTestSuite.with_args(
|
||||
impalad_args="{0} --authorized_proxy_user_config=foo=bar;hue={1} "
|
||||
.format(SENTRY_IMPALAD_ARGS, getuser()),
|
||||
catalogd_args=SENTRY_CATALOGD_ARGS)
|
||||
def test_authorized_proxy_user_with_sentry(self, unique_role):
|
||||
"""Tests authorized proxy user with Sentry using HS2."""
|
||||
self._test_authorized_proxy_with_sentry(unique_role, self._test_authorized_proxy,
|
||||
getuser())
|
||||
|
||||
@pytest.mark.execute_serially
|
||||
@CustomClusterTestSuite.with_args(
|
||||
impalad_args="{0} --authorized_proxy_user_config=foo=bar;hue=non_owner "
|
||||
@@ -126,18 +108,6 @@ class TestAuthorizedProxy(CustomClusterTestSuite):
|
||||
self._test_authorized_proxy_with_ranger(self._test_authorized_proxy, "non_owner",
|
||||
False)
|
||||
|
||||
@SkipIf.sentry_disabled
|
||||
@pytest.mark.execute_serially
|
||||
@CustomClusterTestSuite.with_args(
|
||||
impalad_args="{0} --authorized_proxy_user_config=hue=bar "
|
||||
"--authorized_proxy_group_config=foo=bar;hue={1}"
|
||||
.format(SENTRY_IMPALAD_ARGS, grp.getgrgid(os.getgid()).gr_name),
|
||||
catalogd_args=SENTRY_CATALOGD_ARGS)
|
||||
def test_authorized_proxy_group_with_sentry(self, unique_role):
|
||||
"""Tests authorized proxy group with Sentry using HS2."""
|
||||
self._test_authorized_proxy_with_sentry(unique_role, self._test_authorized_proxy,
|
||||
getuser())
|
||||
|
||||
@pytest.mark.execute_serially
|
||||
@CustomClusterTestSuite.with_args(
|
||||
impalad_args="{0} --authorized_proxy_user_config=hue=non_owner "
|
||||
@@ -150,15 +120,6 @@ class TestAuthorizedProxy(CustomClusterTestSuite):
|
||||
self._test_authorized_proxy_with_ranger(self._test_authorized_proxy, "non_owner",
|
||||
True)
|
||||
|
||||
@SkipIf.sentry_disabled
|
||||
@pytest.mark.execute_serially
|
||||
@CustomClusterTestSuite.with_args(
|
||||
impalad_args="{0} --authorized_proxy_user_config=foo=bar "
|
||||
"--authorized_proxy_group_config=foo=bar".format(SENTRY_IMPALAD_ARGS),
|
||||
catalogd_args=SENTRY_CATALOGD_ARGS)
|
||||
def test_no_matching_user_and_group_authorized_proxy_with_sentry(self):
|
||||
self._test_no_matching_user_and_group_authorized_proxy()
|
||||
|
||||
@pytest.mark.execute_serially
|
||||
@CustomClusterTestSuite.with_args(
|
||||
impalad_args="{0} --authorized_proxy_user_config=foo=bar "
|
||||
@@ -175,24 +136,6 @@ class TestAuthorizedProxy(CustomClusterTestSuite):
|
||||
resp = self.hs2_client.OpenSession(open_session_req)
|
||||
assert "User 'hue' is not authorized to delegate to 'abc'" in str(resp)
|
||||
|
||||
def _test_authorized_proxy_with_sentry(self, role, test_func, delegated_user):
|
||||
try:
|
||||
self.session_handle = self._open_hs2(getuser(), dict()).sessionHandle
|
||||
self._execute_hs2_stmt("create role {0}".format(role))
|
||||
self._execute_hs2_stmt("grant all on table tpch.lineitem to role {0}"
|
||||
.format(role))
|
||||
self._execute_hs2_stmt("grant role {0} to group {1}"
|
||||
.format(role, grp.getgrnam(getuser()).gr_name))
|
||||
self._execute_hs2_stmt("grant role {0} to group {1}"
|
||||
.format(role, grp.getgrgid(os.getgid()).gr_name))
|
||||
test_func(delegated_user)
|
||||
finally:
|
||||
self.session_handle = self._open_hs2(getuser(), dict()).sessionHandle
|
||||
self._execute_hs2_stmt("grant all on server to role {0}".format(role))
|
||||
self._execute_hs2_stmt("grant role {0} to group {1}"
|
||||
.format(role, grp.getgrnam(getuser()).gr_name))
|
||||
self._execute_hs2_stmt("drop role {0}".format(role))
|
||||
|
||||
def _test_authorized_proxy_with_ranger(self, test_func, delegated_user,
|
||||
delegated_to_group):
|
||||
try:
|
||||
|
||||
@@ -1,382 +0,0 @@
|
||||
# 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.
|
||||
#
|
||||
# Client tests for SQL statement authorization
|
||||
|
||||
import grp
|
||||
import pytest
|
||||
from getpass import getuser
|
||||
from os import getenv
|
||||
from time import sleep
|
||||
|
||||
from tests.common.test_dimensions import create_uncompressed_text_dimension
|
||||
from tests.common.sentry_cache_test_suite import SentryCacheTestSuite, TestObject
|
||||
from tests.common.skip import SkipIf
|
||||
from tests.util.calculation_util import get_random_id
|
||||
from tests.verifiers.metric_verifier import MetricVerifier
|
||||
|
||||
SENTRY_CONFIG_DIR = "%s/%s" % (getenv("IMPALA_HOME"), "fe/src/test/resources")
|
||||
SENTRY_CONFIG_FILE = "%s/sentry-site.xml" % SENTRY_CONFIG_DIR
|
||||
SENTRY_CONFIG_FILE_OO = "%s/sentry-site_oo.xml" % SENTRY_CONFIG_DIR
|
||||
|
||||
# Sentry long polling frequency to make Sentry refresh not run.
|
||||
SENTRY_LONG_POLLING_FREQUENCY_S = 3600
|
||||
|
||||
|
||||
@SkipIf.sentry_disabled
|
||||
class TestGrantRevoke(SentryCacheTestSuite):
|
||||
@classmethod
|
||||
def add_test_dimensions(cls):
|
||||
super(TestGrantRevoke, cls).add_test_dimensions()
|
||||
cls.ImpalaTestMatrix.add_dimension(
|
||||
create_uncompressed_text_dimension(cls.get_workload()))
|
||||
|
||||
@classmethod
|
||||
def get_workload(cls):
|
||||
return 'functional-query'
|
||||
|
||||
def setup_method(self, method):
|
||||
super(TestGrantRevoke, self).setup_method(method)
|
||||
self.__test_cleanup()
|
||||
|
||||
def teardown_method(self, method):
|
||||
self.__test_cleanup()
|
||||
super(TestGrantRevoke, self).teardown_method(method)
|
||||
|
||||
def __test_cleanup(self):
|
||||
# Clean up any old roles created by this test
|
||||
for role_name in self.client.execute("show roles").data:
|
||||
if 'grant_revoke_test' in role_name:
|
||||
self.client.execute("drop role %s" % role_name)
|
||||
|
||||
# Cleanup any other roles that were granted to this user.
|
||||
# TODO: Update Sentry Service config and authorization tests to use LocalGroupMapping
|
||||
# for resolving users -> groups. This way we can specify custom test users that don't
|
||||
# actually exist in the system.
|
||||
group_name = grp.getgrnam(getuser()).gr_name
|
||||
for role_name in self.client.execute("show role grant group `%s`" % group_name).data:
|
||||
self.client.execute("drop role %s" % role_name)
|
||||
|
||||
# Create a temporary admin user so we can actually view/clean up the test
|
||||
# db.
|
||||
try:
|
||||
self.client.execute("create role grant_revoke_test_admin")
|
||||
self.client.execute("grant all on server to grant_revoke_test_admin")
|
||||
self.client.execute("grant role grant_revoke_test_admin to group `%s`" % group_name)
|
||||
self.cleanup_db('grant_rev_db', sync_ddl=0)
|
||||
finally:
|
||||
self.client.execute("drop role grant_revoke_test_admin")
|
||||
|
||||
@classmethod
|
||||
def restart_first_impalad(cls):
|
||||
impalad = cls.cluster.impalads[0]
|
||||
impalad.restart()
|
||||
cls.client = impalad.service.create_beeswax_client()
|
||||
|
||||
@pytest.mark.execute_serially
|
||||
@SentryCacheTestSuite.with_args(
|
||||
impalad_args="--server_name=server1",
|
||||
catalogd_args="--sentry_config=" + SENTRY_CONFIG_FILE,
|
||||
sentry_config=SENTRY_CONFIG_FILE)
|
||||
def test_grant_revoke(self, vector):
|
||||
self.run_test_case('QueryTest/grant_revoke', vector, use_db="default")
|
||||
|
||||
@pytest.mark.execute_serially
|
||||
@SentryCacheTestSuite.with_args(
|
||||
impalad_args="--server_name=server1",
|
||||
catalogd_args="--sentry_config=" + SENTRY_CONFIG_FILE,
|
||||
sentry_config=SENTRY_CONFIG_FILE)
|
||||
def test_grant_revoke_kudu(self, vector):
|
||||
if getenv("KUDU_IS_SUPPORTED") == "false":
|
||||
pytest.skip("Kudu is not supported")
|
||||
self.run_test_case('QueryTest/grant_revoke_kudu', vector, use_db="default")
|
||||
|
||||
@pytest.mark.execute_serially
|
||||
@SentryCacheTestSuite.with_args(
|
||||
impalad_args="--server_name=server1",
|
||||
catalogd_args="--sentry_config={0} --sentry_catalog_polling_frequency_s={1}"
|
||||
.format(SENTRY_CONFIG_FILE, SENTRY_LONG_POLLING_FREQUENCY_S),
|
||||
sentry_config=SENTRY_CONFIG_FILE)
|
||||
def test_grant_revoke_with_catalog_v1(self):
|
||||
"""Tests grant and revoke using catalog v1."""
|
||||
self._test_grant_revoke()
|
||||
|
||||
@pytest.mark.execute_serially
|
||||
@SentryCacheTestSuite.with_args(
|
||||
impalad_args="--server_name=server1 --use_local_catalog=true",
|
||||
catalogd_args="--sentry_config={0} --sentry_catalog_polling_frequency_s={1} "
|
||||
"--use_local_catalog=true --catalog_topic_mode=minimal"
|
||||
.format(SENTRY_CONFIG_FILE, SENTRY_LONG_POLLING_FREQUENCY_S),
|
||||
sentry_config=SENTRY_CONFIG_FILE)
|
||||
def test_grant_revoke_with_local_catalog(self):
|
||||
"""Tests grant and revoke using local catalog mode."""
|
||||
self._test_grant_revoke()
|
||||
|
||||
def _test_grant_revoke(self):
|
||||
"""Tests grant and revoke for all objects. In these tests, we run tests twice. One
|
||||
with just using cache, hence the long sentry poll, and another one by ensuring
|
||||
refreshes happen from Sentry."""
|
||||
for refresh in [True, False]:
|
||||
for priv in ["all", "select"]:
|
||||
self._execute_with_grant_option_tests(
|
||||
TestObject(TestObject.SERVER), priv, refresh)
|
||||
self._execute_with_grant_option_tests(TestObject(
|
||||
TestObject.DATABASE, "grant_rev_db"), priv, refresh)
|
||||
self._execute_with_grant_option_tests(TestObject(
|
||||
TestObject.TABLE, "grant_rev_db.tbl1"), priv, refresh)
|
||||
self._execute_with_grant_option_tests(
|
||||
TestObject(TestObject.VIEW, "grant_rev_db.tbl1"), priv, refresh)
|
||||
|
||||
def _execute_with_grant_option_tests(self, test_obj, privilege, refresh_authorization):
|
||||
"""
|
||||
Executes grant/revoke tests with grant option.
|
||||
"""
|
||||
def setup():
|
||||
group_name = grp.getgrnam(getuser()).gr_name
|
||||
try:
|
||||
self.client.execute("create role grant_revoke_test_admin")
|
||||
except Exception:
|
||||
# Ignore this as it was already created on the last run.
|
||||
pass
|
||||
self.client.execute("grant all on server to grant_revoke_test_admin")
|
||||
self.client.execute("grant role grant_revoke_test_admin to group `%s`" % group_name)
|
||||
self.client.execute("create role grant_revoke_test_role")
|
||||
|
||||
def cleanup():
|
||||
# Reset the grant value
|
||||
test_obj.grant = False
|
||||
# Remove the role
|
||||
self.client.execute("drop role grant_revoke_test_role")
|
||||
|
||||
try:
|
||||
setup()
|
||||
if test_obj.obj_type != TestObject.SERVER:
|
||||
self.user_query(self.client, "create %s if not exists %s %s %s" %
|
||||
(test_obj.obj_type, test_obj.obj_name, test_obj.table_def,
|
||||
test_obj.view_select), user="root")
|
||||
|
||||
# Grant a basic privilege
|
||||
self.user_query(self.client, "grant %s on %s %s to role grant_revoke_test_role" %
|
||||
(privilege, test_obj.grant_name, test_obj.obj_name), user="root")
|
||||
|
||||
# Ensure role has privilege.
|
||||
self.validate_privileges(self.client, "show grant role grant_revoke_test_role",
|
||||
test_obj, refresh_authorization=refresh_authorization)
|
||||
|
||||
# Try with grant option on existing privilege.
|
||||
test_obj.grant = True
|
||||
self.user_query(self.client,
|
||||
"grant %s on %s %s to role grant_revoke_test_role with grant option"
|
||||
% (privilege, test_obj.grant_name, test_obj.obj_name), user="root")
|
||||
# Ensure role has updated privilege.
|
||||
self.validate_privileges(self.client, "show grant role grant_revoke_test_role",
|
||||
test_obj, refresh_authorization=refresh_authorization)
|
||||
|
||||
# Revoke the grant option
|
||||
self.user_query(self.client, "revoke grant option for %s on %s %s from role "
|
||||
"grant_revoke_test_role" % (privilege,
|
||||
test_obj.grant_name,
|
||||
test_obj.obj_name))
|
||||
# Ensure role has updated privilege.
|
||||
test_obj.grant = False
|
||||
self.validate_privileges(self.client, "show grant role grant_revoke_test_role",
|
||||
test_obj, refresh_authorization=refresh_authorization)
|
||||
|
||||
# Add the grant option back, then add a regular privilege
|
||||
self.user_query(self.client,
|
||||
"grant %s on %s %s to role grant_revoke_test_role with grant option"
|
||||
% (privilege, test_obj.grant_name, test_obj.obj_name), user="root")
|
||||
self.user_query(self.client, "grant %s on %s %s to role grant_revoke_test_role" %
|
||||
(privilege, test_obj.grant_name, test_obj.obj_name), user="root")
|
||||
test_obj.grant = True
|
||||
self.validate_privileges(self.client, "show grant role grant_revoke_test_role",
|
||||
test_obj, refresh_authorization=refresh_authorization)
|
||||
|
||||
# Revoke the privilege
|
||||
self.user_query(self.client, "revoke %s on %s %s from role grant_revoke_test_role" %
|
||||
(privilege, test_obj.grant_name, test_obj.obj_name))
|
||||
result = self.user_query(self.client, "show grant role grant_revoke_test_role")
|
||||
assert len(result.data) == 0
|
||||
finally:
|
||||
cleanup()
|
||||
|
||||
@pytest.mark.execute_serially
|
||||
@SentryCacheTestSuite.with_args(
|
||||
impalad_args="--server_name=server1",
|
||||
catalogd_args="--sentry_config=" + SENTRY_CONFIG_FILE +
|
||||
" --sentry_catalog_polling_frequency_s=1",
|
||||
sentry_config=SENTRY_CONFIG_FILE)
|
||||
def test_role_privilege_case(self, vector):
|
||||
"""IMPALA-5582: Store sentry privileges in lower case. This
|
||||
test grants select privileges to roles assgined to tables/db
|
||||
specified in lower, upper and mix cases. This test verifies
|
||||
that these privileges do not vanish on a sentryProxy thread
|
||||
update.
|
||||
"""
|
||||
db_name = "test_role_privilege_case_x_" + get_random_id(5)
|
||||
db_name_upper_case = "TEST_ROLE_PRIVILEGE_CASE_Y_" + get_random_id(5).upper()
|
||||
db_name_mixed_case = "TesT_Role_PRIVIlege_case_z" + get_random_id(5)
|
||||
role_name = "test_role_" + get_random_id(5)
|
||||
try:
|
||||
self.client.execute("create role {0}".format(role_name))
|
||||
self.client.execute("grant all on server to {0}".format(role_name))
|
||||
self.client.execute("grant role {0} to group `{1}`".format(role_name,
|
||||
grp.getgrnam(getuser()).gr_name))
|
||||
|
||||
self.client.execute("create database " + db_name)
|
||||
self.client.execute("create database " + db_name_upper_case)
|
||||
self.client.execute("create database " + db_name_mixed_case)
|
||||
self.client.execute(
|
||||
"create table if not exists {0}.test1(i int)".format(db_name))
|
||||
self.client.execute("create table if not exists {0}.TEST2(i int)".format(db_name))
|
||||
self.client.execute("create table if not exists {0}.Test3(i int)".format(db_name))
|
||||
|
||||
self.client.execute(
|
||||
"grant select on table {0}.test1 to {1}".format(db_name, role_name))
|
||||
self.client.execute(
|
||||
"grant select on table {0}.TEST2 to {1}".format(db_name, role_name))
|
||||
self.client.execute(
|
||||
"grant select on table {0}.TesT3 to {1}".format(db_name, role_name))
|
||||
self.client.execute("grant all on database {0} to {1}".format(db_name, role_name))
|
||||
self.client.execute(
|
||||
"grant all on database {0} to {1}".format(db_name_upper_case, role_name))
|
||||
self.client.execute(
|
||||
"grant all on database {0} to {1}".format(db_name_mixed_case, role_name))
|
||||
result = self.client.execute("show grant role {0}".format(role_name))
|
||||
assert any('test1' in x for x in result.data)
|
||||
assert any('test2' in x for x in result.data)
|
||||
assert any('test3' in x for x in result.data)
|
||||
assert any(db_name_upper_case.lower() in x for x in result.data)
|
||||
assert any(db_name_mixed_case.lower() in x for x in result.data)
|
||||
# Sleep for 2 seconds and make sure that the privileges
|
||||
# on all 3 tables still persist on a sentryProxy thread
|
||||
# update. sentry_catalog_polling_frequency_s is set to 1
|
||||
# seconds.
|
||||
sleep(2)
|
||||
result = self.client.execute("show grant role {0}".format(role_name))
|
||||
assert any('test1' in x for x in result.data)
|
||||
assert any('test2' in x for x in result.data)
|
||||
assert any('test3' in x for x in result.data)
|
||||
assert any(db_name_upper_case.lower() in x for x in result.data)
|
||||
assert any(db_name_mixed_case.lower() in x for x in result.data)
|
||||
finally:
|
||||
self.client.execute("drop database if exists {0}".format(db_name_upper_case))
|
||||
self.client.execute("drop database if exists {0}".format(db_name_mixed_case))
|
||||
self.client.execute("drop database if exists {0} cascade".format(db_name))
|
||||
self.client.execute("drop role {0}".format(role_name))
|
||||
|
||||
@pytest.mark.execute_serially
|
||||
@SentryCacheTestSuite.with_args(
|
||||
impalad_args="--server_name=server1",
|
||||
catalogd_args="--sentry_config=" + SENTRY_CONFIG_FILE,
|
||||
sentry_config=SENTRY_CONFIG_FILE)
|
||||
def test_role_update(self, vector):
|
||||
"""IMPALA-5355: The initial update from the statestore has the privileges and roles in
|
||||
reverse order if a role was modified, but not the associated privilege. Verify that
|
||||
Impala is able to handle this.
|
||||
"""
|
||||
role_name = "test_role_" + get_random_id(5)
|
||||
try:
|
||||
self.client.execute("create role {0}".format(role_name))
|
||||
self.client.execute("grant all on server to {0}".format(role_name))
|
||||
# Wait a few seconds to make sure the update propagates to the statestore.
|
||||
sleep(3)
|
||||
# Update the role, increasing its catalog verion.
|
||||
self.client.execute("grant role {0} to group `{1}`".format(
|
||||
role_name, grp.getgrnam(getuser()).gr_name))
|
||||
result = self.client.execute("show tables in functional")
|
||||
assert 'alltypes' in result.data
|
||||
privileges_before = self.client.execute("show grant role {0}".format(role_name))
|
||||
# Wait a few seconds before restarting Impalad to make sure that the Catalog gets
|
||||
# updated.
|
||||
sleep(3)
|
||||
self.restart_first_impalad()
|
||||
verifier = MetricVerifier(self.cluster.impalads[0].service)
|
||||
verifier.wait_for_metric("catalog.ready", True)
|
||||
# Verify that we still have the right privileges after the first impalad was
|
||||
# restarted.
|
||||
result = self.client.execute("show tables in functional")
|
||||
assert 'alltypes' in result.data
|
||||
privileges_after = self.client.execute("show grant role {0}".format(role_name))
|
||||
assert privileges_before.data == privileges_after.data
|
||||
finally:
|
||||
self.client.execute("drop role {0}".format(role_name))
|
||||
|
||||
@pytest.mark.execute_serially
|
||||
@SentryCacheTestSuite.with_args(
|
||||
impalad_args="--server_name=server1",
|
||||
catalogd_args="--sentry_config={0}".format(SENTRY_CONFIG_FILE),
|
||||
sentry_config=SENTRY_CONFIG_FILE)
|
||||
def test_role_name_case_insensitive_invalidate_metadata(self, unique_role):
|
||||
"""IMPALA-7729: Tests running invalidate metadata with role names that have different
|
||||
case sensitivity."""
|
||||
for role_name in [unique_role.lower(), unique_role.upper(), unique_role.capitalize()]:
|
||||
try:
|
||||
self.client.execute("create role {0}".format(role_name))
|
||||
self.client.execute("grant all on server to {0}".format(role_name))
|
||||
self.client.execute("grant role {0} to group `{1}`".format(
|
||||
role_name, grp.getgrnam(getuser()).gr_name))
|
||||
|
||||
# Verify that running invalidate metadata won't hang due to case sensitivity
|
||||
# in the role names.
|
||||
handle = self.client.execute_async("invalidate metadata")
|
||||
assert self.client.wait_for_finished_timeout(handle, timeout=60)
|
||||
finally:
|
||||
self.client.execute("drop role {0}".format(role_name))
|
||||
|
||||
@pytest.mark.execute_serially
|
||||
@SentryCacheTestSuite.with_args(
|
||||
impalad_args="--server_name=server1",
|
||||
catalogd_args="--sentry_config={0}".format(SENTRY_CONFIG_FILE),
|
||||
sentry_config=SENTRY_CONFIG_FILE)
|
||||
def test_uri_privilege_case_sensitive(self, unique_role):
|
||||
"""Tests that revoking on a granted URI with a different case should not be
|
||||
allowed."""
|
||||
try:
|
||||
self.client.execute("create role {0}".format(unique_role))
|
||||
self.client.execute("grant role {0} to group `{1}`"
|
||||
.format(unique_role, grp.getgrnam(getuser()).gr_name))
|
||||
self.client.execute("grant refresh on server to {0}".format(unique_role))
|
||||
self.client.execute("grant all on uri '/test-warehouse/FOO' to {0}"
|
||||
.format(unique_role))
|
||||
self.client.execute("grant all on uri '/test-warehouse/foo' to {0}"
|
||||
.format(unique_role))
|
||||
result = self.client.execute("show grant role {0}".format(unique_role))
|
||||
|
||||
# Both URIs should exist.
|
||||
assert any("/test-warehouse/FOO" in x for x in result.data)
|
||||
assert any("/test-warehouse/foo" in x for x in result.data)
|
||||
|
||||
self.client.execute("refresh authorization")
|
||||
# After refresh authorization, we should expect both URIs to still exist.
|
||||
assert any("/test-warehouse/FOO" in x for x in result.data)
|
||||
assert any("/test-warehouse/foo" in x for x in result.data)
|
||||
|
||||
self.client.execute("revoke all on uri '/test-warehouse/foo' from {0}"
|
||||
.format(unique_role))
|
||||
|
||||
result = self.client.execute("show grant role {0}".format(unique_role))
|
||||
# Only one URI should exist.
|
||||
assert any("/test-warehouse/FOO" in x for x in result.data)
|
||||
assert not any("/test-warehouse/foo" in x for x in result.data)
|
||||
|
||||
self.client.execute("refresh authorization")
|
||||
# After refresh authorization, the one URI should still exist.
|
||||
assert any("/test-warehouse/FOO" in x for x in result.data)
|
||||
assert not any("/test-warehouse/foo" in x for x in result.data)
|
||||
finally:
|
||||
self.client.execute("drop role {0}".format(unique_role))
|
||||
@@ -1,95 +0,0 @@
|
||||
# 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.
|
||||
|
||||
import grp
|
||||
import pytest
|
||||
import os
|
||||
from getpass import getuser
|
||||
|
||||
from tests.common.custom_cluster_test_suite import CustomClusterTestSuite
|
||||
from tests.common.sentry_cache_test_suite import SentryCacheTestSuite
|
||||
from tests.common.skip import SkipIf
|
||||
|
||||
SENTRY_CONFIG_DIR = os.getenv('IMPALA_HOME') + '/fe/src/test/resources/'
|
||||
SENTRY_CONFIG_FILE = SENTRY_CONFIG_DIR + 'sentry-site.xml'
|
||||
|
||||
|
||||
@SkipIf.sentry_disabled
|
||||
class TestSentry(CustomClusterTestSuite):
|
||||
"""This class contains Sentry specific authorization tests."""
|
||||
|
||||
@pytest.mark.execute_serially
|
||||
@SentryCacheTestSuite.with_args(
|
||||
impalad_args="--server_name=server1",
|
||||
catalogd_args="--sentry_config=" + SENTRY_CONFIG_FILE,
|
||||
sentry_config=SENTRY_CONFIG_FILE)
|
||||
def test_grant_revoke_invalid_role(self, unique_name):
|
||||
role_name = "foobar"
|
||||
group = grp.getgrnam(getuser()).gr_name
|
||||
try:
|
||||
# This will create "foobar" role catalog object.
|
||||
self.client.execute("create role {0}".format(role_name))
|
||||
self.client.execute("grant all on server to {0}".format(role_name))
|
||||
self.client.execute("grant role {0} to group `{1}`".format(role_name, group))
|
||||
self.client.execute("create database {0}".format(unique_name))
|
||||
|
||||
ex = self.execute_query_expect_failure(
|
||||
self.client, "grant all on database {0} to role non_role".format(unique_name))
|
||||
assert "Role 'non_role' does not exist." in str(ex)
|
||||
|
||||
ex = self.execute_query_expect_failure(
|
||||
self.client, "revoke all on database {0} from role non_role".format(unique_name))
|
||||
assert "Role 'non_role' does not exist." in str(ex)
|
||||
|
||||
ex = self.execute_query_expect_failure(self.client, "show grant role non_role")
|
||||
assert "Role 'non_role' does not exist." in str(ex)
|
||||
|
||||
ex = self.execute_query_expect_failure(
|
||||
self.client, "grant role non_role to group `{0}`".format(group))
|
||||
assert "Role 'non_role' does not exist." in str(ex)
|
||||
|
||||
ex = self.execute_query_expect_failure(self.client, "drop role non_role")
|
||||
assert "Role 'non_role' does not exist." in str(ex)
|
||||
|
||||
ex = self.execute_query_expect_failure(self.client,
|
||||
"create role {0}".format(role_name))
|
||||
assert "Role '{0}' already exists.".format(role_name) in str(ex)
|
||||
finally:
|
||||
self.client.execute("drop database {0}".format(unique_name))
|
||||
self.client.execute("drop role {0}".format(role_name))
|
||||
|
||||
@pytest.mark.execute_serially
|
||||
@CustomClusterTestSuite.with_args(
|
||||
impalad_args="--server_name=server1 --sentry_config={0}".format(SENTRY_CONFIG_FILE),
|
||||
catalogd_args="--sentry_config={0}".format(SENTRY_CONFIG_FILE))
|
||||
def test_unsupported_sql(self):
|
||||
"""Tests unsupported SQL statements when running with Sentry."""
|
||||
user = getuser()
|
||||
impala_client = self.create_impala_client()
|
||||
error_msg = "UnsupportedFeatureException: {0} is not supported by Sentry."
|
||||
statements = [("grant select on database functional to user foo",
|
||||
error_msg.format("GRANT <privilege> TO USER")),
|
||||
("grant select on database functional to group foo",
|
||||
error_msg.format("GRANT <privilege> TO GROUP")),
|
||||
("revoke select on database functional from user foo",
|
||||
error_msg.format("REVOKE <privilege> FROM USER")),
|
||||
("revoke select on database functional from group foo",
|
||||
error_msg.format("REVOKE <privilege> FROM GROUP")),
|
||||
("show grant group foo", error_msg.format("SHOW GRANT GROUP"))]
|
||||
for statement in statements:
|
||||
result = self.execute_query_expect_failure(impala_client, statement[0], user=user)
|
||||
assert statement[1] in str(result)
|
||||
@@ -41,8 +41,6 @@ CATALOGD_ARGS = 'catalogd_args'
|
||||
KUDU_ARGS = 'kudu_args'
|
||||
# Additional args passed to the start-impala-cluster script.
|
||||
START_ARGS = 'start_args'
|
||||
SENTRY_CONFIG = 'sentry_config'
|
||||
SENTRY_LOG_DIR = 'sentry_log_dir'
|
||||
HIVE_CONF_DIR = 'hive_conf_dir'
|
||||
CLUSTER_SIZE = "cluster_size"
|
||||
# Default query options passed to the impala daemon command line. Handled separately from
|
||||
@@ -103,8 +101,8 @@ class CustomClusterTestSuite(ImpalaTestSuite):
|
||||
|
||||
@staticmethod
|
||||
def with_args(impalad_args=None, statestored_args=None, catalogd_args=None,
|
||||
start_args=None, sentry_config=None, default_query_options=None,
|
||||
impala_log_dir=None, sentry_log_dir=None, hive_conf_dir=None, cluster_size=None,
|
||||
start_args=None, default_query_options=None,
|
||||
impala_log_dir=None, hive_conf_dir=None, cluster_size=None,
|
||||
num_exclusive_coordinators=None, kudu_args=None, statestored_timeout_s=None,
|
||||
impalad_timeout_s=None):
|
||||
"""Records arguments to be passed to a cluster by adding them to the decorated
|
||||
@@ -117,10 +115,6 @@ class CustomClusterTestSuite(ImpalaTestSuite):
|
||||
func.func_dict[CATALOGD_ARGS] = catalogd_args
|
||||
if start_args is not None:
|
||||
func.func_dict[START_ARGS] = start_args.split()
|
||||
if sentry_config is not None:
|
||||
func.func_dict[SENTRY_CONFIG] = sentry_config
|
||||
if sentry_log_dir is not None:
|
||||
func.func_dict[SENTRY_LOG_DIR] = sentry_log_dir
|
||||
if hive_conf_dir is not None:
|
||||
func.func_dict[HIVE_CONF_DIR] = hive_conf_dir
|
||||
if kudu_args is not None:
|
||||
@@ -159,10 +153,6 @@ class CustomClusterTestSuite(ImpalaTestSuite):
|
||||
if KUDU_ARGS in method.func_dict:
|
||||
self._restart_kudu_service(method.func_dict[KUDU_ARGS])
|
||||
|
||||
if SENTRY_CONFIG in method.func_dict:
|
||||
self._start_sentry_service(method.func_dict[SENTRY_CONFIG],
|
||||
method.func_dict.get(SENTRY_LOG_DIR))
|
||||
|
||||
cluster_size = DEFAULT_CLUSTER_SIZE
|
||||
if CLUSTER_SIZE in method.func_dict:
|
||||
cluster_size = method.func_dict[CLUSTER_SIZE]
|
||||
@@ -216,26 +206,6 @@ class CustomClusterTestSuite(ImpalaTestSuite):
|
||||
if call.returncode != 0:
|
||||
raise RuntimeError("Unable to restart Kudu")
|
||||
|
||||
@classmethod
|
||||
def _start_sentry_service(cls, sentry_service_config, sentry_log_dir=None):
|
||||
sentry_env = dict(os.environ)
|
||||
if sentry_log_dir is not None:
|
||||
sentry_env['SENTRY_LOG_DIR'] = sentry_log_dir
|
||||
sentry_env['SENTRY_SERVICE_CONFIG'] = sentry_service_config
|
||||
call = subprocess.Popen(
|
||||
['/bin/bash', '-c', os.path.join(IMPALA_HOME,
|
||||
'testdata/bin/run-sentry-service.sh')],
|
||||
env=sentry_env)
|
||||
call.wait()
|
||||
if call.returncode != 0:
|
||||
raise RuntimeError("Unable to start Sentry")
|
||||
|
||||
@classmethod
|
||||
def _stop_sentry_service(cls):
|
||||
subprocess.check_call([os.path.join(os.environ["IMPALA_HOME"],
|
||||
"testdata/bin/kill-sentry-service.sh")],
|
||||
close_fds=True)
|
||||
|
||||
@classmethod
|
||||
def _start_hive_service(cls, hive_conf_dir):
|
||||
hive_env = dict(os.environ)
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
# 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.
|
||||
#
|
||||
# Base class for test that need to run with both just privilege
|
||||
# cache as well as Sentry privilege refresh.
|
||||
|
||||
from tests.common.custom_cluster_test_suite import CustomClusterTestSuite
|
||||
from tests.util.filesystem_utils import FILESYSTEM_PREFIX
|
||||
|
||||
class SentryCacheTestSuite(CustomClusterTestSuite):
|
||||
@staticmethod
|
||||
def str_to_bool(val):
|
||||
return val.lower() == 'true'
|
||||
|
||||
@staticmethod
|
||||
def check_privileges(result, test_obj, null_create_date, show_user):
|
||||
"""
|
||||
This method validates privileges. If the query is for "show grant user" then we need
|
||||
to account for the two extra columns.
|
||||
"""
|
||||
# results should have the following columns for offset 0
|
||||
# scope, database, table, column, uri, privilege, grant_option, create_time
|
||||
# results should have the following columns for offset 2
|
||||
# principal name, principal type, scope, database, table, column, uri, privilege,
|
||||
# grant_option, create_time
|
||||
for row in result.data:
|
||||
col = row.split('\t')
|
||||
# If we're running show grant user, we ignore any columns that have "ROLE".
|
||||
# This is because this method currently only validates single user privileges.
|
||||
if show_user and col[0] == 'ROLE':
|
||||
continue
|
||||
if show_user:
|
||||
col = col[2:]
|
||||
assert col[0] == test_obj.grant_name
|
||||
assert col[1] == test_obj.db_name
|
||||
if test_obj.table_name is not None and len(test_obj.table_name) > 0:
|
||||
assert col[2] == test_obj.table_name
|
||||
assert SentryCacheTestSuite.str_to_bool(col[6]) == test_obj.grant
|
||||
if not null_create_date and str(col[7]) == 'NULL':
|
||||
return False
|
||||
return True
|
||||
|
||||
def validate_privileges(self, client, query, test_obj, user=None,
|
||||
refresh_authorization=False):
|
||||
"""Validate privileges. When refresh_authorization is set to True, this function
|
||||
will call "refresh authorization" to ensure the privileges get refreshed from Sentry.
|
||||
"""
|
||||
show_user = True if 'show grant user' in query else False
|
||||
if refresh_authorization: self.execute_query('refresh authorization')
|
||||
result = self.execute_query_expect_success(client, query, user=user)
|
||||
return SentryCacheTestSuite.check_privileges(result, test_obj,
|
||||
null_create_date=(not
|
||||
refresh_authorization),
|
||||
show_user=show_user)
|
||||
|
||||
def user_query(self, client, query, user=None, error_msg=None):
|
||||
"""
|
||||
Executes a query with the specified user client. If error_msg is set, then expect a
|
||||
failure. Returns None when there is no error_msg.
|
||||
"""
|
||||
if error_msg is not None:
|
||||
e = self.execute_query_expect_failure(client, query, query_options={"sync_ddl": 1},
|
||||
user=user)
|
||||
SentryCacheTestSuite.verify_exceptions(error_msg, str(e))
|
||||
return None
|
||||
return self.execute_query_expect_success(client, query, query_options={"sync_ddl": 1},
|
||||
user=user)
|
||||
|
||||
@staticmethod
|
||||
def verify_exceptions(expected_str, actual_str):
|
||||
"""
|
||||
Verifies that 'expected_str' is a substring of the actual exception string
|
||||
'actual_str'.
|
||||
"""
|
||||
actual_str = actual_str.replace('\n', '')
|
||||
# Strip newlines so we can split error message into multiple lines
|
||||
expected_str = expected_str.replace('\n', '')
|
||||
if expected_str in actual_str: return
|
||||
assert False, 'Unexpected exception string. Expected: %s\nNot found in actual: %s' % \
|
||||
(expected_str, actual_str)
|
||||
|
||||
|
||||
class TestObject(object):
|
||||
SERVER = "server"
|
||||
DATABASE = "database"
|
||||
TABLE = "table"
|
||||
VIEW = "view"
|
||||
FUNCTION = "function"
|
||||
|
||||
def __init__(self, obj_type, obj_name="", grant=False):
|
||||
self.obj_name = obj_name
|
||||
self.obj_type = obj_type
|
||||
parts = obj_name.split(".")
|
||||
self.db_name = parts[0]
|
||||
self.table_name = None
|
||||
self.table_def = ""
|
||||
self.view_select = ""
|
||||
self.func_def = ""
|
||||
if len(parts) > 1:
|
||||
self.table_name = parts[1]
|
||||
if obj_type == TestObject.VIEW:
|
||||
self.grant_name = TestObject.TABLE
|
||||
self.view_select = "as select * from functional.alltypes"
|
||||
elif obj_type == TestObject.TABLE:
|
||||
self.grant_name = TestObject.TABLE
|
||||
self.table_def = "(col1 int)"
|
||||
elif obj_type == TestObject.FUNCTION:
|
||||
self.grant_name = TestObject.FUNCTION
|
||||
self.func_def = "RETURNS INT LOCATION '{0}/test-warehouse/libTestUdfs.so'\
|
||||
SYMBOL='Fn'".format(FILESYSTEM_PREFIX)
|
||||
else:
|
||||
self.grant_name = obj_type
|
||||
self.grant = grant
|
||||
@@ -127,8 +127,6 @@ class SkipIf:
|
||||
reason="Secondary filesystem needed")
|
||||
is_buggy_el6_kernel = pytest.mark.skipif(
|
||||
IS_BUGGY_EL6_KERNEL, reason="Kernel is affected by KUDU-1508")
|
||||
sentry_disabled = pytest.mark.skipif(os.getenv('DISABLE_SENTRY') == "true",
|
||||
reason="$DISABLE_SENTRY evaluates to true.")
|
||||
|
||||
|
||||
class SkipIfIsilon:
|
||||
@@ -213,8 +211,6 @@ class SkipIfDockerizedCluster:
|
||||
|
||||
|
||||
class SkipIfHive3:
|
||||
sentry_not_supported = pytest.mark.skipif(HIVE_MAJOR_VERSION >= 3,
|
||||
reason="Sentry HMS follower does not work with HMS-3. See SENTRY-2518 for details")
|
||||
kudu_hms_notifications_not_supported = pytest.mark.skipif(HIVE_MAJOR_VERSION >= 3,
|
||||
reason="Kudu is not tested with Hive 3 notifications yet, see IMPALA-8751.")
|
||||
col_stat_separated_by_engine = pytest.mark.skipif(HIVE_MAJOR_VERSION >= 3,
|
||||
|
||||
Reference in New Issue
Block a user