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}
+    }
+  ]
+}
+