You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@impala.apache.org by st...@apache.org on 2020/04/04 12:36:13 UTC
[impala] branch master updated: IMPALA-9350: Produce Ranger audits
for column masking
This is an automated email from the ASF dual-hosted git repository.
stigahuang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/impala.git
The following commit(s) were added to refs/heads/master by this push:
new d877dbc IMPALA-9350: Produce Ranger audits for column masking
d877dbc is described below
commit d877dbc572e95f086b37142e1d41231d5b6b7f9f
Author: Fang-Yu Rao <fa...@cloudera.com>
AuthorDate: Wed Mar 4 17:10:00 2020 -0800
IMPALA-9350: Produce Ranger audits for column masking
Support for column mask transformation was provided in IMPALA-9009.
However, the corresponding audit logs were not generated when a query
involved policies of column masking. This patch fixes the issue by
providing the Ranger plug-in with a non-null instance of
RangerBufferAuditHandler when calling evalDataMaskPolicies() so that
the corresponding audit log could be stored in the audit handler after
the evaluation of a policy involving column masking.
Testing:
- Added a FE test to verify the audit logs corresponding to column
masking are produced.
- Verified that the corresponding audit logs are indeed produced in a
3-node cluster with Ranger and Solr installed.
- Verified that the patch passed the exhaustive tests in the DEBUG build.
Change-Id: I9d8a1181234dcef580f68f56c24ad7e962cfe58e
Reviewed-on: http://gerrit.cloudera.org:8080/15412
Reviewed-by: Quanlong Huang <hu...@gmail.com>
Tested-by: Impala Public Jenkins <im...@cloudera.com>
---
.../apache/impala/analysis/AnalysisContext.java | 39 +++++---
.../java/org/apache/impala/analysis/Analyzer.java | 17 ++--
.../impala/analysis/CreateTableAsSelectStmt.java | 2 +-
.../org/apache/impala/analysis/FromClause.java | 1 -
.../org/apache/impala/analysis/InlineViewRef.java | 8 +-
.../impala/authorization/AuthorizationChecker.java | 4 +-
.../authorization/NoopAuthorizationFactory.java | 2 +-
.../org/apache/impala/authorization/TableMask.java | 7 +-
.../ranger/RangerAuthorizationChecker.java | 33 +++++--
.../sentry/SentryAuthorizationChecker.java | 2 +-
.../authorization/ranger/RangerAuditLogTest.java | 110 +++++++++++++++++++++
.../org/apache/impala/common/FrontendTestBase.java | 2 +-
12 files changed, 191 insertions(+), 36 deletions(-)
diff --git a/fe/src/main/java/org/apache/impala/analysis/AnalysisContext.java b/fe/src/main/java/org/apache/impala/analysis/AnalysisContext.java
index 836a2da..feddb4c 100644
--- a/fe/src/main/java/org/apache/impala/analysis/AnalysisContext.java
+++ b/fe/src/main/java/org/apache/impala/analysis/AnalysisContext.java
@@ -390,7 +390,12 @@ public class AnalysisContext {
}
public Analyzer createAnalyzer(StmtTableCache stmtTableCache) {
- Analyzer result = new Analyzer(stmtTableCache, queryCtx_, authzFactory_);
+ return createAnalyzer(stmtTableCache, null);
+ }
+
+ public Analyzer createAnalyzer(StmtTableCache stmtTableCache,
+ AuthorizationContext authzCtx) {
+ Analyzer result = new Analyzer(stmtTableCache, queryCtx_, authzFactory_, authzCtx);
result.setUseHiveColLabels(useHiveColLabels_);
return result;
}
@@ -411,8 +416,25 @@ public class AnalysisContext {
// Analyze statement and record exception.
AnalysisException analysisException = null;
+ TClientRequest clientRequest;
+ AuthorizationContext authzCtx = null;
+
try {
- analyze(stmtTableCache);
+ clientRequest = queryCtx_.getClient_request();
+ authzCtx = authzChecker.createAuthorizationContext(true,
+ clientRequest.isSetRedacted_stmt() ?
+ clientRequest.getRedacted_stmt() : clientRequest.getStmt(),
+ queryCtx_.getSession(), Optional.of(timeline_));
+ // TODO (IMPALA-9597): Generating column masking audit events in the analysis phase
+ // suffers from the drawback of losing the opportunity to control the final
+ // results. Redundant audits could be generated. For example, we will always
+ // generate audit events for column masking even though the requesting user is not
+ // granted the necessary privilege on the specified resource because
+ // AuthorizationChecker#postAuthorize() is always called whether there is an
+ // AuthorizationException or not. Another example is that if a table occurs several
+ // times in a query, we would have duplicate audits for the same column involved in
+ // a column masking policy.
+ analyze(stmtTableCache, authzCtx);
} catch (AnalysisException e) {
analysisException = e;
}
@@ -421,13 +443,7 @@ public class AnalysisContext {
// Authorize statement and record exception. Authorization relies on information
// collected during analysis.
AuthorizationException authException = null;
- AuthorizationContext authzCtx = null;
try {
- TClientRequest clientRequest = queryCtx_.getClient_request();
- authzCtx = authzChecker.createAuthorizationContext(true,
- clientRequest.isSetRedacted_stmt() ?
- clientRequest.getRedacted_stmt() : clientRequest.getStmt(),
- queryCtx_.getSession(), Optional.of(timeline_));
authzChecker.authorize(authzCtx, analysisResult_, catalog_);
} catch (AuthorizationException e) {
authException = e;
@@ -449,10 +465,11 @@ public class AnalysisContext {
* given loaded tables. Performs expr and subquery rewrites which require re-analyzing
* the transformed statement.
*/
- private void analyze(StmtTableCache stmtTableCache) throws AnalysisException {
+ private void analyze(StmtTableCache stmtTableCache, AuthorizationContext authzCtx)
+ throws AnalysisException {
Preconditions.checkNotNull(analysisResult_);
Preconditions.checkNotNull(analysisResult_.stmt_);
- analysisResult_.analyzer_ = createAnalyzer(stmtTableCache);
+ analysisResult_.analyzer_ = createAnalyzer(stmtTableCache, authzCtx);
analysisResult_.stmt_.analyze(analysisResult_.analyzer_);
// Enforce the statement expression limit at the end of analysis so that there is an
// accurate count of the total number of expressions. The first analyze() call is not
@@ -498,7 +515,7 @@ public class AnalysisContext {
analysisResult_.analyzer_.getPrivilegeReqs();
// Re-analyze the stmt with a new analyzer.
- analysisResult_.analyzer_ = createAnalyzer(stmtTableCache);
+ analysisResult_.analyzer_ = createAnalyzer(stmtTableCache, authzCtx);
// We restore the privileges collected in the first pass below. So, no point in
// collecting them again.
analysisResult_.analyzer_.setEnablePrivChecks(false);
diff --git a/fe/src/main/java/org/apache/impala/analysis/Analyzer.java b/fe/src/main/java/org/apache/impala/analysis/Analyzer.java
index f79e109..a0772d7 100644
--- a/fe/src/main/java/org/apache/impala/analysis/Analyzer.java
+++ b/fe/src/main/java/org/apache/impala/analysis/Analyzer.java
@@ -36,6 +36,7 @@ import org.apache.impala.analysis.Path.PathType;
import org.apache.impala.analysis.StmtMetadataLoader.StmtTableCache;
import org.apache.impala.authorization.AuthorizationChecker;
import org.apache.impala.authorization.AuthorizationConfig;
+import org.apache.impala.authorization.AuthorizationContext;
import org.apache.impala.authorization.AuthorizationFactory;
import org.apache.impala.authorization.Privilege;
import org.apache.impala.authorization.PrivilegeRequest;
@@ -323,6 +324,7 @@ public class Analyzer {
private static class GlobalState {
public final TQueryCtx queryCtx;
public final AuthorizationFactory authzFactory;
+ public final AuthorizationContext authzCtx;
public final DescriptorTable descTbl = new DescriptorTable();
public final IdGenerator<ExprId> conjunctIdGenerator = ExprId.createGenerator();
public final ColumnLineageGraph lineageGraph;
@@ -450,9 +452,10 @@ public class Analyzer {
private int numStmtExprs_ = 0;
public GlobalState(StmtTableCache stmtTableCache, TQueryCtx queryCtx,
- AuthorizationFactory authzFactory) {
+ AuthorizationFactory authzFactory, AuthorizationContext authzCtx) {
this.stmtTableCache = stmtTableCache;
this.queryCtx = queryCtx;
+ this.authzCtx = authzCtx;
this.authzFactory = authzFactory;
this.lineageGraph = new ColumnLineageGraph();
List<ExprRewriteRule> rules = new ArrayList<>();
@@ -469,7 +472,7 @@ public class Analyzer {
rules.add(ExtractCommonConjunctRule.INSTANCE);
if (queryCtx.getClient_request().getQuery_options().isEnable_cnf_rewrites()) {
rules.add(new ConvertToCNFRule(queryCtx.getClient_request().getQuery_options()
- .getMax_cnf_exprs(),true));
+ .getMax_cnf_exprs(),true));
}
// Relies on FoldConstantsRule and NormalizeExprsRule.
rules.add(SimplifyConditionalsRule.INSTANCE);
@@ -522,9 +525,9 @@ public class Analyzer {
private boolean hasEmptySpjResultSet_ = false;
public Analyzer(StmtTableCache stmtTableCache, TQueryCtx queryCtx,
- AuthorizationFactory authzFactory) {
+ AuthorizationFactory authzFactory, AuthorizationContext authzCtx) {
ancestors_ = new ArrayList<>();
- globalState_ = new GlobalState(stmtTableCache, queryCtx, authzFactory);
+ globalState_ = new GlobalState(stmtTableCache, queryCtx, authzFactory, authzCtx);
user_ = new User(TSessionStateUtil.getEffectiveUser(queryCtx.session));
}
@@ -557,7 +560,8 @@ public class Analyzer {
*/
public static Analyzer createWithNewGlobalState(Analyzer parentAnalyzer) {
GlobalState globalState = new GlobalState(parentAnalyzer.globalState_.stmtTableCache,
- parentAnalyzer.getQueryCtx(), parentAnalyzer.getAuthzFactory());
+ parentAnalyzer.getQueryCtx(), parentAnalyzer.getAuthzFactory(),
+ parentAnalyzer.getAuthzCtx());
return new Analyzer(parentAnalyzer, globalState);
}
@@ -776,7 +780,7 @@ public class Analyzer {
try {
if (!tableMask.needsMaskingOrFiltering()) return resolvedTableRef;
return InlineViewRef.createTableMaskView(resolvedPath, resolvedTableRef,
- tableMask);
+ tableMask, getAuthzCtx());
} catch (InternalException e) {
LOG.error("Error performing table masking", e);
throw new AnalysisException("Error performing table masking", e);
@@ -2710,6 +2714,7 @@ public class Analyzer {
public AuthorizationConfig getAuthzConfig() {
return getAuthzFactory().getAuthorizationConfig();
}
+ public AuthorizationContext getAuthzCtx() { return globalState_.authzCtx; }
public boolean isAuthzEnabled() { return getAuthzConfig().isEnabled(); }
public ListMap<TNetworkAddress> getHostIndex() { return globalState_.hostIndex; }
public ColumnLineageGraph getColumnLineageGraph() { return globalState_.lineageGraph; }
diff --git a/fe/src/main/java/org/apache/impala/analysis/CreateTableAsSelectStmt.java b/fe/src/main/java/org/apache/impala/analysis/CreateTableAsSelectStmt.java
index 2c4e442..643caa8 100644
--- a/fe/src/main/java/org/apache/impala/analysis/CreateTableAsSelectStmt.java
+++ b/fe/src/main/java/org/apache/impala/analysis/CreateTableAsSelectStmt.java
@@ -141,7 +141,7 @@ public class CreateTableAsSelectStmt extends StatementBase {
// over the full INSERT statement. To avoid duplicate registrations of table/colRefs,
// create a new root analyzer and clone the query statement for this initial pass.
Analyzer dummyRootAnalyzer = new Analyzer(analyzer.getStmtTableCache(),
- analyzer.getQueryCtx(), analyzer.getAuthzFactory());
+ analyzer.getQueryCtx(), analyzer.getAuthzFactory(), analyzer.getAuthzCtx());
QueryStmt tmpQueryStmt = insertStmt_.getQueryStmt().clone();
Analyzer tmpAnalyzer = new Analyzer(dummyRootAnalyzer);
tmpAnalyzer.setUseHiveColLabels(true);
diff --git a/fe/src/main/java/org/apache/impala/analysis/FromClause.java b/fe/src/main/java/org/apache/impala/analysis/FromClause.java
index 159f45c..f81e6be 100644
--- a/fe/src/main/java/org/apache/impala/analysis/FromClause.java
+++ b/fe/src/main/java/org/apache/impala/analysis/FromClause.java
@@ -72,7 +72,6 @@ public class FromClause extends StmtNode implements Iterable<TableRef> {
TableRef leftTblRef = null; // the one to the left of tblRef
for (int i = 0; i < tableRefs_.size(); ++i) {
TableRef tblRef = tableRefs_.get(i);
- // Replace non-InlineViewRef table refs with a BaseTableRef or ViewRef.
tblRef = analyzer.resolveTableRef(tblRef, doTableMasking_);
tableRefs_.set(i, Preconditions.checkNotNull(tblRef));
tblRef.setLeftTblRef(leftTblRef);
diff --git a/fe/src/main/java/org/apache/impala/analysis/InlineViewRef.java b/fe/src/main/java/org/apache/impala/analysis/InlineViewRef.java
index 41106f0..f63b689 100644
--- a/fe/src/main/java/org/apache/impala/analysis/InlineViewRef.java
+++ b/fe/src/main/java/org/apache/impala/analysis/InlineViewRef.java
@@ -21,6 +21,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Set;
+import org.apache.impala.authorization.AuthorizationContext;
import org.apache.impala.authorization.TableMask;
import org.apache.impala.catalog.Column;
import org.apache.impala.catalog.ColumnStats;
@@ -144,12 +145,15 @@ public class InlineViewRef extends TableRef {
* @param resolvedPath resolved path for the original table/view
* @param tableRef original resolved table/view
* @param tableMask TableMask providing column masking and row filtering policies
+ * @param authzCtx AuthorizationContext containing RangerBufferAuditHandler
*/
static InlineViewRef createTableMaskView(Path resolvedPath, TableRef tableRef,
- TableMask tableMask) throws AnalysisException, InternalException {
+ TableMask tableMask, AuthorizationContext authzCtx) throws AnalysisException,
+ InternalException {
Preconditions.checkNotNull(resolvedPath);
Preconditions.checkNotNull(resolvedPath.getRootTable());
Preconditions.checkNotNull(tableRef);
+ Preconditions.checkNotNull(authzCtx);
Preconditions.checkState(tableRef instanceof InlineViewRef
|| tableRef instanceof BaseTableRef);
List<Column> columns = resolvedPath.getRootTable().getColumnsInHiveOrder();
@@ -159,7 +163,7 @@ public class InlineViewRef extends TableRef {
// TODO: only add materialized columns to avoid introducing new privilege
// requirements (IMPALA-9223)
items.add(new SelectListItem(
- tableMask.createColumnMask(col.getName(), col.getType()),
+ tableMask.createColumnMask(col.getName(), col.getType(), authzCtx),
/*alias*/ col.getName()));
}
SelectList selectList = new SelectList(items);
diff --git a/fe/src/main/java/org/apache/impala/authorization/AuthorizationChecker.java b/fe/src/main/java/org/apache/impala/authorization/AuthorizationChecker.java
index bb1c587..c995164 100644
--- a/fe/src/main/java/org/apache/impala/authorization/AuthorizationChecker.java
+++ b/fe/src/main/java/org/apache/impala/authorization/AuthorizationChecker.java
@@ -94,6 +94,6 @@ public interface AuthorizationChecker {
/**
* Returns the column mask string for the given column.
*/
- String createColumnMask(User user, String dbName, String tableName, String columnName)
- throws InternalException;
+ String createColumnMask(User user, String dbName, String tableName, String columnName,
+ AuthorizationContext authzCtx) throws InternalException;
}
diff --git a/fe/src/main/java/org/apache/impala/authorization/NoopAuthorizationFactory.java b/fe/src/main/java/org/apache/impala/authorization/NoopAuthorizationFactory.java
index f57e943..f476b2e 100644
--- a/fe/src/main/java/org/apache/impala/authorization/NoopAuthorizationFactory.java
+++ b/fe/src/main/java/org/apache/impala/authorization/NoopAuthorizationFactory.java
@@ -221,7 +221,7 @@ public class NoopAuthorizationFactory implements AuthorizationFactory {
@Override
public String createColumnMask(User user, String dbName, String tableName,
- String columnName) {
+ String columnName, AuthorizationContext authzCtx) {
return null;
}
};
diff --git a/fe/src/main/java/org/apache/impala/authorization/TableMask.java b/fe/src/main/java/org/apache/impala/authorization/TableMask.java
index b210568..732c3ed 100644
--- a/fe/src/main/java/org/apache/impala/authorization/TableMask.java
+++ b/fe/src/main/java/org/apache/impala/authorization/TableMask.java
@@ -66,11 +66,12 @@ public class TableMask {
/**
* Return the masked Expr of the given column
*/
- public Expr createColumnMask(String colName, Type colType)
- throws InternalException, AnalysisException {
+ public Expr createColumnMask(String colName, Type colType,
+ AuthorizationContext authzCtx) throws InternalException,
+ AnalysisException {
Preconditions.checkState(!colType.isComplexType());
String maskedValue = authChecker_.createColumnMask(user_, dbName_, tableName_,
- colName);
+ colName, authzCtx);
if (LOG.isTraceEnabled()) {
LOG.trace("Performing column masking on table {}.{}: {} => {}",
dbName_, tableName_, colName, maskedValue);
diff --git a/fe/src/main/java/org/apache/impala/authorization/ranger/RangerAuthorizationChecker.java b/fe/src/main/java/org/apache/impala/authorization/ranger/RangerAuthorizationChecker.java
index 2a848aa..8dabebd 100644
--- a/fe/src/main/java/org/apache/impala/authorization/ranger/RangerAuthorizationChecker.java
+++ b/fe/src/main/java/org/apache/impala/authorization/ranger/RangerAuthorizationChecker.java
@@ -276,7 +276,7 @@ public class RangerAuthorizationChecker extends BaseAuthorizationChecker {
*/
private void authorizeColumnMask(User user, String dbName, String tableName,
String columnName) throws InternalException, AuthorizationException {
- if (evalColumnMask(user, dbName, tableName, columnName).isMaskEnabled()) {
+ if (evalColumnMask(user, dbName, tableName, columnName, null).isMaskEnabled()) {
throw new AuthorizationException(String.format(
"Column masking is disabled by --enable_column_masking flag. Can't access " +
"column %s.%s.%s that has column masking policy.",
@@ -288,7 +288,8 @@ public class RangerAuthorizationChecker extends BaseAuthorizationChecker {
public boolean needsMaskingOrFiltering(User user, String dbName, String tableName,
List<String> requiredColumns) throws InternalException {
for (String column: requiredColumns) {
- if (evalColumnMask(user, dbName, tableName, column).isMaskEnabled()) {
+ if (evalColumnMask(user, dbName, tableName, column, null)
+ .isMaskEnabled()) {
return true;
}
}
@@ -297,14 +298,26 @@ public class RangerAuthorizationChecker extends BaseAuthorizationChecker {
@Override
public String createColumnMask(User user, String dbName, String tableName,
- String columnName) throws InternalException {
- RangerAccessResult accessResult = evalColumnMask(user, dbName, tableName,
- columnName);
+ String columnName, AuthorizationContext rangerCtx) throws InternalException {
+ RangerBufferAuditHandler auditHandler =
+ ((RangerAuthorizationContext) rangerCtx).getAuditHandler();
+ RangerAccessResult accessResult = evalColumnMask(user, dbName, tableName, columnName,
+ auditHandler);
+
// No column masking policies, return the original column.
if (!accessResult.isMaskEnabled()) return columnName;
String maskType = accessResult.getMaskType();
RangerServiceDef.RangerDataMaskTypeDef maskTypeDef = accessResult.getMaskTypeDef();
Preconditions.checkNotNull(maskType);
+ List<AuthzAuditEvent> auditEvents = auditHandler.getAuthzEvents();
+ Preconditions.checkState(!auditEvents.isEmpty());
+ // Recall that the same instance of RangerAuthorizationContext is passed to
+ // createColumnMask() every time this method is called. Thus the same instance of
+ // RangerBufferAuditHandler is provided for evalColumnMask() to log the event.
+ // Since there will be exactly one event added to 'auditEvents' when there is a
+ // corresponding column masking policy, i.e., accessResult.isMaskEnabled() evaluates
+ // to true, we only need to process the last event on 'auditEvents'.
+ auditEvents.get(auditEvents.size() - 1).setAccessType(maskType.toLowerCase());
// The expression used to replace the original column.
String maskedColumn = columnName;
// The expression of the mask type. Column names are referenced by "{col}".
@@ -341,7 +354,8 @@ public class RangerAuthorizationChecker extends BaseAuthorizationChecker {
* A RangerAccessResult contains the matched policy details and the masked column.
*/
private RangerAccessResult evalColumnMask(User user, String dbName,
- String tableName, String columnName) throws InternalException {
+ String tableName, String columnName, RangerBufferAuditHandler auditHandler)
+ throws InternalException {
RangerAccessResourceImpl resource = new RangerImpalaResourceBuilder()
.database(dbName)
.table(tableName)
@@ -349,7 +363,12 @@ public class RangerAuthorizationChecker extends BaseAuthorizationChecker {
.build();
RangerAccessRequest req = new RangerAccessRequestImpl(resource,
SELECT_ACCESS_TYPE, user.getShortName(), getUserGroups(user));
- return plugin_.evalDataMaskPolicies(req, null);
+ // The method evalDataMaskPolicies() only checks whether there is a corresponding
+ // column masking policy on the Ranger server and thus does not check whether the
+ // requesting user/group is granted the necessary privilege on the specified
+ // resource. No AnalysisException or AuthorizationException will be thrown if the
+ // requesting user/group does not possess the necessary privilege.
+ return plugin_.evalDataMaskPolicies(req, auditHandler);
}
/**
diff --git a/fe/src/main/java/org/apache/impala/authorization/sentry/SentryAuthorizationChecker.java b/fe/src/main/java/org/apache/impala/authorization/sentry/SentryAuthorizationChecker.java
index 3f1b0e8..1fc0eb9 100644
--- a/fe/src/main/java/org/apache/impala/authorization/sentry/SentryAuthorizationChecker.java
+++ b/fe/src/main/java/org/apache/impala/authorization/sentry/SentryAuthorizationChecker.java
@@ -97,7 +97,7 @@ public class SentryAuthorizationChecker extends BaseAuthorizationChecker {
@Override
public String createColumnMask(User user, String dbName, String tableName,
- String columnName) {
+ String columnName, AuthorizationContext authzCtx) {
return columnName;
}
diff --git a/fe/src/test/java/org/apache/impala/authorization/ranger/RangerAuditLogTest.java b/fe/src/test/java/org/apache/impala/authorization/ranger/RangerAuditLogTest.java
index 463ee0b..531cc77 100644
--- a/fe/src/test/java/org/apache/impala/authorization/ranger/RangerAuditLogTest.java
+++ b/fe/src/test/java/org/apache/impala/authorization/ranger/RangerAuditLogTest.java
@@ -32,6 +32,7 @@ import org.apache.ranger.audit.model.AuthzAuditEvent;
import org.junit.Ignore;
import org.junit.Test;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
@@ -201,6 +202,115 @@ public class RangerAuditLogTest extends AuthorizationTestBase {
}, "show partitions functional.alltypes");
}
+ @Test
+ public void testAuditsForColumnMasking() throws ImpalaException {
+ String databaseName = "functional";
+ String tableName = "alltypestiny";
+ String policyNames[] = {"col_mask_custom", "col_mask_null"};
+ String columnNames[] = {"string_col", "date_string_col"};
+ String masks[] = {
+ " {\n" +
+ " \"dataMaskType\": \"CUSTOM\",\n" +
+ " \"valueExpr\": \"concat({col}, 'xyz')\"\n" +
+ " }\n",
+ " {\n" +
+ " \"dataMaskType\": \"MASK_NULL\"\n" +
+ " }\n"
+ };
+
+ List<String> policies = new ArrayList<>();
+ for (int i = 0; i < masks.length; ++i) {
+ String json = String.format("{\n" +
+ " \"name\": \"%s\",\n" +
+ " \"policyType\": 1,\n" +
+ " \"serviceType\": \"%s\",\n" +
+ " \"service\": \"%s\",\n" +
+ " \"resources\": {\n" +
+ " \"database\": {\n" +
+ " \"values\": [\"%s\"],\n" +
+ " \"isExcludes\": false,\n" +
+ " \"isRecursive\": false\n" +
+ " },\n" +
+ " \"table\": {\n" +
+ " \"values\": [\"%s\"],\n" +
+ " \"isExcludes\": false,\n" +
+ " \"isRecursive\": false\n" +
+ " },\n" +
+ " \"column\": {\n" +
+ " \"values\": [\"%s\"],\n" +
+ " \"isExcludes\": false,\n" +
+ " \"isRecursive\": false\n" +
+ " }\n" +
+ " },\n" +
+ " \"dataMaskPolicyItems\": [\n" +
+ " {\n" +
+ " \"accesses\": [\n" +
+ " {\n" +
+ " \"type\": \"select\",\n" +
+ " \"isAllowed\": true\n" +
+ " }\n" +
+ " ],\n" +
+ " \"users\": [\"%s\"],\n" +
+ " \"dataMaskInfo\":\n" +
+ "%s" +
+ " }\n" +
+ " ]\n" +
+ "}", policyNames[i], RANGER_SERVICE_TYPE, RANGER_SERVICE_NAME, databaseName,
+ tableName, columnNames[i], user_.getShortName(), masks[i]);
+ policies.add(json);
+ }
+
+ try {
+ for (int i = 0; i < masks.length; ++i) {
+ String policyName = policyNames[i];
+ String json = policies.get(i);
+ createRangerPolicy(policyName, json);
+ }
+
+ authzOk(events -> {
+ assertEquals(15, events.size());
+ assertEquals("select * from functional.alltypestiny",
+ events.get(0).getRequestData());
+ assertEventEquals("@column", "mask_null",
+ "functional/alltypestiny/date_string_col", 1, events.get(0));
+ assertEventEquals("@column", "custom", "functional/alltypestiny/string_col", 1,
+ events.get(1));
+ assertEventEquals("@table", "select", "functional/alltypestiny", 1,
+ events.get(2));
+ assertEventEquals("@column", "select", "functional/alltypestiny/id", 1,
+ events.get(3));
+ assertEventEquals("@column", "select", "functional/alltypestiny/bool_col", 1,
+ events.get(4));
+ assertEventEquals("@column", "select", "functional/alltypestiny/tinyint_col", 1,
+ events.get(5));
+ assertEventEquals("@column", "select", "functional/alltypestiny/smallint_col", 1,
+ events.get(6));
+ assertEventEquals("@column", "select", "functional/alltypestiny/int_col", 1,
+ events.get(7));
+ assertEventEquals("@column", "select", "functional/alltypestiny/bigint_col", 1,
+ events.get(8));
+ assertEventEquals("@column", "select", "functional/alltypestiny/float_col", 1,
+ events.get(9));
+ assertEventEquals("@column", "select", "functional/alltypestiny/double_col", 1,
+ events.get(10));
+ assertEventEquals("@column", "select", "functional/alltypestiny/string_col", 1,
+ events.get(11));
+ assertEventEquals("@column", "select", "functional/alltypestiny/timestamp_col", 1,
+ events.get(12));
+ assertEventEquals("@column", "select", "functional/alltypestiny/year", 1,
+ events.get(13));
+ assertEventEquals("@column", "select", "functional/alltypestiny/month", 1,
+ events.get(14));
+ }, "select * from functional.alltypestiny", onTable("functional", "alltypestiny",
+ TPrivilegeLevel.SELECT));
+ } finally {
+ for (int i = 0; i < masks.length; ++i) {
+ String policyName = policyNames[i];
+ deleteRangerPolicy(policyName);
+ }
+ }
+ }
+
private void authzOk(Consumer<List<AuthzAuditEvent>> resultChecker, String stmt,
TPrivilege[]... privileges) throws ImpalaException {
authorize(stmt).ok(privileges);
diff --git a/fe/src/test/java/org/apache/impala/common/FrontendTestBase.java b/fe/src/test/java/org/apache/impala/common/FrontendTestBase.java
index a4f43c4..1cc1213 100644
--- a/fe/src/test/java/org/apache/impala/common/FrontendTestBase.java
+++ b/fe/src/test/java/org/apache/impala/common/FrontendTestBase.java
@@ -392,7 +392,7 @@ public class FrontendTestBase extends AbstractFrontendTest {
@Override
public String createColumnMask(User user, String dbName, String tableName,
- String columnName) {
+ String columnName, AuthorizationContext authzCtx) {
return null;
}