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:
Joe McDonnell
2020-04-12 12:39:24 -07:00
parent 95ee26354d
commit 3e76da9f51
81 changed files with 94 additions and 9078 deletions

View File

@@ -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}" | |

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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_;
};

View File

@@ -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);

View File

@@ -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 =

View File

@@ -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.

View File

@@ -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"),

View File

@@ -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. "

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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()

View File

@@ -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

View File

@@ -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"

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

@@ -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>

View File

@@ -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));

View File

@@ -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));

View File

@@ -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();
}
}
}

View File

@@ -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.

View File

@@ -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());

View File

@@ -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 {

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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();
}

View File

@@ -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_; }
}

View File

@@ -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_; }
}

View File

@@ -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);
}
}

View File

@@ -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_; }
}

View File

@@ -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_; }
}

View File

@@ -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_; }
}

View File

@@ -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_); }
}

View File

@@ -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);
}
}

View File

@@ -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_; }
}

View File

@@ -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;
}
}

View File

@@ -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.
}
}

View File

@@ -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();
}
}

View File

@@ -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_; }
}

View File

@@ -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));
}
}
}

View File

@@ -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);
}
}

View File

@@ -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());
}
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;
}
/**

View File

@@ -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.

View File

@@ -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());

View File

@@ -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.");

View File

@@ -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.

View File

@@ -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));
}
}

View File

@@ -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());

View File

@@ -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());
}
}

View File

@@ -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));
}
}
}
}

View File

@@ -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");

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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, "")));
}
}

View File

@@ -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',
})

View File

@@ -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]

View File

@@ -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>

View File

@@ -51,7 +51,6 @@ SERVICE_DEPENDENCIES = {
"HIVE" : True,
"YARN" : False,
"HBASE" : False,
"SENTRY" : False,
"ZOOKEEPER" : False
}

View File

@@ -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"

View File

@@ -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/"

View File

@@ -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

View File

@@ -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.'
====

View File

@@ -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.'
====

View File

@@ -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
====

View File

@@ -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)

View File

@@ -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:

View File

@@ -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))

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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,