RANGER-4655: Execute and read permissions granted to a user in different HDFS policies does not take effect
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngineImpl.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngineImpl.java
index 252482c..df39467 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngineImpl.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngineImpl.java
@@ -763,6 +763,22 @@
}
}
+ if (!request.isAccessTypeAny()) {
+ Set<String> allRequestedAccesses = RangerAccessRequestUtil.getAllRequestedAccessTypes(request);
+ if (CollectionUtils.size(allRequestedAccesses) > 1 && !RangerAccessRequestUtil.getIsAnyAccessInContext(request.getContext())) {
+ Map<String, RangerAccessResult> accessTypeResults = RangerAccessRequestUtil.getAccessTypeResults(request.getContext());
+ if (accessTypeResults != null) {
+ if (accessTypeResults.keySet().containsAll(allRequestedAccesses)) {
+ // Allow
+ RangerAccessResult result = accessTypeResults.values().iterator().next(); // Pick one result randomly
+ ret.setAccessResultFrom(result);
+ ret.setIsAccessDetermined(true);
+ }
+ RangerAccessRequestUtil.setAccessTypeResults(request.getContext(), null);
+ }
+ }
+ }
+
if (!ret.getIsAccessDetermined()) {
if (isDeniedByTags) {
ret.setIsAllowed(false);
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultPolicyEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultPolicyEvaluator.java
index 7fe2a2e..ded8d09 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultPolicyEvaluator.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultPolicyEvaluator.java
@@ -818,11 +818,29 @@
} else {
Set<String> allRequestedAccesses = RangerAccessRequestUtil.getAllRequestedAccessTypes(request);
- if (CollectionUtils.isNotEmpty(allRequestedAccesses)) {
+ if (CollectionUtils.size(allRequestedAccesses) > 1) {
for (String accessType : allRequestedAccesses) {
- accessResult = lookupPolicyACLSummary(request.getUser(), request.getUserGroups(), request.getUserRoles(), accessType);
- if (accessResult == null) {
- break;
+ Integer oneAccessResult = lookupPolicyACLSummary(request.getUser(), request.getUserGroups(), request.getUserRoles(), accessType);
+ if (oneAccessResult != null) {
+ if (oneAccessResult.equals(RangerPolicyEvaluator.ACCESS_DENIED)) {
+ accessResult = oneAccessResult;
+ RangerAccessRequestUtil.setAccessTypeResults(request.getContext(), null);
+
+ break;
+ }
+ if (oneAccessResult.equals(RangerPolicyEvaluator.ACCESS_ALLOWED)) {
+ if (!result.getIsAllowed()) { // if access is not yet allowed by another policy
+ if (matchType != RangerPolicyResourceMatcher.MatchType.ANCESTOR) {
+ RangerAccessResult oneResult = new RangerAccessResult(result.getPolicyType(), result.getServiceName(), result.getServiceDef(), result.getAccessRequest());
+ oneResult.setIsAllowed(true);
+ oneResult.setPolicyPriority(getPolicyPriority());
+ oneResult.setPolicyId(getPolicyId());
+ oneResult.setPolicyVersion(getPolicy().getVersion());
+
+ RangerAccessRequestUtil.setAccessTypeResult(request.getContext(), accessType, oneResult);
+ }
+ }
+ }
}
}
} else {
@@ -865,7 +883,7 @@
updateAccessResult(oneResult, matchType, false, "matched deny-all-else policy");
}
- if (request.isAccessTypeAny() || RangerAccessRequestUtil.getIsAnyAccessInContext(request.getContext())) {
+ if (request.isAccessTypeAny() || allRequestedAccesses.size() == 1 || RangerAccessRequestUtil.getIsAnyAccessInContext(request.getContext())) {
// Implement OR logic
if (oneResult.getIsAllowed()) {
allowResult = oneResult;
@@ -886,14 +904,11 @@
// Implement AND logic
if (oneResult.getIsAccessDetermined() && !oneResult.getIsAllowed()) {
denyResult = oneResult;
- allowResult = null;
+ RangerAccessRequestUtil.setAccessTypeResults(request.getContext(), null);
break;
} else if (oneResult.getIsAllowed()) {
- allowResult = noResult ? null : oneResult;
- } else {
- noResult = true;
- allowResult = null;
+ RangerAccessRequestUtil.setAccessTypeResult(request.getContext(), accessType, oneResult);
}
}
}
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerAccessRequestUtil.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerAccessRequestUtil.java
index 92a4fe0..a56ecb2 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerAccessRequestUtil.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerAccessRequestUtil.java
@@ -29,6 +29,7 @@
import org.apache.ranger.plugin.contextenricher.RangerTagForEval;
import org.apache.ranger.plugin.policyengine.RangerAccessRequest;
import org.apache.ranger.plugin.policyengine.RangerAccessResource;
+import org.apache.ranger.plugin.policyengine.RangerAccessResult;
import org.apache.ranger.plugin.policyengine.gds.GdsAccessResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -51,6 +52,7 @@
public static final String KEY_CONTEXT_GDS_RESULT = "_GDS_RESULT";
public static final String KEY_CONTEXT_IS_REQUEST_PREPROCESSED = "ISREQUESTPREPROCESSED";
public static final String KEY_CONTEXT_RESOURCE_ZONE_NAMES = "RESOURCE_ZONE_NAMES";
+ public static final String KEY_CONTEXT_ACCESS_TYPE_RESULTS = "_ACCESS_TYPE_RESULTS";
public static void setRequestTagsInContext(Map<String, Object> context, Set<RangerTagForEval> tags) {
if(CollectionUtils.isEmpty(tags)) {
@@ -322,4 +324,41 @@
return ret != null && ret.size() == 1 ? ret.iterator().next() : null;
}
+
+ public static void setAccessTypeResults(Map<String, Object> context, Map<String, RangerAccessResult> accessTypeResults) {
+ if (context != null) {
+ if (accessTypeResults != null) {
+ context.put(KEY_CONTEXT_ACCESS_TYPE_RESULTS, accessTypeResults);
+ } else {
+ context.remove(KEY_CONTEXT_ACCESS_TYPE_RESULTS);
+ }
+ }
+ }
+
+ public static Map<String, RangerAccessResult> getAccessTypeResults(Map<String, Object> context) {
+ Map<String, RangerAccessResult> ret = null;
+
+ if (context != null) {
+ Object o = context.get(KEY_CONTEXT_ACCESS_TYPE_RESULTS);
+ if (o != null) {
+ ret = (Map<String, RangerAccessResult>)o;
+ }
+ }
+
+ return ret;
+ }
+
+ public static void setAccessTypeResult(Map<String, Object> context, String accessType, RangerAccessResult result) {
+ if (context != null) {
+ Map<String, RangerAccessResult> results = getAccessTypeResults(context);
+
+ if (results == null) {
+ results = new HashMap<>();
+
+ setAccessTypeResults(context, results);
+ }
+
+ results.putIfAbsent(accessType, result);
+ }
+ }
}
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestPolicyEngine.java b/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestPolicyEngine.java
index 2afbfeb..64563b8 100644
--- a/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestPolicyEngine.java
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestPolicyEngine.java
@@ -509,6 +509,13 @@
runTestsFromResourceFiles(resourceFiles);
}
+ @Test
+ public void testMultipleAccessAuthorization() throws Exception {
+ String[] resourceFiles = {"/policyengine/test_policyengine_hdfs_multiple_accesses.json"};
+
+ runTestsFromResourceFiles(resourceFiles);
+ }
+
private void runTestsFromResourceFiles(String[] resourceNames) {
for(String resourceName : resourceNames) {
InputStream inStream = this.getClass().getResourceAsStream(resourceName);
diff --git a/agents-common/src/test/resources/policyengine/test_policyengine_hdfs_multiple_accesses.json b/agents-common/src/test/resources/policyengine/test_policyengine_hdfs_multiple_accesses.json
new file mode 100644
index 0000000..6b53d2e
--- /dev/null
+++ b/agents-common/src/test/resources/policyengine/test_policyengine_hdfs_multiple_accesses.json
@@ -0,0 +1,92 @@
+{
+ "serviceName":"hdfsdev",
+
+ "serviceDef":{
+ "name":"hdfs",
+ "id":1,
+ "resources":[
+ {"name":"path","type":"path","level":1,"mandatory":true,"lookupSupported":true,"matcher":"org.apache.ranger.plugin.resourcematcher.RangerPathResourceMatcher","matcherOptions":{"wildCard":true, "ignoreCase":true},"label":"Resource Path","description":"HDFS file or directory path"}
+ ],
+ "accessTypes":[
+ {"name":"read","label":"Read"},
+ {"name":"write","label":"Write"},
+ {"name":"execute","label":"Execute"}
+ ],
+ "contextEnrichers":
+ [
+ {
+ "itemId":1,
+ "name" : "GeolocationEnricher",
+ "enricher" : "org.apache.ranger.plugin.contextenricher.RangerFileBasedGeolocationProvider",
+ "enricherOptions" : {
+ "FilePath":"/etc/ranger/geo/geo.txt", "ForceRead":"false", "IPInDotFormat":"true"
+ ,"geolocation.meta.prefix": "TEST_"
+ }
+ }
+ ],
+ "policyConditions": [
+ {
+ "itemId":1,
+ "name":"ScriptConditionEvaluator",
+ "evaluator": "org.apache.ranger.plugin.conditionevaluator.RangerScriptConditionEvaluator",
+ "evaluatorOptions" : {"engineName":"JavaScript"},
+ "label":"Script",
+ "description": "Script to execute"
+ }
+ ]
+ },
+
+ "policies":[
+ {"id":1,"name":"audit-all-access under /public","isEnabled":true,"isAuditEnabled":true,
+ "resources":{"path":{"values":["/public"],"isRecursive":true}},
+ "policyItems":[
+ {"accesses":[],"users":[],"groups":["public"],"delegateAdmin":false}
+ ]
+ }
+ ,
+ {"id":2,"name":"allow-execute-to-all under /public/","isEnabled":true,"isAuditEnabled":false,
+ "resources":{"path":{"values":["/public/*"],"isRecursive":true}},
+ "policyItems":[
+ {"accesses":[{"type":"execute","isAllowed":true}],"users":[],"groups":["public"],"delegateAdmin":false}
+ ]
+ }
+ ,
+ {"id":3,"name":"allow-read-to-finance under /public/finance","isEnabled":true,"isAuditEnabled":true,
+ "resources":{"path":{"values":["/public/finance"],"isRecursive":true}},
+ "policyItems":[
+ {"accesses":[{"type":"read","isAllowed":true}],"users":["finance"],"groups":[],"delegateAdmin":false}
+ ]
+ }
+ ],
+
+ "tests":[
+ {"name":"ALLOW 'read_execute /public/finance' for user finance",
+ "request":{
+ "resource":{"elements":{"path":"/public/finance"}},
+ "accessType":"read","user":"finance","userGroups":[],"requestData":"read_execute /public/finance",
+ "context": {"ACCESSTYPES": [ "read", "execute" ]}
+
+ },
+ "result":{"isAudited":true,"isAllowed":true,"policyId":3}
+ }
+ ,
+ {"name":"DENY 'read_execute /public/finance' for user hr",
+ "request":{
+ "resource":{"elements":{"path":"/public/finance"}},
+ "accessType":"read","user":"hr","userGroups":[],"requestData":"read_execute /public/finance",
+ "context": {"ACCESSTYPES": [ "read", "execute" ]}
+
+ },
+ "result":{"isAudited":true,"isAllowed":false,"policyId":-1}
+ }
+ ,
+ {"name":"ALLOW 'execute /public/finance' for user hr",
+ "request":{
+ "resource":{"elements":{"path":"/public/finance"}},
+ "accessType":"execute","user":"hr","userGroups":[],"requestData":"execute /public/finance"
+ },
+ "result":{"isAudited":true,"isAllowed":true,"policyId":2}
+ }
+ ]
+}
+