You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ranger.apache.org by ab...@apache.org on 2018/03/05 06:27:12 UTC

[3/4] ranger git commit: RANGER-2000: Policy effective dates to support time-bound and temporary authorization

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngineImpl.java
----------------------------------------------------------------------
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 f8241c5..29ecfa8 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
@@ -39,6 +39,7 @@ import org.apache.ranger.plugin.util.ServicePolicies;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -656,7 +657,8 @@ public class RangerPolicyEngineImpl implements RangerPolicyEngine {
 
 		List<RangerPolicy> ret = new ArrayList<>();
 
-		// TODO: run through evaluator in tagPolicyRepository as well
+
+        // TODO: run through evaluator in tagPolicyRepository as well
 		for (RangerPolicyEvaluator evaluator : policyRepository.getPolicyEvaluators()) {
 			RangerPolicy policy = evaluator.getPolicy();
 
@@ -680,42 +682,56 @@ public class RangerPolicyEngineImpl implements RangerPolicyEngine {
 		}
 
 		RangerAccessResult ret = createAccessResult(request, policyType);
+		Date accessTime = request.getAccessTime();
 
-		if (ret != null && request != null) {
-
-			if (hasTagPolicies()) {
+        if (ret != null && request != null) {
 
-				evaluateTagPolicies(request, policyType, ret);
+			evaluateTagPolicies(request, policyType, ret);
 
-				if (LOG.isDebugEnabled()) {
-					if (ret.getIsAccessDetermined() && ret.getIsAuditedDetermined()) {
-						LOG.debug("RangerPolicyEngineImpl.evaluatePoliciesNoAudit() - access and audit determined by tag policy. request=" + request + ", result=" + ret);
+			if (LOG.isDebugEnabled()) {
+				if (ret.getIsAccessDetermined() && ret.getIsAuditedDetermined()) {
+					if (!ret.getIsAllowed()) {
+						LOG.debug("RangerPolicyEngineImpl.evaluatePoliciesNoAudit() - audit determined and access denied by a tag policy. Higher priority resource policies will be evaluated to check for allow, request=" + request + ", result=" + ret);
+					} else {
+						LOG.debug("RangerPolicyEngineImpl.evaluatePoliciesNoAudit() - audit determined and access allowed by a tag policy. Same or higher priority resource policies will be evaluated to check for deny, request=" + request + ", result=" + ret);
 					}
 				}
 			}
 
 			boolean isAllowedByTags          = ret.getIsAccessDetermined() && ret.getIsAllowed();
 			boolean isDeniedByTags           = ret.getIsAccessDetermined() && !ret.getIsAllowed();
-			boolean evaluateResourcePolicies = hasResourcePolicies() && (!isDeniedByTags || !ret.getIsAuditedDetermined());
+			boolean evaluateResourcePolicies = hasResourcePolicies();
 
 			if (evaluateResourcePolicies) {
-
 				boolean findAuditByResource = !ret.getIsAuditedDetermined();
 				boolean foundInCache        = findAuditByResource && policyRepository.setAuditEnabledFromCache(request, ret);
 
-				if(isAllowedByTags) {
-					ret.setIsAccessDetermined(false); // discard allowed result by tag-policies, to evaluate resource policies for possible deny
-				}
+				ret.setIsAccessDetermined(false); // discard result by tag-policies, to evaluate resource policies for possible override
 
 				List<RangerPolicyEvaluator> evaluators = policyRepository.getLikelyMatchPolicyEvaluators(request.getResource(), policyType);
 
 				for (RangerPolicyEvaluator evaluator : evaluators) {
+					if (!evaluator.isApplicable(accessTime)) {
+						continue;
+					}
+
+					if (isDeniedByTags) {
+						if (ret.getPolicyPriority() >= evaluator.getPolicyPriority()) {
+							ret.setIsAccessDetermined(true);
+						}
+					} else if (isAllowedByTags) {
+						if (ret.getPolicyPriority() > evaluator.getPolicyPriority()) {
+							ret.setIsAccessDetermined(true);
+						}
+					}
 
 					ret.incrementEvaluatedPoliciesCount();
 					evaluator.evaluate(request, ret);
 
-					if (ret.getIsAllowed() && !evaluator.hasDeny()) { // all policies having deny have been evaluated
-						ret.setIsAccessDetermined(true);
+					if (ret.getIsAllowed()) {
+						if (!evaluator.hasDeny()) { // No more deny policies left
+							ret.setIsAccessDetermined(true);
+						}
 					}
 
 					if (ret.getIsAuditedDetermined() && ret.getIsAccessDetermined()) {
@@ -724,6 +740,14 @@ public class RangerPolicyEngineImpl implements RangerPolicyEngine {
 
 				}
 
+				if (!ret.getIsAccessDetermined()) {
+					if (isDeniedByTags) {
+						ret.setIsAllowed(false);
+					} else if (isAllowedByTags) {
+						ret.setIsAllowed(true);
+					}
+				}
+
 				if(ret.getIsAllowed()) {
 					ret.setIsAccessDetermined(true);
 				}
@@ -746,15 +770,16 @@ public class RangerPolicyEngineImpl implements RangerPolicyEngine {
 			LOG.debug("==> RangerPolicyEngineImpl.evaluateTagPolicies(" + request + ", policyType =" + policyType + ", " + result + ")");
 		}
 
+		Date accessTime = request.getAccessTime();
+
 		Set<RangerTagForEval> tags = RangerAccessRequestUtil.getRequestTagsFromContext(request.getContext());
 
-		List<PolicyEvaluatorForTag> policyEvaluators = tagPolicyRepository.getLikelyMatchPolicyEvaluators(tags, policyType);
+		List<PolicyEvaluatorForTag> policyEvaluators = tagPolicyRepository == null ? null : tagPolicyRepository.getLikelyMatchPolicyEvaluators(tags, policyType, accessTime);
 
 		if (CollectionUtils.isNotEmpty(policyEvaluators)) {
-
 			for (PolicyEvaluatorForTag policyEvaluator : policyEvaluators) {
-
 				RangerPolicyEvaluator evaluator = policyEvaluator.getEvaluator();
+
 				RangerTagForEval tag = policyEvaluator.getTag();
 
 				RangerAccessRequest tagEvalRequest = new RangerTagAccessRequest(tag, tagPolicyRepository.getServiceDef(), request);
@@ -771,8 +796,10 @@ public class RangerPolicyEngineImpl implements RangerPolicyEngine {
 
 				evaluator.evaluate(tagEvalRequest, tagEvalResult);
 
-				if (tagEvalResult.getIsAllowed() && !evaluator.hasDeny()) { // all policies having deny have been evaluated
-					tagEvalResult.setIsAccessDetermined(true);
+				if (tagEvalResult.getIsAllowed()) {
+					if (!evaluator.hasDeny()) { // No Deny policies left now
+						tagEvalResult.setIsAccessDetermined(true);
+					}
 				}
 
 				if (tagEvalResult.getIsAudited()) {

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyRepository.java
----------------------------------------------------------------------
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyRepository.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyRepository.java
index 23d1efa..088b729 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyRepository.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyRepository.java
@@ -43,6 +43,7 @@ import org.apache.ranger.plugin.util.ServicePolicies;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -286,10 +287,10 @@ class RangerPolicyRepository {
     }
 
     List<RangerPolicyEvaluator> getRowFilterPolicyEvaluators() {
-        return rowFilterPolicyEvaluators;
+	    return rowFilterPolicyEvaluators;
     }
 
-    List<PolicyEvaluatorForTag> getLikelyMatchPolicyEvaluators(Set<RangerTagForEval> tags, int policyType) {
+    List<PolicyEvaluatorForTag> getLikelyMatchPolicyEvaluators(Set<RangerTagForEval> tags, int policyType, Date accessTime) {
         List<PolicyEvaluatorForTag> ret = Collections.EMPTY_LIST;
 
         if (CollectionUtils.isNotEmpty(tags) && getServiceDef() != null) {
@@ -297,26 +298,34 @@ class RangerPolicyRepository {
             ret = new ArrayList<PolicyEvaluatorForTag>();
 
             for (RangerTagForEval tag : tags) {
-                RangerAccessResource resource = new RangerTagResource(tag.getType(), getServiceDef());
-                List<RangerPolicyEvaluator> evaluators = getLikelyMatchPolicyEvaluators(resource, policyType);
-
-                if (CollectionUtils.isNotEmpty(evaluators)) {
-                    for (RangerPolicyEvaluator evaluator : evaluators) {
-                        ret.add(new PolicyEvaluatorForTag(evaluator, tag));
-                    }
-                }
+            	if (tag.isApplicable(accessTime)) {
+		            RangerAccessResource resource = new RangerTagResource(tag.getType(), getServiceDef());
+		            List<RangerPolicyEvaluator> evaluators = getLikelyMatchPolicyEvaluators(resource, policyType);
+
+		            if (CollectionUtils.isNotEmpty(evaluators)) {
+			            for (RangerPolicyEvaluator evaluator : evaluators) {
+			                if (evaluator.isApplicable(accessTime)) {
+                                ret.add(new PolicyEvaluatorForTag(evaluator, tag));
+                            }
+			            }
+		            }
+	            } else {
+            		if (LOG.isDebugEnabled()) {
+            			LOG.debug("Tag:[" + tag.getType() + "] is not applicable at accessTime:[" + accessTime +"]");
+		            }
+	            }
             }
 
             if (CollectionUtils.isNotEmpty(ret)) {
                 switch(policyType) {
                     case RangerPolicy.POLICY_TYPE_ACCESS:
-                        Collections.sort(ret, new PolicyEvaluatorForTag.PolicyEvalOrderComparator());
+                        Collections.sort(ret, PolicyEvaluatorForTag.EVAL_ORDER_COMPARATOR);
                         break;
                     case RangerPolicy.POLICY_TYPE_DATAMASK:
-                        Collections.sort(ret, new PolicyEvaluatorForTag.PolicyNameComparator());
+                        Collections.sort(ret, PolicyEvaluatorForTag.NAME_COMPARATOR);
                         break;
                     case RangerPolicy.POLICY_TYPE_ROWFILTER:
-                        Collections.sort(ret, new PolicyEvaluatorForTag.PolicyNameComparator());
+                        Collections.sort(ret, PolicyEvaluatorForTag.NAME_COMPARATOR);
                         break;
                     default:
                         LOG.warn("Unknown policy-type:[" + policyType + "]. Ignoring..");

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerAbstractPolicyEvaluator.java
----------------------------------------------------------------------
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerAbstractPolicyEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerAbstractPolicyEvaluator.java
index d218c73..4e6ca2f 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerAbstractPolicyEvaluator.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerAbstractPolicyEvaluator.java
@@ -74,6 +74,11 @@ public abstract class RangerAbstractPolicyEvaluator implements RangerPolicyEvalu
 	}
 
 	@Override
+	public int getPolicyPriority() {
+		return policy != null && policy.getPolicyPriority() != null ? policy.getPolicyPriority() : RangerPolicy.POLICY_PRIORITY_NORMAL;
+	}
+
+	@Override
 	public RangerServiceDef getServiceDef() {
 		return serviceDef;
 	}

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultDataMaskPolicyItemEvaluator.java
----------------------------------------------------------------------
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultDataMaskPolicyItemEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultDataMaskPolicyItemEvaluator.java
index 349ab36..bfdf581 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultDataMaskPolicyItemEvaluator.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultDataMaskPolicyItemEvaluator.java
@@ -18,8 +18,6 @@
  */
 package org.apache.ranger.plugin.policyevaluator;
 
-
-
 import org.apache.ranger.plugin.model.RangerPolicy;
 import org.apache.ranger.plugin.model.RangerServiceDef;
 import org.apache.ranger.plugin.model.RangerPolicy.RangerDataMaskPolicyItem;
@@ -44,7 +42,7 @@ public class RangerDefaultDataMaskPolicyItemEvaluator extends RangerDefaultPolic
 	}
 
 	@Override
-	public void updateAccessResult(RangerAccessResult result, RangerPolicyResourceMatcher.MatchType matchType, Long policyId) {
+	public void updateAccessResult(RangerAccessResult result, RangerPolicyResourceMatcher.MatchType matchType, RangerPolicy policy) {
 		RangerPolicyItemDataMaskInfo dataMaskInfo = getDataMaskInfo();
 
 		if (dataMaskInfo != null) {
@@ -54,6 +52,7 @@ public class RangerDefaultDataMaskPolicyItemEvaluator extends RangerDefaultPolic
 			result.setMaskType(dataMaskInfo.getDataMaskType());
 			result.setMaskCondition(dataMaskInfo.getConditionExpr());
 			result.setMaskedValue(dataMaskInfo.getValueExpr());
+			result.setPolicyPriority(policy.getPolicyPriority());
 			result.setPolicyId(policyId);
 		}
 	}

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultPolicyEvaluator.java
----------------------------------------------------------------------
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 2b66c70..ffeea26 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
@@ -22,6 +22,7 @@ package org.apache.ranger.plugin.policyevaluator;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -40,6 +41,7 @@ import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource;
 import org.apache.ranger.plugin.model.RangerPolicy.RangerRowFilterPolicyItem;
 import org.apache.ranger.plugin.model.RangerServiceDef;
 import org.apache.ranger.plugin.model.RangerServiceDef.RangerAccessTypeDef;
+import org.apache.ranger.plugin.model.RangerValiditySchedule;
 import org.apache.ranger.plugin.policyengine.RangerAccessRequest;
 import org.apache.ranger.plugin.policyengine.RangerAccessResource;
 import org.apache.ranger.plugin.policyengine.RangerAccessResult;
@@ -61,6 +63,7 @@ public class RangerDefaultPolicyEvaluator extends RangerAbstractPolicyEvaluator
 	private static final Log PERF_POLICY_REQUEST_LOG = RangerPerfTracer.getPerfLogger("policy.request");
 
 	private RangerPolicyResourceMatcher     resourceMatcher;
+	private List<RangerValidityScheduleEvaluator> validityScheduleEvaluators;
 	private List<RangerPolicyItemEvaluator> allowEvaluators;
 	private List<RangerPolicyItemEvaluator> denyEvaluators;
 	private List<RangerPolicyItemEvaluator> allowExceptionEvaluators;
@@ -117,6 +120,7 @@ public class RangerDefaultPolicyEvaluator extends RangerAbstractPolicyEvaluator
 		resourceMatcher.init();
 
 		if(policy != null) {
+			validityScheduleEvaluators = createValidityScheduleEvaluators(policy);
 			allowEvaluators          = createPolicyItemEvaluators(policy, serviceDef, options, RangerPolicyItemEvaluator.POLICY_ITEM_TYPE_ALLOW);
 			denyEvaluators           = createPolicyItemEvaluators(policy, serviceDef, options, RangerPolicyItemEvaluator.POLICY_ITEM_TYPE_DENY);
 			allowExceptionEvaluators = createPolicyItemEvaluators(policy, serviceDef, options, RangerPolicyItemEvaluator.POLICY_ITEM_TYPE_ALLOW_EXCEPTIONS);
@@ -124,6 +128,7 @@ public class RangerDefaultPolicyEvaluator extends RangerAbstractPolicyEvaluator
 			dataMaskEvaluators       = createDataMaskPolicyItemEvaluators(policy, serviceDef, options, policy.getDataMaskPolicyItems());
 			rowFilterEvaluators      = createRowFilterPolicyItemEvaluators(policy, serviceDef, options, policy.getRowFilterPolicyItems());
 		} else {
+			validityScheduleEvaluators = Collections.<RangerValidityScheduleEvaluator>emptyList();
 			allowEvaluators          = Collections.<RangerPolicyItemEvaluator>emptyList();
 			denyEvaluators           = Collections.<RangerPolicyItemEvaluator>emptyList();
 			allowExceptionEvaluators = Collections.<RangerPolicyItemEvaluator>emptyList();
@@ -150,6 +155,32 @@ public class RangerDefaultPolicyEvaluator extends RangerAbstractPolicyEvaluator
 		}
 	}
 
+	@Override
+    public boolean isApplicable(Date accessTime) {
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("==> RangerDefaultPolicyEvaluator.isApplicable(" + accessTime + ")");
+        }
+
+        boolean ret = false;
+
+        if (accessTime != null && CollectionUtils.isNotEmpty(validityScheduleEvaluators)) {
+			for (RangerValidityScheduleEvaluator evaluator : validityScheduleEvaluators) {
+				if (evaluator.isApplicable(accessTime.getTime())) {
+					ret = true;
+					break;
+				}
+			}
+        } else {
+        	ret = true;
+        }
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("<== RangerDefaultPolicyEvaluator.isApplicable(" + accessTime + ") : " + ret);
+        }
+
+        return ret;
+    }
+
     @Override
     public void evaluate(RangerAccessRequest request, RangerAccessResult result) {
         if (LOG.isDebugEnabled()) {
@@ -373,7 +404,7 @@ public class RangerDefaultPolicyEvaluator extends RangerAbstractPolicyEvaluator
 		RangerPolicyItemEvaluator matchedPolicyItem = getMatchingPolicyItem(request, result);
 
 		if(matchedPolicyItem != null) {
-			matchedPolicyItem.updateAccessResult(result, matchType, getPolicy().getId());
+			matchedPolicyItem.updateAccessResult(result, matchType, getPolicy());
 		}
 
 		if(LOG.isDebugEnabled()) {
@@ -583,6 +614,22 @@ public class RangerDefaultPolicyEvaluator extends RangerAbstractPolicyEvaluator
 		return ret;
 	}
 
+    private List<RangerValidityScheduleEvaluator> createValidityScheduleEvaluators(RangerPolicy policy) {
+	    List<RangerValidityScheduleEvaluator> ret = null;
+
+	    if (CollectionUtils.isNotEmpty(policy.getValiditySchedules())) {
+	        ret = new ArrayList<>();
+
+	        for (RangerValiditySchedule schedule : policy.getValiditySchedules()) {
+	            ret.add(new RangerValidityScheduleEvaluator(schedule));
+            }
+        } else {
+            ret = Collections.<RangerValidityScheduleEvaluator>emptyList();
+        }
+
+        return ret;
+    }
+
 	private List<RangerPolicyItemEvaluator> createPolicyItemEvaluators(RangerPolicy policy, RangerServiceDef serviceDef, RangerPolicyEngineOptions options, int policyItemType) {
 		List<RangerPolicyItemEvaluator> ret         = null;
 		List<RangerPolicyItem>          policyItems = null;

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultPolicyItemEvaluator.java
----------------------------------------------------------------------
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultPolicyItemEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultPolicyItemEvaluator.java
index 9564565..312deef 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultPolicyItemEvaluator.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultPolicyItemEvaluator.java
@@ -350,16 +350,18 @@ public class RangerDefaultPolicyItemEvaluator extends RangerAbstractPolicyItemEv
 	}
 
 	@Override
-	public void updateAccessResult(RangerAccessResult result, RangerPolicyResourceMatcher.MatchType matchType, Long policyId) {
+	public void updateAccessResult(RangerAccessResult result, RangerPolicyResourceMatcher.MatchType matchType, RangerPolicy policy) {
 		if(getPolicyItemType() == RangerPolicyItemEvaluator.POLICY_ITEM_TYPE_DENY) {
 			if(matchType != RangerPolicyResourceMatcher.MatchType.DESCENDANT) {
 				result.setIsAllowed(false);
+                result.setPolicyPriority(policy.getPolicyPriority());
 				result.setPolicyId(policyId);
 				result.setReason(getComments());
 			}
 		} else {
 			if(! result.getIsAllowed()) { // if access is not yet allowed by another policy
 				result.setIsAllowed(true);
+                result.setPolicyPriority(policy.getPolicyPriority());
 				result.setPolicyId(policyId);
 				result.setReason(getComments());
 			}

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultRowFilterPolicyItemEvaluator.java
----------------------------------------------------------------------
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultRowFilterPolicyItemEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultRowFilterPolicyItemEvaluator.java
index cacae5a..a6cea95 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultRowFilterPolicyItemEvaluator.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultRowFilterPolicyItemEvaluator.java
@@ -18,7 +18,6 @@
  */
 package org.apache.ranger.plugin.policyevaluator;
 
-
 import org.apache.ranger.plugin.model.RangerPolicy;
 import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemRowFilterInfo;
 import org.apache.ranger.plugin.model.RangerPolicy.RangerRowFilterPolicyItem;
@@ -43,7 +42,7 @@ public class RangerDefaultRowFilterPolicyItemEvaluator extends RangerDefaultPoli
 	}
 
 	@Override
-	public void updateAccessResult(RangerAccessResult result, RangerPolicyResourceMatcher.MatchType matchType, Long policyId) {
+	public void updateAccessResult(RangerAccessResult result, RangerPolicyResourceMatcher.MatchType matchType, RangerPolicy policy) {
 		RangerPolicyItemRowFilterInfo rowFilterInfo = getRowFilterInfo();
 
 		if (rowFilterInfo != null) {
@@ -51,6 +50,7 @@ public class RangerDefaultRowFilterPolicyItemEvaluator extends RangerDefaultPoli
 			result.setIsAccessDetermined(true);
 
 			result.setFilterExpr(rowFilterInfo.getFilterExpr());
+			result.setPolicyPriority(policy.getPolicyPriority());
 			result.setPolicyId(policyId);
 		}
 	}

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerPolicyEvaluator.java
----------------------------------------------------------------------
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerPolicyEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerPolicyEvaluator.java
index 7a890b8..60b350e 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerPolicyEvaluator.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerPolicyEvaluator.java
@@ -22,6 +22,7 @@ package org.apache.ranger.plugin.policyevaluator;
 
 import java.io.Serializable;
 import java.util.Comparator;
+import java.util.Date;
 import java.util.Map;
 import java.util.Set;
 
@@ -38,6 +39,7 @@ import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceEvalua
 
 public interface RangerPolicyEvaluator extends RangerPolicyResourceEvaluator {
 	Comparator<RangerPolicyEvaluator> EVAL_ORDER_COMPARATOR = new RangerPolicyEvaluator.PolicyEvalOrderComparator();
+	Comparator<RangerPolicyEvaluator> NAME_COMPARATOR       = new RangerPolicyEvaluator.PolicyNameComparator();
 
 	String EVALUATOR_TYPE_AUTO   = "auto";
 	String EVALUATOR_TYPE_OPTIMIZED = "optimized";
@@ -53,6 +55,10 @@ public interface RangerPolicyEvaluator extends RangerPolicyResourceEvaluator {
 
 	boolean hasDeny();
 
+	int getPolicyPriority();
+
+	boolean isApplicable(Date accessTime);
+
 	int getEvalOrder();
 
 	long getUsageCount();
@@ -84,6 +90,12 @@ public interface RangerPolicyEvaluator extends RangerPolicyResourceEvaluator {
 	class PolicyEvalOrderComparator implements Comparator<RangerPolicyEvaluator>, Serializable {
 		@Override
 		public int compare(RangerPolicyEvaluator me, RangerPolicyEvaluator other) {
+			int result = Integer.compare(other.getPolicyPriority(), me.getPolicyPriority());
+
+			return result == 0 ? compareNormal(me, other) : result;
+		}
+
+		private int compareNormal(RangerPolicyEvaluator me, RangerPolicyEvaluator other) {
 			int result;
 
 			if (me.hasDeny() && !other.hasDeny()) {
@@ -102,4 +114,27 @@ public interface RangerPolicyEvaluator extends RangerPolicyResourceEvaluator {
 		}
 	}
 
+	class PolicyNameComparator implements Comparator<RangerPolicyEvaluator>, Serializable {
+		@Override
+		public int compare(RangerPolicyEvaluator me, RangerPolicyEvaluator other) {
+			int result = Integer.compare(other.getPolicyPriority(), me.getPolicyPriority());
+
+			return result == 0 ? compareNormal(me, other) : result;
+		}
+
+		private int compareNormal(RangerPolicyEvaluator me, RangerPolicyEvaluator other) {
+			final int result;
+
+			if (me.hasDeny() && !other.hasDeny()) {
+				result = -1;
+			} else if (!me.hasDeny() && other.hasDeny()) {
+				result = 1;
+			} else {
+				result = me.getPolicy().getName().compareTo(other.getPolicy().getName());
+			}
+
+			return result;
+		}
+	}
+
 }

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerPolicyItemEvaluator.java
----------------------------------------------------------------------
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerPolicyItemEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerPolicyItemEvaluator.java
index e486403..bd61cfd 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerPolicyItemEvaluator.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerPolicyItemEvaluator.java
@@ -24,6 +24,7 @@ import java.util.List;
 import java.util.Set;
 
 import org.apache.ranger.plugin.conditionevaluator.RangerConditionEvaluator;
+import org.apache.ranger.plugin.model.RangerPolicy;
 import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItem;
 import org.apache.ranger.plugin.policyengine.RangerAccessRequest;
 import org.apache.ranger.plugin.policyengine.RangerAccessResult;
@@ -65,6 +66,6 @@ public interface RangerPolicyItemEvaluator {
 			return Integer.compare(me.getEvalOrder(), other.getEvalOrder());
 		}
 	}
-	void updateAccessResult(RangerAccessResult result, RangerPolicyResourceMatcher.MatchType matchType, Long policyId);
+	void updateAccessResult(RangerAccessResult result, RangerPolicyResourceMatcher.MatchType matchType, RangerPolicy policy);
 
 }

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerValidityScheduleEvaluator.java
----------------------------------------------------------------------
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerValidityScheduleEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerValidityScheduleEvaluator.java
new file mode 100644
index 0000000..8f996d1
--- /dev/null
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerValidityScheduleEvaluator.java
@@ -0,0 +1,588 @@
+/*
+ * 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.ranger.plugin.policyevaluator;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.ranger.plugin.model.RangerValidityRecurrence;
+import org.apache.ranger.plugin.model.RangerValiditySchedule;
+import org.apache.ranger.plugin.resourcematcher.ScheduledTimeAlwaysMatcher;
+import org.apache.ranger.plugin.resourcematcher.ScheduledTimeExactMatcher;
+import org.apache.ranger.plugin.resourcematcher.ScheduledTimeMatcher;
+import org.apache.ranger.plugin.resourcematcher.ScheduledTimeRangeMatcher;
+import org.apache.ranger.plugin.util.RangerPerfTracer;
+
+import javax.annotation.Nonnull;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.List;
+import java.util.TimeZone;
+
+public class RangerValidityScheduleEvaluator {
+    private static final Log LOG = LogFactory.getLog(RangerValidityScheduleEvaluator.class);
+    private static final Log PERF_LOG = LogFactory.getLog("test.perf.RangerValidityScheduleEvaluator");
+
+    private final static TimeZone defaultTZ = TimeZone.getDefault();
+
+    private static final ThreadLocal<DateFormat> DATE_FORMATTER = new ThreadLocal<DateFormat>() {
+        @Override
+        protected DateFormat initialValue() {
+            return new SimpleDateFormat(RangerValiditySchedule.VALIDITY_SCHEDULE_DATE_STRING_SPECIFICATION);
+        }
+    };
+
+    private final Date                            startTime;
+    private final Date                            endTime;
+    private final String                          timeZone;
+    private final List<RangerRecurrenceEvaluator> recurrenceEvaluators = new ArrayList<>();
+
+    public RangerValidityScheduleEvaluator(@Nonnull RangerValiditySchedule validitySchedule) {
+        this(validitySchedule.getStartTime(), validitySchedule.getEndTime(), validitySchedule.getTimeZone(), validitySchedule.getRecurrences());
+    }
+
+    public RangerValidityScheduleEvaluator(String startTimeStr, String endTimeStr, String timeZone, List<RangerValidityRecurrence> recurrences) {
+        Date startTime = null;
+        Date endTime   = null;
+
+        if (startTimeStr != null) {
+            try {
+                startTime = DATE_FORMATTER.get().parse(startTimeStr);
+            } catch (ParseException exception) {
+                LOG.error("Error parsing startTime:[" + startTimeStr + "]", exception);
+            }
+        }
+
+        if (endTimeStr != null) {
+            try {
+                endTime = DATE_FORMATTER.get().parse(endTimeStr);
+            } catch (ParseException exception) {
+                LOG.error("Error parsing endTime:[" + endTimeStr + "]", exception);
+            }
+        }
+
+        this.startTime = startTime;
+        this.endTime   = endTime;
+        this.timeZone  = timeZone;
+
+        if (CollectionUtils.isNotEmpty(recurrences)) {
+            for (RangerValidityRecurrence recurrence : recurrences) {
+                recurrenceEvaluators.add(new RangerRecurrenceEvaluator(recurrence));
+            }
+        }
+    }
+
+    public boolean isApplicable(long accessTime) {
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("===> isApplicable(accessTime=" + accessTime + ")");
+        }
+
+        boolean          ret  = false;
+        RangerPerfTracer perf = null;
+
+        if (RangerPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
+            perf = RangerPerfTracer.getPerfTracer(PERF_LOG, "RangerValidityScheduleEvaluator.isApplicable(accessTime=" + accessTime + ")");
+        }
+
+        long startTimeInMSs = startTime == null ? 0 : startTime.getTime();
+        long endTimeInMSs   = endTime == null ? 0 : endTime.getTime();
+
+        if (StringUtils.isNotBlank(timeZone)) {
+            TimeZone targetTZ = TimeZone.getTimeZone(timeZone);
+
+            if (startTimeInMSs > 0) {
+                startTimeInMSs = getAdjustedTime(startTimeInMSs, targetTZ);
+            }
+
+            if (endTimeInMSs > 0) {
+                endTimeInMSs = getAdjustedTime(endTimeInMSs, targetTZ);
+            }
+        }
+
+        if ((startTimeInMSs == 0 || accessTime >= startTimeInMSs) && (endTimeInMSs == 0 || accessTime <= endTimeInMSs)) {
+            if (CollectionUtils.isEmpty(recurrenceEvaluators)) {
+                ret = true;
+            } else {
+                Calendar now = new GregorianCalendar();
+                now.setTime(new Date(accessTime));
+
+                for (RangerRecurrenceEvaluator recurrenceEvaluator : recurrenceEvaluators) {
+                    ret = recurrenceEvaluator.isApplicable(now);
+
+                    if (ret) {
+                        break;
+                    }
+                }
+            }
+        }
+
+        RangerPerfTracer.log(perf);
+
+	    if (LOG.isDebugEnabled()) {
+		    LOG.debug("<=== isApplicable(accessTime=" + accessTime + ") :" + ret);
+	    }
+        return ret;
+    }
+
+    public static long getAdjustedTime(long localTime, TimeZone timeZone) {
+        long ret = localTime;
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Input:[" + new Date(localTime) + ", target-timezone" + timeZone + "], default-timezone:[" + defaultTZ + "]");
+        }
+
+        if (!defaultTZ.equals(timeZone)) {
+            int targetOffset  = timeZone.getOffset(localTime);
+            int defaultOffset = defaultTZ.getOffset(localTime);
+            int diff          = defaultOffset - targetOffset;
+
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("Offset of target-timezone from UTC :[" + targetOffset + "]");
+                LOG.debug("Offset of default-timezone from UTC :[" + defaultOffset + "]");
+                LOG.debug("Difference between default-timezone and target-timezone :[" + diff + "]");
+            }
+
+            ret += diff;
+        }
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Output:[" + new Date(ret) + "]");
+        }
+
+        return ret;
+    }
+
+    static class RangerRecurrenceEvaluator {
+        private final List<ScheduledTimeMatcher> minutes     = new ArrayList<>();
+        private final List<ScheduledTimeMatcher> hours       = new ArrayList<>();
+        private final List<ScheduledTimeMatcher> daysOfMonth = new ArrayList<>();
+        private final List<ScheduledTimeMatcher> daysOfWeek  = new ArrayList<>();
+        private final List<ScheduledTimeMatcher> months      = new ArrayList<>();
+        private final List<ScheduledTimeMatcher> years       = new ArrayList<>();
+        private final RangerValidityRecurrence   recurrence;
+        private       int                        intervalInMinutes = 0;
+
+
+        public RangerRecurrenceEvaluator(RangerValidityRecurrence recurrence) {
+            this.recurrence = recurrence;
+
+            if (recurrence != null) {
+                intervalInMinutes = RangerValidityRecurrence.ValidityInterval.getValidityIntervalInMinutes(recurrence.getInterval());
+
+                if (intervalInMinutes > 0) {
+                    addScheduledTime(RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.minute, minutes);
+                    addScheduledTime(RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.hour, hours);
+                    addScheduledTime(RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.dayOfMonth, daysOfMonth);
+                    addScheduledTime(RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.dayOfWeek, daysOfWeek);
+                    addScheduledTime(RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.month, months);
+                    addScheduledTime(RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.year, years);
+                }
+            }
+        }
+
+        public boolean isApplicable(Calendar now) {
+            boolean ret = false;
+
+            RangerPerfTracer perf = null;
+
+            if (RangerPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
+                perf = RangerPerfTracer.getPerfTracer(PERF_LOG, "RangerRecurrenceEvaluator.isApplicable(accessTime=" + now.getTime().getTime() + ")");
+            }
+
+            if (recurrence != null && intervalInMinutes > 0) { // recurring schedule
+
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("Access-Time:[" + now.getTime() + "]");
+                }
+
+                Calendar startOfInterval = getClosestPastEpoch(now);
+
+                if (startOfInterval != null) {
+                    if (LOG.isDebugEnabled()) {
+                        LOG.debug("Start-of-Interval:[" + startOfInterval.getTime() + "]");
+                    }
+
+                    Calendar endOfInterval = (Calendar) startOfInterval.clone();
+                    endOfInterval.add(Calendar.MINUTE, recurrence.getInterval().getMinutes());
+                    endOfInterval.add(Calendar.HOUR, recurrence.getInterval().getHours());
+                    endOfInterval.add(Calendar.DAY_OF_MONTH, recurrence.getInterval().getDays());
+
+                    endOfInterval.getTime();    // for recomputation
+                    now.getTime();
+
+                    if (LOG.isDebugEnabled()) {
+                        LOG.debug("End-of-Interval:[" + endOfInterval.getTime() + "]");
+                    }
+
+                    ret = startOfInterval.compareTo(now) <= 0 && endOfInterval.compareTo(now) >= 0;
+                }
+
+            } else {
+                ret = true;
+            }
+
+            RangerPerfTracer.log(perf);
+            return ret;
+        }
+
+        private void addScheduledTime(RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec fieldSpec, List<ScheduledTimeMatcher> list) {
+            final String  str     = recurrence.getSchedule().getFieldValue(fieldSpec);
+            final boolean isMonth = fieldSpec == RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.month;
+
+            if (StringUtils.isNotBlank(str)) {
+                String[] specs = str.split(",");
+
+                for (String spec : specs) {
+                    String[] range = spec.split("-");
+
+                    if (range.length == 1) {
+                        if (StringUtils.equals(range[0], RangerValidityRecurrence.RecurrenceSchedule.WILDCARD)) {
+                            list.clear();
+                            list.add(new ScheduledTimeAlwaysMatcher());
+
+                            break;
+                        } else {
+                            list.add(new ScheduledTimeExactMatcher(Integer.valueOf(range[0]) - (isMonth ? 1 : 0)));
+                        }
+                    } else {
+                        if (StringUtils.equals(range[0], RangerValidityRecurrence.RecurrenceSchedule.WILDCARD) || StringUtils.equals(range[1], RangerValidityRecurrence.RecurrenceSchedule.WILDCARD)) {
+                            list.clear();
+                            list.add(new ScheduledTimeAlwaysMatcher());
+
+                            break;
+                        } else {
+                            list.add(new ScheduledTimeRangeMatcher(Integer.valueOf(range[0]) - (isMonth ? 1 : 0), Integer.valueOf(range[1]) - (isMonth ? 1 : 0)));
+                        }
+                    }
+                }
+
+                Collections.reverse(list);
+            }
+
+        }
+
+    /*
+    Given a Calendar object, get the closest, earlier Calendar object based on configured validity schedules.
+    Returns - a valid Calendar object. Throws exception if any errors during processing or no suitable Calendar object is found.
+    Description - Typically, a caller will call this with Calendar constructed with current time, and use returned object
+                along with specified interval to ensure that next schedule time is after the input Calendar.
+    Algorithm -   This involves doing a Calendar arithmetic (subtraction) with borrow. The tricky parts are ensuring that
+                  Calendar arithmetic yields a valid Calendar object.
+                    - Start with minutes, and then hours.
+                    - Must make sure that the later of the two Calendars - one computed with dayOfMonth, another computed with
+                      dayOfWeek - is picked
+                    - For dayOfMonth calculation, consider that months have different number of days
+    */
+
+        private class ValueWithBorrow {
+            int value;
+            boolean borrow;
+
+            ValueWithBorrow() {
+            }
+
+            ValueWithBorrow(int value) {
+                this(value, false);
+            }
+
+            ValueWithBorrow(int value, boolean borrow) {
+                this.value = value;
+                this.borrow = borrow;
+            }
+
+            void setValue(int value) {
+                this.value = value;
+            }
+
+            void setBorrow(boolean borrow) {
+                this.borrow = borrow;
+            }
+
+            int getValue() {
+                return value;
+            }
+
+            boolean getBorrow() {
+                return borrow;
+            }
+
+            @Override
+            public String toString() {
+                return "value=" + value + ", borrow=" + borrow;
+            }
+        }
+
+        private Calendar getClosestPastEpoch(Calendar current) {
+            Calendar ret = null;
+
+            try {
+                ValueWithBorrow input = new ValueWithBorrow();
+
+                input.setValue(current.get(Calendar.MINUTE));
+                input.setBorrow(false);
+                ValueWithBorrow closestMinute = getPastFieldValueWithBorrow(RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.minute, minutes, input);
+
+                input.setValue(current.get(Calendar.HOUR_OF_DAY));
+                input.setBorrow(closestMinute.borrow);
+                ValueWithBorrow closestHour = getPastFieldValueWithBorrow(RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.hour, hours, input);
+
+                Calendar dayOfMonthCalendar = getClosestDayOfMonth(current, closestMinute, closestHour);
+
+                Calendar dayOfWeekCalendar = getClosestDayOfWeek(current, closestMinute, closestHour);
+
+                ret = getEarlierCalendar(dayOfMonthCalendar, dayOfWeekCalendar);
+
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("ClosestPastEpoch:[" + (ret != null ? ret.getTime() : null) + "]");
+                }
+
+            } catch (Exception e) {
+                LOG.error("Could not find ClosestPastEpoch, Exception=", e);
+            }
+            return ret;
+        }
+
+        private Calendar getClosestDayOfMonth(Calendar current, ValueWithBorrow closestMinute, ValueWithBorrow closestHour) throws Exception {
+            Calendar ret = null;
+            if (StringUtils.isNotBlank(recurrence.getSchedule().getDayOfMonth())) {
+                int initialDayOfMonth = current.get(Calendar.DAY_OF_MONTH);
+
+                int currentDayOfMonth = initialDayOfMonth, currentMonth = current.get(Calendar.MONTH), currentYear = current.get(Calendar.YEAR);
+                int maximumDaysInPreviousMonth = getMaximumValForPreviousMonth(current);
+
+                if (closestHour.borrow) {
+                    initialDayOfMonth--;
+                    Calendar dayOfMonthCalc = (GregorianCalendar) current.clone();
+                    dayOfMonthCalc.add(Calendar.DAY_OF_MONTH, -1);
+                    dayOfMonthCalc.getTime();
+                    int previousDayOfMonth = dayOfMonthCalc.get(Calendar.DAY_OF_MONTH);
+                    if (initialDayOfMonth < previousDayOfMonth) {
+                        if (LOG.isDebugEnabled()) {
+                            LOG.debug("Need to borrow from previous month, initialDayOfMonth:[" + initialDayOfMonth + "], previousDayOfMonth:[" + previousDayOfMonth + "], dayOfMonthCalc:[" + dayOfMonthCalc.getTime() + "]");
+                        }
+                        currentDayOfMonth = previousDayOfMonth;
+                        currentMonth = dayOfMonthCalc.get(Calendar.MONTH);
+                        currentYear = dayOfMonthCalc.get(Calendar.YEAR);
+                        maximumDaysInPreviousMonth = getMaximumValForPreviousMonth(dayOfMonthCalc);
+                    } else if (initialDayOfMonth == previousDayOfMonth) {
+                        if (LOG.isDebugEnabled()) {
+                            LOG.debug("No need to borrow from previous month, initialDayOfMonth:[" + initialDayOfMonth + "], previousDayOfMonth:[" + previousDayOfMonth + "]");
+                        }
+                    } else {
+                        LOG.error("Should not get here, initialDayOfMonth:[" + initialDayOfMonth + "], previousDayOfMonth:[" + previousDayOfMonth + "]");
+                        throw new Exception("Should not get here, initialDayOfMonth:[" + initialDayOfMonth + "], previousDayOfMonth:[" + previousDayOfMonth + "]");
+                    }
+                }
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("currentDayOfMonth:[" + currentDayOfMonth + "], maximumDaysInPreviourMonth:[" + maximumDaysInPreviousMonth + "]");
+                }
+                ValueWithBorrow input = new ValueWithBorrow();
+                input.setValue(currentDayOfMonth);
+                input.setBorrow(false);
+                ValueWithBorrow closestDayOfMonth = null;
+                do {
+                    int i = 0;
+                    try {
+                        closestDayOfMonth = getPastFieldValueWithBorrow(RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.dayOfMonth, daysOfMonth, input, maximumDaysInPreviousMonth);
+                    } catch (Exception e) {
+                        i++;
+                        Calendar c = (GregorianCalendar) current.clone();
+                        c.set(Calendar.YEAR, currentYear);
+                        c.set(Calendar.MONTH, currentMonth);
+                        c.set(Calendar.DAY_OF_MONTH, currentDayOfMonth);
+                        c.add(Calendar.MONTH, -i);
+                        c.getTime();
+                        currentMonth = c.get(Calendar.MONTH);
+                        currentYear = c.get(Calendar.YEAR);
+                        currentDayOfMonth = c.get(Calendar.DAY_OF_MONTH);
+                        maximumDaysInPreviousMonth = getMaximumValForPreviousMonth(c);
+                        input.setValue(currentDayOfMonth);
+                        input.setBorrow(false);
+                    }
+                } while (closestDayOfMonth == null);
+
+                // Build calendar for dayOfMonth
+                ret = new GregorianCalendar();
+                ret.set(Calendar.DAY_OF_MONTH, closestDayOfMonth.value);
+                ret.set(Calendar.HOUR_OF_DAY, closestHour.value);
+                ret.set(Calendar.MINUTE, closestMinute.value);
+                ret.set(Calendar.SECOND, 0);
+                ret.set(Calendar.MILLISECOND, 0);
+
+                ret.set(Calendar.YEAR, currentYear);
+
+                if (closestDayOfMonth.borrow) {
+                    ret.set(Calendar.MONTH, currentMonth - 1);
+                } else {
+                    ret.set(Calendar.MONTH, currentMonth);
+                }
+                ret.getTime(); // For recomputation
+
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("Best guess using DAY_OF_MONTH:[" + ret.getTime() + "]");
+                }
+            }
+            return ret;
+        }
+
+        private Calendar getClosestDayOfWeek(Calendar current, ValueWithBorrow closestMinute, ValueWithBorrow closestHour) throws Exception {
+            Calendar ret = null;
+            if (StringUtils.isNotBlank(recurrence.getSchedule().getDayOfWeek())) {
+                ValueWithBorrow input = new ValueWithBorrow();
+
+                input.setValue(current.get(Calendar.DAY_OF_WEEK));
+                input.setBorrow(closestHour.borrow);
+
+
+                ValueWithBorrow closestDayOfWeek = getPastFieldValueWithBorrow(RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.dayOfWeek, daysOfWeek, input);
+
+                int daysToGoback = closestHour.borrow ? 1 : 0;
+                int range = RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.dayOfWeek.maximum - RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.dayOfWeek.minimum + 1;
+
+                if (closestDayOfWeek.borrow) {
+                    if (input.value - closestDayOfWeek.value != daysToGoback) {
+                        daysToGoback = range + input.value - closestDayOfWeek.value;
+                    }
+                } else {
+                    daysToGoback = input.value - closestDayOfWeek.value;
+                }
+
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("Need to go back [" + daysToGoback + "] days to match dayOfWeek");
+                }
+
+                ret = (GregorianCalendar) current.clone();
+                ret.set(Calendar.MINUTE, closestMinute.value);
+                ret.set(Calendar.HOUR_OF_DAY, closestHour.value);
+                ret.add(Calendar.DAY_OF_MONTH, (0 - daysToGoback));
+	            ret.set(Calendar.SECOND, 0);
+	            ret.set(Calendar.MILLISECOND, 0);
+
+                ret.getTime();
+
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("Best guess using DAY_OF_WEEK:[" + ret.getTime() + "]");
+                }
+            }
+            return ret;
+
+        }
+
+        private int getMaximumValForPreviousMonth(Calendar current) {
+            Calendar cal = (Calendar) current.clone();
+            cal.add(Calendar.MONTH, -1);
+            cal.getTime(); // For recomputation
+
+            return cal.getActualMaximum(Calendar.DAY_OF_MONTH);
+        }
+
+        private Calendar getEarlierCalendar(Calendar dayOfMonthCalendar, Calendar dayOfWeekCalendar) throws Exception {
+
+            Calendar withDayOfMonth = fillOutCalendar(dayOfMonthCalendar);
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("dayOfMonthCalendar:[" + (withDayOfMonth != null ? withDayOfMonth.getTime() : null) + "]");
+            }
+
+            Calendar withDayOfWeek = fillOutCalendar(dayOfWeekCalendar);
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("dayOfWeekCalendar:[" + (withDayOfWeek != null ? withDayOfWeek.getTime() : null) + "]");
+            }
+
+            if (withDayOfMonth != null && withDayOfWeek != null) {
+                return withDayOfMonth.after(withDayOfWeek) ? withDayOfMonth : withDayOfWeek;
+            } else if (withDayOfMonth == null) {
+                return withDayOfWeek;
+            } else {
+                return withDayOfMonth;
+            }
+        }
+
+        private Calendar fillOutCalendar(Calendar calendar) throws Exception {
+            Calendar ret = null;
+
+            if (calendar != null) {
+                ValueWithBorrow input = new ValueWithBorrow(calendar.get(Calendar.MONTH));
+                ValueWithBorrow closestMonth = getPastFieldValueWithBorrow(RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.month, months, input);
+
+                input.setValue(calendar.get(Calendar.YEAR));
+                input.setBorrow(closestMonth.borrow);
+                ValueWithBorrow closestYear = getPastFieldValueWithBorrow(RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.year, years, input);
+
+                // Build calendar
+                ret = (Calendar) calendar.clone();
+                ret.set(Calendar.YEAR, closestYear.value);
+                ret.set(Calendar.MONTH, closestMonth.value);
+                ret.set(Calendar.SECOND, 0);
+
+                ret.getTime(); // for recomputation
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("Filled-out-Calendar:[" + ret.getTime() + "]");
+                }
+            }
+            return ret;
+        }
+
+        private ValueWithBorrow getPastFieldValueWithBorrow(RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec fieldSpec, List<ScheduledTimeMatcher> searchList, ValueWithBorrow input) throws Exception {
+            return getPastFieldValueWithBorrow(fieldSpec, searchList, input, fieldSpec.maximum);
+        }
+
+        private ValueWithBorrow getPastFieldValueWithBorrow(RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec fieldSpec, List<ScheduledTimeMatcher> searchList, ValueWithBorrow input, int maximum) throws Exception {
+
+            ValueWithBorrow ret;
+            boolean borrow = false;
+
+            int value = input.value - (input.borrow ? 1 : 0);
+
+            if (CollectionUtils.isNotEmpty(searchList)) {
+                int range = fieldSpec.maximum - fieldSpec.minimum + 1;
+
+                for (int i = 0; i < range; i++, value--) {
+                    if (value < fieldSpec.minimum) {
+                        value = maximum;
+                        borrow = true;
+                    }
+                    for (ScheduledTimeMatcher time : searchList) {
+                        if (time.isMatch(value)) {
+                            if (LOG.isDebugEnabled()) {
+                                LOG.debug("Found match in field:[" + fieldSpec + "], value:[" + value + "], borrow:[" + borrow + "], maximum:[" + maximum + "]");
+                            }
+                            return new ValueWithBorrow(value, borrow);
+                        }
+                    }
+                }
+                // Not found
+                throw new Exception("No match found in field:[" + fieldSpec + "] for [input=" + input + "]");
+            } else {
+                if (value < fieldSpec.minimum) {
+                    value = maximum;
+                }
+                ret = new ValueWithBorrow(value, false);
+            }
+            return ret;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/ScheduledTimeAlwaysMatcher.java
----------------------------------------------------------------------
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/ScheduledTimeAlwaysMatcher.java b/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/ScheduledTimeAlwaysMatcher.java
new file mode 100644
index 0000000..414efcf
--- /dev/null
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/ScheduledTimeAlwaysMatcher.java
@@ -0,0 +1,27 @@
+/*
+ * 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.ranger.plugin.resourcematcher;
+
+public class ScheduledTimeAlwaysMatcher implements ScheduledTimeMatcher {
+    @Override
+    public boolean isMatch(int currentTime) {
+        return true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/ScheduledTimeExactMatcher.java
----------------------------------------------------------------------
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/ScheduledTimeExactMatcher.java b/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/ScheduledTimeExactMatcher.java
new file mode 100644
index 0000000..da71040
--- /dev/null
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/ScheduledTimeExactMatcher.java
@@ -0,0 +1,32 @@
+/*
+ * 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.ranger.plugin.resourcematcher;
+
+public class ScheduledTimeExactMatcher implements ScheduledTimeMatcher {
+    private int scheduledTime;
+
+    public ScheduledTimeExactMatcher(int scheduledTime) {
+        this.scheduledTime = scheduledTime;
+    }
+    @Override
+    public boolean isMatch(int currentTime) {
+        return currentTime == scheduledTime;
+    }
+}

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/ScheduledTimeMatcher.java
----------------------------------------------------------------------
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/ScheduledTimeMatcher.java b/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/ScheduledTimeMatcher.java
new file mode 100644
index 0000000..3c388ca
--- /dev/null
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/ScheduledTimeMatcher.java
@@ -0,0 +1,24 @@
+/*
+ * 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.ranger.plugin.resourcematcher;
+
+public interface ScheduledTimeMatcher {
+    boolean isMatch(int targetTime);
+}

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/ScheduledTimeRangeMatcher.java
----------------------------------------------------------------------
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/ScheduledTimeRangeMatcher.java b/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/ScheduledTimeRangeMatcher.java
new file mode 100644
index 0000000..8c0bcd5
--- /dev/null
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/ScheduledTimeRangeMatcher.java
@@ -0,0 +1,34 @@
+/*
+ * 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.ranger.plugin.resourcematcher;
+
+public class ScheduledTimeRangeMatcher implements ScheduledTimeMatcher {
+    private int lowerBound;
+    private int upperBound;
+
+    public ScheduledTimeRangeMatcher(int lowerBound, int upperBound) {
+        this.lowerBound = lowerBound;
+        this.upperBound = upperBound;
+    }
+    @Override
+    public boolean isMatch(int currentTime) {
+        return currentTime >= lowerBound && currentTime <= upperBound;
+    }
+}

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/agents-common/src/main/java/org/apache/ranger/plugin/store/AbstractPredicateUtil.java
----------------------------------------------------------------------
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/store/AbstractPredicateUtil.java b/agents-common/src/main/java/org/apache/ranger/plugin/store/AbstractPredicateUtil.java
index 1e3f145..4d2bc62 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/store/AbstractPredicateUtil.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/store/AbstractPredicateUtil.java
@@ -96,7 +96,8 @@ public class AbstractPredicateUtil {
 		addPredicateForPartialPolicyName(filter.getParam(SearchFilter.POLICY_NAME_PARTIAL), predicates);
 		addPredicateForResourceSignature(filter.getParam(SearchFilter.RESOURCE_SIGNATURE), predicates);
 		addPredicateForPolicyType(filter.getParam(SearchFilter.POLICY_TYPE), predicates);
-                addPredicateForPartialPolicyLabels(filter.getParam(SearchFilter.POLICY_LABELS_PARTIAL), predicates);
+		addPredicateForPolicyPriority(filter.getParam(SearchFilter.POLICY_PRIORITY), predicates);
+		addPredicateForPartialPolicyLabels(filter.getParam(SearchFilter.POLICY_LABELS_PARTIAL), predicates);
 	}
 
 	public Comparator<RangerBaseModelObject> getSorter(SearchFilter filter) {
@@ -825,45 +826,86 @@ public class AbstractPredicateUtil {
 		return ret;
 	}
 
-        private Predicate addPredicateForPartialPolicyLabels(final String policyLabels, List<Predicate> predicates) {
-                if (StringUtils.isEmpty(policyLabels)) {
-                        return null;
-                }
-
-                Predicate ret = new Predicate() {
-                        @Override
-                        public boolean evaluate(Object object) {
-                                if (object == null) {
-                                        return false;
-                                }
-                                boolean ret = false;
-
-                                if (object instanceof RangerPolicy) {
-                                        RangerPolicy policy = (RangerPolicy) object;
-                                        //	exact match
+	private Predicate addPredicateForPartialPolicyLabels(final String policyLabels, List<Predicate> predicates) {
+		if (StringUtils.isEmpty(policyLabels)) {
+			return null;
+		}
+
+		Predicate ret = new Predicate() {
+			@Override
+			public boolean evaluate(Object object) {
+				if (object == null) {
+					return false;
+				}
+				boolean ret = false;
+
+				if (object instanceof RangerPolicy) {
+					RangerPolicy policy = (RangerPolicy) object;
+					//	exact match
                                         /*if (policy.getPolicyLabels().contains(policyLabels)) {
                                                 ret = true;
                                         }*/
-                                        /*partial match*/
-                                        for (String label :policy.getPolicyLabels()){
-                                                ret = StringUtils.containsIgnoreCase(label, policyLabels);
-                                                if(ret){
-                                                        return ret;
-                                                }
-                                        }
-
-                                } else {
-                                        ret = true;
-                                }
-                                return ret;
-                        }
-                };
-                if (predicates != null) {
-                        predicates.add(ret);
-                }
-
-                return ret;
-        }
+					/*partial match*/
+					for (String label :policy.getPolicyLabels()){
+						ret = StringUtils.containsIgnoreCase(label, policyLabels);
+						if(ret){
+							return ret;
+						}
+					}
+
+				} else {
+					ret = true;
+				}
+				return ret;
+			}
+		};
+		if (predicates != null) {
+			predicates.add(ret);
+		}
+
+		return ret;
+	}
+
+	private Predicate addPredicateForPolicyPriority(final String policyPriority, List<Predicate> predicates) {
+		if(StringUtils.isEmpty(policyPriority)) {
+			return null;
+		}
+
+		Predicate ret = new Predicate() {
+			@Override
+			public boolean evaluate(Object object) {
+				if (object == null) {
+					return false;
+				}
+
+				boolean ret = true;
+
+				if (object instanceof RangerPolicy) {
+					RangerPolicy policy = (RangerPolicy) object;
+
+					Integer priority = policy.getPolicyPriority() != null ? policy.getPolicyPriority() : RangerPolicy.POLICY_PRIORITY_NORMAL;
+
+					if (priority == RangerPolicy.POLICY_PRIORITY_NORMAL) {
+						ret = StringUtils.equalsIgnoreCase(policyPriority, policy.POLICY_PRIORITY_NAME_NORMAL)
+								|| StringUtils.equalsIgnoreCase(policyPriority, priority.toString());
+					} else if (priority == RangerPolicy.POLICY_PRIORITY_OVERRIDE) {
+						ret = StringUtils.equalsIgnoreCase(policyPriority, policy.POLICY_PRIORITY_NAME_OVERRIDE)
+								|| StringUtils.equalsIgnoreCase(policyPriority, priority.toString());
+					} else {
+						ret = false;
+					}
+				}
+				return ret;
+			}
+
+		};
+
+		if(predicates != null) {
+			predicates.add(ret);
+		}
+
+		return ret;
+	}
 
 
 	public Predicate createPredicateForResourceSignature(final String policySignature) {

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/agents-common/src/main/java/org/apache/ranger/plugin/util/SearchFilter.java
----------------------------------------------------------------------
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/SearchFilter.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/SearchFilter.java
index 4a8f139..33f82dd 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/util/SearchFilter.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/SearchFilter.java
@@ -51,6 +51,7 @@ public class SearchFilter {
 	public static final String SORT_BY         = "sortBy";
 	public static final String RESOURCE_SIGNATURE = "resourceSignature:";     // search
 	public static final String POLICY_TYPE     = "policyType";    // search
+	public static final String POLICY_PRIORITY     = "policyPriority";    // search
     public static final String GUID		   = "guid"; //search
     public static final String POLICY_LABEL     = "policyLabel";    // search
     public static final String POLICY_LABELS_PARTIAL     = "policyLabelsPartial";    // search

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestPolicyEngine.java
----------------------------------------------------------------------
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 f8c692b..97ae8a2 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
@@ -27,6 +27,7 @@ import com.google.gson.JsonElement;
 import com.google.gson.JsonParseException;
 import com.google.gson.reflect.TypeToken;
 
+import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang.StringUtils;
 import org.apache.ranger.audit.provider.AuditHandler;
 import org.apache.ranger.audit.provider.AuditProviderFactory;
@@ -35,7 +36,11 @@ import org.apache.ranger.plugin.audit.RangerDefaultAuditHandler;
 import org.apache.ranger.plugin.contextenricher.RangerTagForEval;
 import org.apache.ranger.plugin.model.RangerPolicy;
 import org.apache.ranger.plugin.model.RangerServiceDef;
+import org.apache.ranger.plugin.model.RangerValiditySchedule;
+import org.apache.ranger.plugin.model.validation.RangerValidityScheduleValidator;
+import org.apache.ranger.plugin.model.validation.ValidationFailureDetails;
 import org.apache.ranger.plugin.policyengine.TestPolicyEngine.PolicyEngineTestCase.TestData;
+import org.apache.ranger.plugin.policyevaluator.RangerValidityScheduleEvaluator;
 import org.apache.ranger.plugin.util.RangerAccessRequestUtil;
 import org.apache.ranger.plugin.util.RangerRequestedResources;
 import org.apache.ranger.plugin.util.ServicePolicies;
@@ -50,6 +55,8 @@ import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
 import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Date;
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
@@ -62,7 +69,7 @@ public class TestPolicyEngine {
 
 	@BeforeClass
 	public static void setUpBeforeClass() throws Exception {
-		gsonBuilder = new GsonBuilder().setDateFormat("yyyyMMdd-HH:mm:ss.SSS-Z")
+		gsonBuilder = new GsonBuilder().setDateFormat("yyyyMMdd-HH:mm:ss.SSSZ")
 				.setPrettyPrinting()
 				.registerTypeAdapter(RangerAccessRequest.class, new RangerAccessRequestDeserializer())
 				.registerTypeAdapter(RangerAccessResource.class, new RangerResourceDeserializer())
@@ -301,6 +308,12 @@ public class TestPolicyEngine {
 
 		runTestsFromResourceFiles(resourceFiles);
 	}
+	@Test
+	public void testPolicyEngine_temporary() {
+		String[] resourceFiles = {"/policyengine/test_policyengine_temporary.json"};
+
+		runTestsFromResourceFiles(resourceFiles);
+	}
 
 	@Test
 	public void testPolicyEngine_atlas() {
@@ -505,8 +518,88 @@ public class TestPolicyEngine {
 			public List<RangerPolicy> tagPolicies;
 		}
 	}
-	
-	static class RangerAccessRequestDeserializer implements JsonDeserializer<RangerAccessRequest> {
+
+    static class ValiditySchedulerTestResult {
+        boolean isValid;
+        int validationFailureCount;
+        boolean isApplicable;
+    }
+
+    static class ValiditySchedulerTestCase {
+        String name;
+        List<RangerValiditySchedule> validitySchedules;
+        Date accessTime;
+        ValiditySchedulerTestResult result;
+    }
+
+    @Test
+    public void testValiditySchedularInvalid() {
+        String resourceName = "/policyengine/validityscheduler/test-validity-schedules-invalid.json";
+
+        runValiditySchedulerTests(resourceName);
+    }
+
+    @Test
+    public void testValiditySchedularValid() {
+        String resourceName = "/policyengine/validityscheduler/test-validity-schedules-valid.json";
+
+        runValiditySchedulerTests(resourceName);
+    }
+
+    @Test
+    public void testValiditySchedularApplicable() {
+        String resourceName = "/policyengine/validityscheduler/test-validity-schedules-valid-and-applicable.json";
+
+        runValiditySchedulerTests(resourceName);
+    }
+
+    private void runValiditySchedulerTests(String resourceName) {
+        List<ValiditySchedulerTestCase> testCases = null;
+        InputStream inStream = this.getClass().getResourceAsStream(resourceName);
+        InputStreamReader reader   = new InputStreamReader(inStream);
+        try {
+            Type listType = new TypeToken<List<ValiditySchedulerTestCase>>() {}.getType();
+            testCases = gsonBuilder.fromJson(reader, listType);
+        } catch (Exception e) {
+	        assertFalse("Exception in reading validity-scheduler test cases.", true);
+        }
+
+        assertNotNull("TestCases are null!", testCases);
+
+
+        if (CollectionUtils.isNotEmpty(testCases)) {
+            for (ValiditySchedulerTestCase testCase : testCases) {
+                boolean isValid = true;
+                List<ValidationFailureDetails> validationFailures = new ArrayList<>();
+                boolean isApplicable = false;
+
+                List<RangerValiditySchedule> validatedSchedules = new ArrayList<>();
+
+                for (RangerValiditySchedule validitySchedule : testCase.validitySchedules) {
+                    RangerValidityScheduleValidator validator = new RangerValidityScheduleValidator(validitySchedule);
+                    RangerValiditySchedule validatedSchedule = validator.validate(validationFailures);
+                    isValid = isValid && validatedSchedule != null;
+                    if (isValid) {
+                        validatedSchedules.add(validatedSchedule);
+                    }
+                }
+                if (isValid) {
+                    for (RangerValiditySchedule validSchedule : validatedSchedules) {
+                        isApplicable = new RangerValidityScheduleEvaluator(validSchedule).isApplicable(testCase.accessTime.getTime());
+                        if (isApplicable) {
+                            break;
+                        }
+                    }
+                }
+
+                assertTrue(testCase.name, isValid == testCase.result.isValid);
+                assertTrue(testCase.name, isApplicable == testCase.result.isApplicable);
+                assertTrue(testCase.name + ", [" + validationFailures +"]", validationFailures.size() == testCase.result.validationFailureCount);
+            }
+        }
+    }
+
+    static class RangerAccessRequestDeserializer implements JsonDeserializer<RangerAccessRequest> {
 		@Override
 		public RangerAccessRequest deserialize(JsonElement jsonObj, Type type,
 				JsonDeserializationContext context) throws JsonParseException {

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/agents-common/src/test/resources/log4j.xml
----------------------------------------------------------------------
diff --git a/agents-common/src/test/resources/log4j.xml b/agents-common/src/test/resources/log4j.xml
index 558e27b..ddf57b9 100644
--- a/agents-common/src/test/resources/log4j.xml
+++ b/agents-common/src/test/resources/log4j.xml
@@ -78,12 +78,12 @@
         -->
 
     <logger name="org.apache.ranger.perf.policyresourcematcher" additivity="false">
-        <level value="debug" />
+        <level value="warn" />
         <appender-ref ref="ranger_perf_appender" />
     </logger>
 
     <root>
-        <level value="warn" />
+        <level value="debug" />
         <appender-ref ref="console" />
     </root>
 

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/agents-common/src/test/resources/policyengine/test_policyengine_temporary.json
----------------------------------------------------------------------
diff --git a/agents-common/src/test/resources/policyengine/test_policyengine_temporary.json b/agents-common/src/test/resources/policyengine/test_policyengine_temporary.json
new file mode 100644
index 0000000..17cf322
--- /dev/null
+++ b/agents-common/src/test/resources/policyengine/test_policyengine_temporary.json
@@ -0,0 +1,347 @@
+{
+  "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"}
+    ],
+    "options": {
+      "enableDenyAndExceptionsInPolicies":"true"
+    },
+    "accessTypes":[
+      {"name":"read","label":"Read"},
+      {"name":"write","label":"Write"},
+      {"name":"execute","label":"Execute"}
+    ]
+  },
+  "policies":[
+    {
+      "id": 99,
+      "name": "/resource: allow: groups=public",
+      "isEnabled": true,
+      "isAuditEnabled": true,
+      "resources": {
+        "path": { "values": [ "/resource" ], "isRecursive": true }
+      },
+      "policyItems": [
+        {
+          "accesses":[
+            {"type":"read"},
+            {"type": "execute" },
+            {"type":"write"}
+          ],
+          "groups" : ["public"]
+        }
+      ]
+    },
+    {
+      "id": 1,
+      "name": "/resource: allow: users=super; deny: user=user",
+      "isEnabled": true,
+      "isAuditEnabled": true,
+      "resources": {
+        "path": { "values": [ "/resource" ], "isRecursive": true }
+      },
+      "policyPriority":1,
+      "validitySchedules": [
+        {
+          "startTime": "2018/01/12 14:32:00",
+          "endTime": "2020/02/13 12:16:00",
+          "recurrences": [
+            {
+              "schedule": {
+                "minute": "0,10,20,30,40,50",
+                "hour": "*",
+                "dayOfMonth": "*",
+                "dayOfWeek": "5,7",
+                "month": "*",
+                "year": "2018"
+              },
+              "interval": {
+                "minutes": 4
+              }
+            }
+          ]
+        }
+      ],
+      "policyItems": [
+        {
+          "accesses":[
+            {"type":"read"},
+            {"type":"write"}
+          ],
+          "users":["super"]
+        }
+      ],
+      "allowExceptions":[ ],
+      "denyPolicyItems": [
+        {
+          "accesses": [
+            { "type": "read" },
+            { "type": "execute" },
+            { "type": "write" }
+          ],
+          "users": [ "user"]
+        }
+      ],
+      "denyExceptions":[ ]
+    },
+    {
+      "id": 2,
+      "name": "/unaudited-resource: allow: users=super deny: user=user",
+      "isEnabled": true,
+      "isAuditEnabled": false,
+      "resources": {
+        "path": { "values": [ "/unaudited-resource" ], "isRecursive": true }
+      },
+      "validitySchedules": [],
+      "policyItems": [
+        {
+          "accesses":[
+            {"type":"read" },
+            {"type":"write" }
+          ],
+          "users":["super"]
+        }
+      ],
+      "allowExceptions":[ ],
+      "denyPolicyItems": [
+        {
+          "accesses": [
+            { "type": "read" },
+            { "type": "execute" },
+            { "type": "write" }
+          ],
+          "users": [ "user" ]
+        }
+      ],
+      "denyExceptions":[ ]
+    }
+  ],
+
+  "tagPolicyInfo": {
+    "serviceName":"tagdev",
+    "serviceDef": {
+      "name": "tag",
+      "id": 100,
+      "resources": [
+        { "name": "tag", "type": "string", "level": 1, "mandatory": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": false, "ignoreCase": false }, "label": "TAG", "description": "TAG" }
+      ],
+      "accessTypes": [
+        { "name": "hdfs:read", "label": "hdfs:Read"  },
+        { "name": "hdfs:write", "label": "hdfs:Write" },
+        { "name": "hdfs:execute", "label": "hdfs:Execute" }
+      ],
+      "contextEnrichers": [ ],
+      "policyConditions": [ ]
+    },
+    "tagPolicies":[
+      {
+        "id":101,
+        "name":"PII",
+        "isEnabled":true,
+        "isAuditEnabled":true,
+        "resources":{"tag":{"values":["PII"],"isRecursive":false}},
+        "policyItems":[
+          {
+            "accesses":[
+              {"type":"hdfs:read", "isAllowed":true},
+              {"type":"hdfs:write", "isAllowed":true}
+            ],
+            "users":["super"]
+          }
+        ],
+        "allowExceptions":[ ],
+        "denyPolicyItems":[
+          {
+            "accesses":[
+              {"type":"hdfs:read" },
+              {"type":"hdfs:write" }
+            ],
+            "users":["user"]
+          }
+        ],
+        "denyExceptions":[ ],
+        "policyPriority":1,
+        "validitySchedules": [
+          {
+            "startTime": "2018/01/12 14:32:00",
+            "endTime": "2020/02/13 12:16:00",
+            "recurrences": [
+              {
+                "schedule": {
+                  "minute": "0,10,20,30,40,50",
+                  "hour": "*",
+                  "dayOfMonth": "*",
+                  "dayOfWeek": "5,7",
+                  "month": "*",
+                  "year": "2018"
+                },
+                "interval": {
+                  "minutes": 5
+                }
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "id":102,
+        "name":"Unaudited-TAG",
+        "isEnabled":true,
+        "isAuditEnabled":false,
+        "resources":{"tag":{"values":["Unaudited-TAG"],"isRecursive":false}},
+        "validitySchedules": [],
+        "policyItems":[
+          {
+            "accesses":[
+              {"type":"hdfs:read" },
+              {"type":"hdfs:write" }
+            ],
+            "users":["super"]
+          }
+        ],
+        "allowExceptions":[ ],
+        "denyPolicyItems":[
+          {
+            "accesses":[
+              {"type":"hdfs:read" },
+              {"type":"hdfs:write" }
+            ],
+            "users":["user"]
+          }
+        ],
+        "denyExceptions":[ ]
+      }
+    ]
+  },
+  "tests":[
+    {
+      "name": "ALLOW 'read /resource' for u=user no tag temporarily",
+      "request": {
+        "resource": {
+          "elements": {
+            "path": "/resource"
+          }
+        },
+        "accessType": "read",
+        "user": "user",
+        "userGroups": [],
+        "requestData": "read /resource for u=user no tag temporarily",
+        "context": {
+          "TAGS": "[{\"type\":\"PII\",\"options\":{\"TAG_VALIDITY_PERIODS\":\"[{\\\"startTime\\\":\\\"2018/01/12 14:32:00\\\",\\\"endTime\\\":\\\"2020/02/13 12:16:00\\\",\\\"recurrences\\\":[{\\\"schedule\\\":{\\\"minute\\\":\\\"0,10,20,30,40,50\\\",\\\"hour\\\":\\\"*\\\",\\\"dayOfMonth\\\":\\\"*\\\",\\\"dayOfWeek\\\":\\\"5,7\\\",\\\"month\\\":\\\"*\\\",\\\"year\\\":\\\"2018\\\"},\\\"interval\\\":{\\\"days\\\":0,\\\"hours\\\":0,\\\"minutes\\\":4}}]}]\"}}]"
+        },
+        "accessTime": "20180127-10:06:00.000-0800"
+      },
+      "result": {"isAudited": true, "isAllowed": true, "policyId": 99}
+    },
+    {
+      "name": "DENY 'read /resource' for u=user tag temporarily",
+      "request": {
+        "resource": {
+          "elements": {
+            "path": "/resource"
+          }
+        },
+        "accessType": "read",
+        "user": "user",
+        "userGroups": [],
+        "requestData": "read /resource for u=user tag temporarily",
+        "context": {
+          "TAGS": "[{\"type\":\"PII\",\"options\":{\"TAG_VALIDITY_PERIODS\":\"[{\\\"startTime\\\":\\\"2018/01/12 14:32:00\\\",\\\"endTime\\\":\\\"2020/02/13 12:16:00\\\",\\\"recurrences\\\":[{\\\"schedule\\\":{\\\"minute\\\":\\\"0,10,20,30,40,50\\\",\\\"hour\\\":\\\"*\\\",\\\"dayOfMonth\\\":\\\"*\\\",\\\"dayOfWeek\\\":\\\"5,7\\\",\\\"month\\\":\\\"*\\\",\\\"year\\\":\\\"2018\\\"},\\\"interval\\\":{\\\"days\\\":0,\\\"hours\\\":0,\\\"minutes\\\":7}}]}]\"}}]"
+        },
+        "accessTime": "20180127-10:05:00.000-0800"
+      },
+      "result": {"isAudited": true, "isAllowed": false, "policyId": 101}
+    },
+    {
+      "name": "ALLOW 'read /resource' for u=user temporarily",
+      "request": {
+        "resource": {
+          "elements": {
+            "path": "/resource"
+          }
+        },
+        "accessType": "read",
+        "user": "user",
+        "userGroups": [],
+        "requestData": "read /resource temporarily",
+        "accessTime": "20180127-10:06:00.000-0800"
+      },
+      "result": {"isAudited": true, "isAllowed": true, "policyId": 99}
+    },
+    {
+      "name": "DENY 'read /resource' for u=user temporarily",
+      "request": {
+        "resource": {
+          "elements": {
+            "path": "/resource"
+          }
+        },
+        "accessType": "read",
+        "user": "user",
+        "userGroups": [],
+        "requestData": "read /resource temporarily",
+        "accessTime": "20180127-10:03:00.000-0800"
+      },
+      "result": {"isAudited": true, "isAllowed": false, "policyId": 1}
+    },
+    {
+      "name": "DENY 'read /resource' for u=user",
+      "request": {
+        "resource": { "elements": { "path": "/resource" } },
+        "accessType": "read",
+        "user": "user",
+        "userGroups": [ ],
+        "requestData": "read /resource for u=user",
+        "accessTime": "20180127-10:03:00.000-0800",
+        "context": {
+          "TAGS": "[{\"type\":\"PII\"}]"
+        }
+      },
+      "result": { "isAudited": true, "isAllowed": false, "policyId": 101 }
+    },
+    {
+      "name": "ALLOW 'read /resource' for u=super",
+      "request": {
+        "resource": { "elements": { "path": "/resource" } },
+        "accessType": "read",
+        "user": "super",
+        "userGroups": [ ],
+        "requestData": "read /resource for u=super",
+        "accessTime": "20180127-10:03:00.000-0800",
+        "context": {
+          "TAGS": "[{\"type\":\"PII\"}]"
+        }
+      },
+      "result": { "isAudited": true, "isAllowed": true, "policyId": 101 }
+    }
+    ,
+    {
+      "name": "ALLOW 'read /resource' for u=super",
+      "request": {
+        "resource": { "elements": { "path": "/resource" } },
+        "accessType": "read",
+        "user": "super",
+        "userGroups": [ ],
+        "requestData": "read /resource for u=super",
+        "accessTime": "20180127-10:03:00.000-0800"
+
+      },
+      "result": { "isAudited": true, "isAllowed": true, "policyId": 1 }
+    },
+    {
+      "name": "ALLOW 'read /resource' for u=any",
+      "request": {
+        "resource": { "elements": { "path": "/resource" } },
+        "accessType": "read",
+        "user": "any",
+        "userGroups": [ ],
+        "requestData": "read /resource for u=any"
+      },
+      "result": { "isAudited": true, "isAllowed": true, "policyId": 99 }
+    }
+
+    ]
+}