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:10 UTC

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

Repository: ranger
Updated Branches:
  refs/heads/master 703792005 -> 844315cdb


http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/tagsync/src/main/java/org/apache/ranger/tagsync/source/atlas/AtlasNotificationMapper.java
----------------------------------------------------------------------
diff --git a/tagsync/src/main/java/org/apache/ranger/tagsync/source/atlas/AtlasNotificationMapper.java b/tagsync/src/main/java/org/apache/ranger/tagsync/source/atlas/AtlasNotificationMapper.java
index 916aad3..85c7c20 100644
--- a/tagsync/src/main/java/org/apache/ranger/tagsync/source/atlas/AtlasNotificationMapper.java
+++ b/tagsync/src/main/java/org/apache/ranger/tagsync/source/atlas/AtlasNotificationMapper.java
@@ -28,6 +28,7 @@ import org.apache.ranger.plugin.model.RangerServiceResource;
 import org.apache.ranger.plugin.model.RangerTag;
 import org.apache.ranger.plugin.model.RangerTagDef;
 import org.apache.ranger.plugin.model.RangerTagDef.RangerTagAttributeDef;
+import org.apache.ranger.plugin.model.RangerValiditySchedule;
 import org.apache.ranger.plugin.util.ServiceTags;
 import org.apache.ranger.tagsync.source.atlasrest.RangerAtlasEntity;
 import org.apache.ranger.tagsync.source.atlasrest.RangerAtlasEntityWithTags;
@@ -38,295 +39,315 @@ import java.util.List;
 import java.util.Map;
 
 public class AtlasNotificationMapper {
-	private static final Log LOG = LogFactory.getLog(AtlasNotificationMapper.class);
-
-
-	private static Map<String, Long> unhandledEventTypes = new HashMap<String, Long>();
-
-	private static void logUnhandledEntityNotification(EntityNotificationWrapper entityNotification) {
-
-		final int REPORTING_INTERVAL_FOR_UNHANDLED_ENTITYTYPE_IN_MILLIS = 5 * 60 * 1000; // 5 minutes
-
-		boolean loggingNeeded = false;
-		String entityTypeName = entityNotification.getEntityTypeName();
-
-		if (entityTypeName != null) {
-			Long timeInMillis = unhandledEventTypes.get(entityTypeName);
-			long currentTimeInMillis = System.currentTimeMillis();
-			if (timeInMillis == null ||
-					(currentTimeInMillis - timeInMillis) >= REPORTING_INTERVAL_FOR_UNHANDLED_ENTITYTYPE_IN_MILLIS) {
-				unhandledEventTypes.put(entityTypeName, currentTimeInMillis);
-				loggingNeeded = true;
-			}
-		} else {
-			LOG.error("EntityNotification contains NULL entity or NULL entity-type");
-		}
-
-		if (loggingNeeded) {
-			LOG.warn("Ignoring entity notification of type " + entityTypeName);
-		}
-
-		if (LOG.isDebugEnabled()) {
-			LOG.debug("Ignoring entity notification of type " + entityTypeName);
-		}
-	}
-
-	@SuppressWarnings("unchecked")
-	public static ServiceTags processEntityNotification(EntityNotificationWrapper entityNotification) {
-
-		ServiceTags ret = null;
-
-		if (isNotificationHandled(entityNotification)) {
-			try {
-				RangerAtlasEntityWithTags entityWithTags = new RangerAtlasEntityWithTags(entityNotification);
-
-				if (entityNotification.getIsEntityDeleteOp()) {
-					ret = buildServiceTagsForEntityDeleteNotification(entityWithTags);
-				} else {
-					ret = buildServiceTags(entityWithTags, null);
-				}
-
-			} catch (Exception exception) {
-				LOG.error("createServiceTags() failed!! ", exception);
-			}
-		} else {
-			logUnhandledEntityNotification(entityNotification);
-		}
-		return ret;
-	}
-
-	public static Map<String, ServiceTags> processAtlasEntities(List<RangerAtlasEntityWithTags> atlasEntities) {
-		Map<String, ServiceTags> ret = null;
-
-		try {
-			ret = buildServiceTags(atlasEntities);
-		} catch (Exception exception) {
-			LOG.error("Failed to build serviceTags", exception);
-		}
-
-		return ret;
-	}
-
-	static private boolean isNotificationHandled(EntityNotificationWrapper entityNotification) {
-		boolean ret = false;
-
-		EntityNotificationWrapper.NotificationType opType = entityNotification.getEntityNotificationType();
-
-		if (opType != null) {
-			switch (opType) {
-				case ENTITY_CREATE:
-					ret = ! entityNotification.getIsEmptyClassifications();
-					break;
-				case ENTITY_UPDATE:
-				case ENTITY_DELETE:
-				case CLASSIFICATION_ADD:
-				case CLASSIFICATION_UPDATE:
-				case CLASSIFICATION_DELETE: {
-					ret = true;
-					break;
-				}
-				default:
-					LOG.error(opType + ": unknown notification received - not handled");
-					break;
-			}
-			if (ret) {
-				ret = entityNotification.getIsEntityTypeHandled();
-			}
-		}
-
-		return ret;
-	}
-
-	static private ServiceTags buildServiceTagsForEntityDeleteNotification(RangerAtlasEntityWithTags entityWithTags) throws Exception {
-		final ServiceTags ret;
-
-		RangerAtlasEntity entity = entityWithTags.getEntity();
-
-		String guid = entity.getGuid();
-		if (StringUtils.isNotBlank(guid)) {
-			ret = new ServiceTags();
-			RangerServiceResource serviceResource = new RangerServiceResource();
-			serviceResource.setGuid(guid);
-			ret.getServiceResources().add(serviceResource);
-		} else {
-			ret = buildServiceTags(entityWithTags, null);
-			if (ret != null) {
-				// tag-definitions should NOT be deleted as part of service-resource delete
-				ret.setTagDefinitions(MapUtils.EMPTY_MAP);
-				// Ranger deletes tags associated with deleted service-resource
-				ret.setTags(MapUtils.EMPTY_MAP);
-			}
-		}
-
-		if (ret != null) {
-			ret.setOp(ServiceTags.OP_DELETE);
-		}
-
-		return ret;
-	}
-
-	static private Map<String, ServiceTags> buildServiceTags(List<RangerAtlasEntityWithTags> entitiesWithTags) throws Exception {
-
-		Map<String, ServiceTags> ret = new HashMap<String, ServiceTags>();
-
-		for (RangerAtlasEntityWithTags element : entitiesWithTags) {
-			RangerAtlasEntity entity = element.getEntity();
-			if (entity != null) {
-				buildServiceTags(element, ret);
-			} else {
-				if (LOG.isDebugEnabled()) {
-					LOG.debug("Ignoring entity because its State is not ACTIVE: " + element);
-				}
-			}
-		}
-
-		// Remove duplicate tag definitions
-		if(CollectionUtils.isNotEmpty(ret.values())) {
-			for (ServiceTags serviceTag : ret.values()) {
-				if(MapUtils.isNotEmpty(serviceTag.getTagDefinitions())) {
-					Map<String, RangerTagDef> uniqueTagDefs = new HashMap<String, RangerTagDef>();
-
-					for (RangerTagDef tagDef : serviceTag.getTagDefinitions().values()) {
-						RangerTagDef existingTagDef = uniqueTagDefs.get(tagDef.getName());
-
-						if (existingTagDef == null) {
-							uniqueTagDefs.put(tagDef.getName(), tagDef);
-						} else {
-							if(CollectionUtils.isNotEmpty(tagDef.getAttributeDefs())) {
-								for(RangerTagAttributeDef tagAttrDef : tagDef.getAttributeDefs()) {
-									boolean attrDefExists = false;
-
-									if(CollectionUtils.isNotEmpty(existingTagDef.getAttributeDefs())) {
-										for(RangerTagAttributeDef existingTagAttrDef : existingTagDef.getAttributeDefs()) {
-											if(StringUtils.equalsIgnoreCase(existingTagAttrDef.getName(), tagAttrDef.getName())) {
-												attrDefExists = true;
-												break;
-											}
-										}
-									}
-
-									if(! attrDefExists) {
-										existingTagDef.getAttributeDefs().add(tagAttrDef);
-									}
-								}
-							}
-						}
-					}
-
-					serviceTag.getTagDefinitions().clear();
-					for(RangerTagDef tagDef : uniqueTagDefs.values()) {
-						serviceTag.getTagDefinitions().put(tagDef.getId(), tagDef);
-					}
-				}
-			}
-		}
-
-		if (MapUtils.isNotEmpty(ret)) {
-			for (Map.Entry<String, ServiceTags> entry : ret.entrySet()) {
-				ServiceTags serviceTags = entry.getValue();
-				serviceTags.setOp(ServiceTags.OP_REPLACE);
-			}
-		}
-		return ret;
-	}
-
-	static private ServiceTags buildServiceTags(RangerAtlasEntityWithTags entityWithTags, Map<String, ServiceTags> serviceTagsMap) throws Exception {
-		ServiceTags            ret             = null;
-		RangerAtlasEntity entity          = entityWithTags.getEntity();
-		RangerServiceResource  serviceResource = AtlasResourceMapperUtil.getRangerServiceResource(entity);
-
-		if (serviceResource != null) {
-
-			List<RangerTag>     tags        = getTags(entityWithTags);
-			List<RangerTagDef>  tagDefs     = getTagDefs(entityWithTags);
-			String              serviceName = serviceResource.getServiceName();
-
-			ret = createOrGetServiceTags(serviceTagsMap, serviceName);
-
-			if (serviceTagsMap == null || CollectionUtils.isNotEmpty(tags)) {
-
-				serviceResource.setId((long) ret.getServiceResources().size());
-				ret.getServiceResources().add(serviceResource);
-
-				List<Long> tagIds = new ArrayList<Long>();
-
-				if (CollectionUtils.isNotEmpty(tags)) {
-					for (RangerTag tag : tags) {
-						tag.setId((long) ret.getTags().size());
-						ret.getTags().put(tag.getId(), tag);
-
-						tagIds.add(tag.getId());
-					}
-				}
-				ret.getResourceToTagIds().put(serviceResource.getId(), tagIds);
-
-				if (CollectionUtils.isNotEmpty(tagDefs)) {
-					for (RangerTagDef tagDef : tagDefs) {
-						tagDef.setId((long) ret.getTagDefinitions().size());
-						ret.getTagDefinitions().put(tagDef.getId(), tagDef);
-					}
-				}
-			} else {
-				if (LOG.isDebugEnabled()) {
-					LOG.debug("Entity " + entityWithTags + " does not have any tags associated with it when full-sync is being done.");
-					LOG.debug("Will not add this entity to serviceTags, so that this entity, if exists,  will be removed from ranger");
-				}
-			}
-		} else {
-			LOG.error("Failed to build serviceResource for entity:" + entity.getGuid());
-		}
-
-		return ret;
-	}
-
-	static private ServiceTags createOrGetServiceTags(Map<String, ServiceTags> serviceTagsMap, String serviceName) {
-		ServiceTags ret = serviceTagsMap == null ? null : serviceTagsMap.get(serviceName);
-
-		if (ret == null) {
-			ret = new ServiceTags();
-
-			if (serviceTagsMap != null) {
-				serviceTagsMap.put(serviceName, ret);
-			}
-
-			ret.setOp(ServiceTags.OP_ADD_OR_UPDATE);
-			ret.setServiceName(serviceName);
-		}
-
-		return ret;
-	}
-
-	static private List<RangerTag> getTags(RangerAtlasEntityWithTags entityWithTags) {
-		List<RangerTag> ret = new ArrayList<RangerTag>();
-
-		if (entityWithTags != null && MapUtils.isNotEmpty(entityWithTags.getTags())) {
-			Map<String, Map<String, String>> tags = entityWithTags.getTags();
-
-			for (Map.Entry<String, Map<String, String>> tag : tags.entrySet()) {
-				ret.add(new RangerTag(null, tag.getKey(), tag.getValue(), RangerTag.OWNER_SERVICERESOURCE));
-			}
-		}
-
-		return ret;
-	}
-
-	static private List<RangerTagDef> getTagDefs(RangerAtlasEntityWithTags entityWithTags) {
-		List<RangerTagDef> ret = new ArrayList<RangerTagDef>();
-
-		if (entityWithTags != null && MapUtils.isNotEmpty(entityWithTags.getTags())) {
-			Map<String, Map<String, String>> tags = entityWithTags.getTags();
-
-			for (Map.Entry<String, Map<String, String>> tag : tags.entrySet()) {
-				RangerTagDef tagDef = new RangerTagDef(tag.getKey(), "Atlas");
-				if (MapUtils.isNotEmpty(tag.getValue())) {
-					for (String attributeName : tag.getValue().keySet()) {
-						tagDef.getAttributeDefs().add(new RangerTagAttributeDef(attributeName, entityWithTags.getTagAttributeType(tag.getKey(), attributeName)));
-					}
-				}
-				ret.add(tagDef);
-			}
-		}
-
-		return ret;
-	}
+    private static final    int                 REPORTING_INTERVAL_FOR_UNHANDLED_ENTITYTYPE_IN_MILLIS = 5 * 60 * 1000; // 5 minutes
+
+    private static final    Log                 LOG                 = LogFactory.getLog(AtlasNotificationMapper.class);
+    private static          Map<String, Long>   unhandledEventTypes = new HashMap<>();
+
+    private static void logUnhandledEntityNotification(EntityNotificationWrapper entityNotification) {
+
+        boolean skipLogging = entityNotification.getIsEntityCreateOp() && entityNotification.getIsEmptyClassifications()
+                || ! entityNotification.getIsEntityActive();
+
+        if (!skipLogging) {
+            boolean loggingNeeded = false;
+
+            String entityTypeName = entityNotification.getEntityTypeName();
+
+            if (entityTypeName != null) {
+                Long timeInMillis = unhandledEventTypes.get(entityTypeName);
+                long currentTimeInMillis = System.currentTimeMillis();
+                if (timeInMillis == null ||
+                        (currentTimeInMillis - timeInMillis) >= REPORTING_INTERVAL_FOR_UNHANDLED_ENTITYTYPE_IN_MILLIS) {
+                    unhandledEventTypes.put(entityTypeName, currentTimeInMillis);
+                    loggingNeeded = true;
+                }
+            } else {
+                LOG.error("EntityNotification contains NULL entity or NULL entity-type");
+            }
+
+            if (loggingNeeded) {
+                LOG.warn("Did not process entity notification for [" + entityTypeName + "]");
+            }
+
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public static ServiceTags processEntityNotification(EntityNotificationWrapper entityNotification) {
+
+        ServiceTags ret = null;
+
+        if (isNotificationHandled(entityNotification)) {
+            try {
+                RangerAtlasEntityWithTags entityWithTags = new RangerAtlasEntityWithTags(entityNotification);
+
+                if (entityNotification.getIsEntityDeleteOp()) {
+                    ret = buildServiceTagsForEntityDeleteNotification(entityWithTags);
+                } else {
+                    ret = buildServiceTags(entityWithTags, null);
+                }
+
+            } catch (Exception exception) {
+                LOG.error("createServiceTags() failed!! ", exception);
+            }
+        } else {
+            logUnhandledEntityNotification(entityNotification);
+        }
+        return ret;
+    }
+
+    public static Map<String, ServiceTags> processAtlasEntities(List<RangerAtlasEntityWithTags> atlasEntities) {
+        Map<String, ServiceTags> ret = null;
+
+        try {
+            ret = buildServiceTags(atlasEntities);
+        } catch (Exception exception) {
+            LOG.error("Failed to build serviceTags", exception);
+        }
+
+        return ret;
+    }
+
+    static private boolean isNotificationHandled(EntityNotificationWrapper entityNotification) {
+        boolean ret = false;
+
+        EntityNotificationWrapper.NotificationOpType opType = entityNotification.getOpType();
+
+        if (opType != null) {
+            switch (opType) {
+                case ENTITY_CREATE:
+                    ret = ! entityNotification.getIsEmptyClassifications();
+                    break;
+                case ENTITY_UPDATE:
+                case ENTITY_DELETE:
+                case CLASSIFICATION_ADD:
+                case CLASSIFICATION_UPDATE:
+                case CLASSIFICATION_DELETE: {
+                    ret = true;
+                    break;
+                }
+                default:
+                    LOG.error(opType + ": unknown notification received - not handled");
+                    break;
+            }
+            if (ret) {
+                ret = entityNotification.getIsEntityTypeHandled();
+            }
+            if (!ret) {
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("Notification : [" + entityNotification + "] will NOT be processed.");
+                }
+            }
+        }
+
+        return ret;
+    }
+
+    @SuppressWarnings("unchecked")
+    static private ServiceTags buildServiceTagsForEntityDeleteNotification(RangerAtlasEntityWithTags entityWithTags) {
+        final ServiceTags ret;
+
+        RangerAtlasEntity   entity = entityWithTags.getEntity();
+        String              guid   = entity.getGuid();
+
+        if (StringUtils.isNotBlank(guid)) {
+            ret                                   = new ServiceTags();
+            RangerServiceResource serviceResource = new RangerServiceResource();
+            serviceResource.setGuid(guid);
+            ret.getServiceResources().add(serviceResource);
+        } else {
+            ret = buildServiceTags(entityWithTags, null);
+            if (ret != null) {
+                // tag-definitions should NOT be deleted as part of service-resource delete
+                ret.setTagDefinitions(MapUtils.EMPTY_MAP);
+                // Ranger deletes tags associated with deleted service-resource
+                ret.setTags(MapUtils.EMPTY_MAP);
+            }
+        }
+
+        if (ret != null) {
+            ret.setOp(ServiceTags.OP_DELETE);
+        }
+
+        return ret;
+    }
+
+    static private Map<String, ServiceTags> buildServiceTags(List<RangerAtlasEntityWithTags> entitiesWithTags) {
+
+        Map<String, ServiceTags> ret = new HashMap<>();
+
+        for (RangerAtlasEntityWithTags element : entitiesWithTags) {
+            RangerAtlasEntity entity = element.getEntity();
+            if (entity != null) {
+                buildServiceTags(element, ret);
+            } else {
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("Ignoring entity because its State is not ACTIVE: " + element);
+                }
+            }
+        }
+
+        // Remove duplicate tag definitions
+        if(CollectionUtils.isNotEmpty(ret.values())) {
+            for (ServiceTags serviceTag : ret.values()) {
+                if(MapUtils.isNotEmpty(serviceTag.getTagDefinitions())) {
+                    Map<String, RangerTagDef> uniqueTagDefs = new HashMap<>();
+
+                    for (RangerTagDef tagDef : serviceTag.getTagDefinitions().values()) {
+                        RangerTagDef existingTagDef = uniqueTagDefs.get(tagDef.getName());
+
+                        if (existingTagDef == null) {
+                            uniqueTagDefs.put(tagDef.getName(), tagDef);
+                        } else {
+                            if(CollectionUtils.isNotEmpty(tagDef.getAttributeDefs())) {
+                                for(RangerTagAttributeDef tagAttrDef : tagDef.getAttributeDefs()) {
+                                    boolean attrDefExists = false;
+
+                                    if(CollectionUtils.isNotEmpty(existingTagDef.getAttributeDefs())) {
+                                        for(RangerTagAttributeDef existingTagAttrDef : existingTagDef.getAttributeDefs()) {
+                                            if(StringUtils.equalsIgnoreCase(existingTagAttrDef.getName(), tagAttrDef.getName())) {
+                                                attrDefExists = true;
+                                                break;
+                                            }
+                                        }
+                                    }
+
+                                    if(! attrDefExists) {
+                                        existingTagDef.getAttributeDefs().add(tagAttrDef);
+                                    }
+                                }
+                            }
+                        }
+                    }
+
+                    serviceTag.getTagDefinitions().clear();
+                    for(RangerTagDef tagDef : uniqueTagDefs.values()) {
+                        serviceTag.getTagDefinitions().put(tagDef.getId(), tagDef);
+                    }
+                }
+            }
+        }
+
+        if (MapUtils.isNotEmpty(ret)) {
+            for (Map.Entry<String, ServiceTags> entry : ret.entrySet()) {
+                ServiceTags serviceTags = entry.getValue();
+                serviceTags.setOp(ServiceTags.OP_REPLACE);
+            }
+        }
+        return ret;
+    }
+
+    static private ServiceTags buildServiceTags(RangerAtlasEntityWithTags entityWithTags, Map<String, ServiceTags> serviceTagsMap) {
+        ServiceTags             ret             = null;
+        RangerAtlasEntity       entity          = entityWithTags.getEntity();
+        RangerServiceResource  serviceResource  = AtlasResourceMapperUtil.getRangerServiceResource(entity);
+
+        if (serviceResource != null) {
+
+            List<RangerTag>     tags        = getTags(entityWithTags);
+            List<RangerTagDef>  tagDefs     = getTagDefs(entityWithTags);
+            String              serviceName = serviceResource.getServiceName();
+
+            ret = createOrGetServiceTags(serviceTagsMap, serviceName);
+
+            if (serviceTagsMap == null || CollectionUtils.isNotEmpty(tags)) {
+
+                serviceResource.setId((long) ret.getServiceResources().size());
+                ret.getServiceResources().add(serviceResource);
+
+                List<Long> tagIds = new ArrayList<>();
+
+                if (CollectionUtils.isNotEmpty(tags)) {
+                    for (RangerTag tag : tags) {
+                        tag.setId((long) ret.getTags().size());
+                        ret.getTags().put(tag.getId(), tag);
+
+                        tagIds.add(tag.getId());
+                    }
+                }
+                ret.getResourceToTagIds().put(serviceResource.getId(), tagIds);
+
+                if (CollectionUtils.isNotEmpty(tagDefs)) {
+                    for (RangerTagDef tagDef : tagDefs) {
+                        tagDef.setId((long) ret.getTagDefinitions().size());
+                        ret.getTagDefinitions().put(tagDef.getId(), tagDef);
+                    }
+                }
+            } else {
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("Entity " + entityWithTags + " does not have any tags associated with it when full-sync is being done.");
+                    LOG.debug("Will not add this entity to serviceTags, so that this entity, if exists,  will be removed from ranger");
+                }
+            }
+        } else {
+            LOG.error("Failed to build serviceResource for entity:" + entity.getGuid());
+        }
+
+        return ret;
+    }
+
+    static private ServiceTags createOrGetServiceTags(Map<String, ServiceTags> serviceTagsMap, String serviceName) {
+        ServiceTags ret = serviceTagsMap == null ? null : serviceTagsMap.get(serviceName);
+
+        if (ret == null) {
+            ret = new ServiceTags();
+
+            if (serviceTagsMap != null) {
+                serviceTagsMap.put(serviceName, ret);
+            }
+
+            ret.setOp(ServiceTags.OP_ADD_OR_UPDATE);
+            ret.setServiceName(serviceName);
+        }
+
+        return ret;
+    }
+
+    static private List<RangerTag> getTags(RangerAtlasEntityWithTags entityWithTags) {
+        List<RangerTag> ret = new ArrayList<>();
+
+        if (entityWithTags != null && CollectionUtils.isNotEmpty(entityWithTags.getTags())) {
+            List<EntityNotificationWrapper.RangerAtlasClassification> tags = entityWithTags.getTags();
+
+            for (EntityNotificationWrapper.RangerAtlasClassification tag : tags) {
+                RangerTag rangerTag = new RangerTag(null, tag.getName(), tag.getAttributes(), RangerTag.OWNER_SERVICERESOURCE);
+
+                List<RangerValiditySchedule> validityPeriods = tag.getValidityPeriods();
+
+                if (CollectionUtils.isNotEmpty(validityPeriods)) {
+                    rangerTag.setValidityPeriods(validityPeriods);
+                }
+                ret.add(rangerTag);
+            }
+        }
+
+        return ret;
+    }
+
+    static private List<RangerTagDef> getTagDefs(RangerAtlasEntityWithTags entityWithTags) {
+        List<RangerTagDef> ret = new ArrayList<>();
+
+        if (entityWithTags != null && CollectionUtils.isNotEmpty(entityWithTags.getTags())) {
+
+            Map<String, String> tagNames = new HashMap<>();
+
+            for (EntityNotificationWrapper.RangerAtlasClassification tag : entityWithTags.getTags()) {
+
+                if (!tagNames.containsKey(tag.getName())) {
+                    tagNames.put(tag.getName(), tag.getName());
+
+                    RangerTagDef tagDef = new RangerTagDef(tag.getName(), "Atlas");
+                    if (MapUtils.isNotEmpty(tag.getAttributes())) {
+                        for (String attributeName : tag.getAttributes().keySet()) {
+                            tagDef.getAttributeDefs().add(new RangerTagAttributeDef(attributeName, entityWithTags.getTagAttributeType(tag.getName(), attributeName)));
+                        }
+                    }
+                    ret.add(tagDef);
+                }
+            }
+        }
+
+        return ret;
+    }
 }

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/tagsync/src/main/java/org/apache/ranger/tagsync/source/atlas/AtlasTagSource.java
----------------------------------------------------------------------
diff --git a/tagsync/src/main/java/org/apache/ranger/tagsync/source/atlas/AtlasTagSource.java b/tagsync/src/main/java/org/apache/ranger/tagsync/source/atlas/AtlasTagSource.java
index a13a789..ea4c20c 100644
--- a/tagsync/src/main/java/org/apache/ranger/tagsync/source/atlas/AtlasTagSource.java
+++ b/tagsync/src/main/java/org/apache/ranger/tagsync/source/atlas/AtlasTagSource.java
@@ -141,7 +141,7 @@ public class AtlasTagSource extends AbstractTagSource {
 	private static String getPrintableEntityNotification(EntityNotificationWrapper notification) {
 		StringBuilder sb = new StringBuilder();
 
-		sb.append("{ Notification-Type: ").append(notification.getEntityNotificationType()).append(", ");
+		sb.append("{ Notification-Type: ").append(notification.getOpType()).append(", ");
         RangerAtlasEntityWithTags entityWithTags = new RangerAtlasEntityWithTags(notification);
         sb.append(entityWithTags.toString());
 

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/tagsync/src/main/java/org/apache/ranger/tagsync/source/atlas/EntityNotificationWrapper.java
----------------------------------------------------------------------
diff --git a/tagsync/src/main/java/org/apache/ranger/tagsync/source/atlas/EntityNotificationWrapper.java b/tagsync/src/main/java/org/apache/ranger/tagsync/source/atlas/EntityNotificationWrapper.java
index e680b14..f08c331 100644
--- a/tagsync/src/main/java/org/apache/ranger/tagsync/source/atlas/EntityNotificationWrapper.java
+++ b/tagsync/src/main/java/org/apache/ranger/tagsync/source/atlas/EntityNotificationWrapper.java
@@ -19,6 +19,7 @@
 
 package org.apache.ranger.tagsync.source.atlas;
 
+import org.apache.atlas.model.TimeBoundary;
 import org.apache.atlas.model.instance.AtlasClassification;
 import org.apache.atlas.model.instance.AtlasEntity;
 import org.apache.atlas.model.notification.EntityNotification;
@@ -30,9 +31,11 @@ import org.apache.atlas.v1.model.notification.EntityNotificationV2;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.apache.ranger.plugin.model.RangerValiditySchedule;
 import org.apache.ranger.tagsync.source.atlasrest.RangerAtlasEntity;
 
 import javax.annotation.Nonnull;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -40,64 +43,199 @@ import java.util.Map;
 public class EntityNotificationWrapper {
 	private static final Log LOG = LogFactory.getLog(EntityNotificationWrapper.class);
 
-	public enum NotificationType { UNKNOWN, ENTITY_CREATE, ENTITY_UPDATE, ENTITY_DELETE, CLASSIFICATION_ADD, CLASSIFICATION_UPDATE, CLASSIFICATION_DELETE}
-
-	private final EntityNotification notification;
-	private final EntityNotification.EntityNotificationType notificationType;
-	private final RangerAtlasEntity rangerAtlasEntity;
-	private final String entityTypeName;
-	private final boolean isEntityTypeHandled;
-	private final boolean isEntityDeleteOp;
-	private final boolean isEmptyClassifications;
-
-	EntityNotificationWrapper(@Nonnull EntityNotification notification) {
-		this.notification = notification;
-		notificationType = this.notification.getType();
-
-		switch (notificationType) {
-			case ENTITY_NOTIFICATION_V2: {
-
-				EntityNotificationV2 v2Notification = (EntityNotificationV2) notification;
-				AtlasEntity atlasEntity = v2Notification.getEntity();
-				String guid = atlasEntity.getGuid();
-				String typeName = atlasEntity.getTypeName();
-
-				rangerAtlasEntity = new RangerAtlasEntity(typeName, guid, atlasEntity.getAttributes());
-				entityTypeName = atlasEntity.getTypeName();
-				isEntityTypeHandled = atlasEntity.getStatus() == AtlasEntity.Status.ACTIVE
-						&& AtlasResourceMapperUtil.isEntityTypeHandled(entityTypeName);
-				isEntityDeleteOp = EntityNotificationV2.OperationType.ENTITY_DELETE == v2Notification.getOperationType();
-				isEmptyClassifications = CollectionUtils.isNotEmpty(v2Notification.getClassifications());
-			}
-			break;
-			case ENTITY_NOTIFICATION_V1: {
-				EntityNotificationV1 v1Notification = (EntityNotificationV1) notification;
-
-				Referenceable atlasEntity = v1Notification.getEntity();
-				String guid = atlasEntity.getId()._getId();
-				String typeName = atlasEntity.getTypeName();
-
-				rangerAtlasEntity = new RangerAtlasEntity(typeName, guid, atlasEntity.getValues());
-				entityTypeName = atlasEntity.getTypeName();
-				isEntityTypeHandled = atlasEntity.getId().getState() == Id.EntityState.ACTIVE
-						&& AtlasResourceMapperUtil.isEntityTypeHandled(entityTypeName);
-				isEntityDeleteOp = EntityNotificationV1.OperationType.ENTITY_DELETE == v1Notification.getOperationType();
-				isEmptyClassifications = CollectionUtils.isNotEmpty(v1Notification.getAllTraits());
-			}
-			break;
-			default: {
-				LOG.error("Unknown notification type - [" + notificationType + "]");
-
-				rangerAtlasEntity = null;
-				entityTypeName = null;
-				isEntityTypeHandled = false;
-				isEntityDeleteOp = false;
-				isEmptyClassifications = true;
-			}
-
-			break;
+	public enum NotificationOpType { UNKNOWN, ENTITY_CREATE, ENTITY_UPDATE, ENTITY_DELETE, CLASSIFICATION_ADD, CLASSIFICATION_UPDATE, CLASSIFICATION_DELETE}
+
+	public static class RangerAtlasClassification {
+		private final String                       name;
+		private final Map<String, String>          attributes;
+		private final List<RangerValiditySchedule> validityPeriods;
+
+		public RangerAtlasClassification(String name, Map<String, String> attributes, List<RangerValiditySchedule> validityPeriods) {
+			this.name            = name;
+			this.attributes      = attributes;
+			this.validityPeriods = validityPeriods;
+		}
+		public String getName() {
+			return name;
+		}
+		public Map<String, String> getAttributes() {
+			return attributes;
+		}
+		public List<RangerValiditySchedule> getValidityPeriods() {
+			return validityPeriods;
 		}
+
 	}
+    private final RangerAtlasEntity                         rangerAtlasEntity;
+    private final String                                    entityTypeName;
+    private final boolean                                   isEntityActive;
+    private final boolean                                   isEntityTypeHandled;
+    private final boolean                                   isEntityDeleteOp;
+    private final boolean                                   isEntityCreateOp;
+    private final boolean                                   isEmptyClassifications;
+    private final List<RangerAtlasClassification>           classifications;
+    private final NotificationOpType                        opType;
+
+    EntityNotificationWrapper(@Nonnull EntityNotification notification) {
+        EntityNotification.EntityNotificationType notificationType = notification.getType();
+
+        switch (notificationType) {
+            case ENTITY_NOTIFICATION_V2: {
+                EntityNotificationV2 v2Notification = (EntityNotificationV2) notification;
+                AtlasEntity          atlasEntity    = v2Notification.getEntity();
+                String               guid           = atlasEntity.getGuid();
+                String               typeName       = atlasEntity.getTypeName();
+
+                rangerAtlasEntity      = new RangerAtlasEntity(typeName, guid, atlasEntity.getAttributes());
+                entityTypeName         = atlasEntity.getTypeName();
+                isEntityActive         = atlasEntity.getStatus() == AtlasEntity.Status.ACTIVE;
+                isEntityTypeHandled    = isEntityActive && AtlasResourceMapperUtil.isEntityTypeHandled(entityTypeName);
+                isEntityDeleteOp       = EntityNotificationV2.OperationType.ENTITY_DELETE == v2Notification.getOperationType();
+                isEntityCreateOp       = EntityNotificationV2.OperationType.ENTITY_CREATE == v2Notification.getOperationType();
+                isEmptyClassifications = CollectionUtils.isNotEmpty(v2Notification.getClassifications());
+
+                List<AtlasClassification> allClassifications = v2Notification.getClassifications();
+
+                if (CollectionUtils.isNotEmpty(allClassifications)) {
+                    classifications                = new ArrayList<>();
+
+                    for (AtlasClassification classification : allClassifications) {
+                        String classificationName = classification.getTypeName();
+
+                        Map<String, Object> valuesMap  = classification.getAttributes();
+                        Map<String, String> attributes = new HashMap<>();
+
+                        if (valuesMap != null) {
+                            for (Map.Entry<String, Object> value : valuesMap.entrySet()) {
+                                if (value.getValue() != null) {
+                                    attributes.put(value.getKey(), value.getValue().toString());
+                                }
+                            }
+                        }
+
+                        List<RangerValiditySchedule> validitySchedules = null;
+                        List<TimeBoundary> validityPeriods = classification.getValidityPeriods();
+
+                        if (CollectionUtils.isNotEmpty(validityPeriods)) {
+                            validitySchedules = convertTimeSpecFromAtlasToRanger(validityPeriods);
+                        }
+                        classifications.add(new RangerAtlasClassification(classificationName, attributes, validitySchedules));
+                    }
+                } else {
+                    classifications                = null;
+                }
+
+                EntityNotificationV2.OperationType operationType = v2Notification.getOperationType();
+                switch (operationType) {
+                    case ENTITY_CREATE:
+                        opType = NotificationOpType.ENTITY_CREATE;
+                        break;
+                    case ENTITY_UPDATE:
+                        opType = NotificationOpType.ENTITY_UPDATE;
+                        break;
+                    case ENTITY_DELETE:
+                        opType = NotificationOpType.ENTITY_DELETE;
+                        break;
+                    case CLASSIFICATION_ADD:
+                        opType = NotificationOpType.CLASSIFICATION_ADD;
+                        break;
+                    case CLASSIFICATION_UPDATE:
+                        opType = NotificationOpType.CLASSIFICATION_UPDATE;
+                        break;
+                    case CLASSIFICATION_DELETE:
+                        opType = NotificationOpType.CLASSIFICATION_DELETE;
+                        break;
+                    default:
+                        LOG.error("Received OperationType [" + operationType + "], converting to UNKNOWN");
+                        opType = NotificationOpType.UNKNOWN;
+                        break;
+                }
+            }
+            break;
+
+            case ENTITY_NOTIFICATION_V1: {
+                EntityNotificationV1 v1Notification = (EntityNotificationV1) notification;
+                Referenceable        atlasEntity    = v1Notification.getEntity();
+                String               guid           = atlasEntity.getId()._getId();
+                String               typeName       = atlasEntity.getTypeName();
+
+                rangerAtlasEntity      = new RangerAtlasEntity(typeName, guid, atlasEntity.getValues());
+                entityTypeName         = atlasEntity.getTypeName();
+                isEntityActive         = atlasEntity.getId().getState() == Id.EntityState.ACTIVE;
+                isEntityTypeHandled    = isEntityActive && AtlasResourceMapperUtil.isEntityTypeHandled(entityTypeName);
+                isEntityDeleteOp       = EntityNotificationV1.OperationType.ENTITY_DELETE == v1Notification.getOperationType();
+                isEntityCreateOp       = EntityNotificationV1.OperationType.ENTITY_CREATE == v1Notification.getOperationType();
+                isEmptyClassifications = CollectionUtils.isNotEmpty(v1Notification.getAllTraits());
+
+                List<Struct> allTraits = ((EntityNotificationV1) notification).getAllTraits();
+
+                if (CollectionUtils.isNotEmpty(allTraits)) {
+                    classifications = new ArrayList<>();
+
+                    for (Struct trait : allTraits) {
+                        String              traitName  = trait.getTypeName();
+                        Map<String, Object> valuesMap  = trait.getValuesMap();
+                        Map<String, String> attributes = new HashMap<>();
+
+                        if (valuesMap != null) {
+                            for (Map.Entry<String, Object> value : valuesMap.entrySet()) {
+                                if (value.getValue() != null) {
+                                    attributes.put(value.getKey(), value.getValue().toString());
+                                }
+                            }
+                        }
+
+                        classifications.add(new RangerAtlasClassification(traitName, attributes, null));
+                    }
+                } else {
+                    classifications = null;
+                }
+
+                EntityNotificationV1.OperationType operationType = v1Notification.getOperationType();
+                switch (operationType) {
+                    case ENTITY_CREATE:
+                        opType = NotificationOpType.ENTITY_CREATE;
+                        break;
+                    case ENTITY_UPDATE:
+                        opType = NotificationOpType.ENTITY_UPDATE;
+                        break;
+                    case ENTITY_DELETE:
+                        opType = NotificationOpType.ENTITY_DELETE;
+                        break;
+                    case TRAIT_ADD:
+                        opType = NotificationOpType.CLASSIFICATION_ADD;
+                        break;
+                    case TRAIT_UPDATE:
+                        opType = NotificationOpType.CLASSIFICATION_UPDATE;
+                        break;
+                    case TRAIT_DELETE:
+                        opType = NotificationOpType.CLASSIFICATION_DELETE;
+                        break;
+                    default:
+                        LOG.error("Received OperationType [" + operationType + "], converting to UNKNOWN");
+                        opType = NotificationOpType.UNKNOWN;
+                        break;
+                }
+            }
+            break;
+
+            default: {
+                LOG.error("Unknown notification type - [" + notificationType + "]");
+
+                rangerAtlasEntity              = null;
+                entityTypeName                 = null;
+                isEntityActive                 = false;
+                isEntityTypeHandled            = false;
+                isEntityDeleteOp               = false;
+                isEntityCreateOp               = false;
+                isEmptyClassifications         = true;
+                classifications                = null;
+                opType                         = NotificationOpType.UNKNOWN;
+            }
+
+            break;
+        }
+    }
 
 	public RangerAtlasEntity getRangerAtlasEntity() {
 		return rangerAtlasEntity;
@@ -115,128 +253,41 @@ public class EntityNotificationWrapper {
 		return isEntityDeleteOp;
 	}
 
+	public boolean getIsEntityCreateOp() {
+		return isEntityCreateOp;
+	}
+
 	public boolean getIsEmptyClassifications() {
 		return isEmptyClassifications;
 	}
 
-	public NotificationType getEntityNotificationType() {
-		NotificationType ret = NotificationType.UNKNOWN;
-
-		switch (notificationType) {
-			case ENTITY_NOTIFICATION_V2: {
-				EntityNotificationV2.OperationType opType = ((EntityNotificationV2) notification).getOperationType();
-				switch (opType) {
-					case ENTITY_CREATE:
-						ret = NotificationType.ENTITY_CREATE;
-						break;
-					case ENTITY_UPDATE:
-						ret = NotificationType.ENTITY_UPDATE;
-						break;
-					case ENTITY_DELETE:
-						ret = NotificationType.ENTITY_DELETE;
-						break;
-					case CLASSIFICATION_ADD:
-						ret = NotificationType.CLASSIFICATION_ADD;
-						break;
-					case CLASSIFICATION_UPDATE:
-						ret = NotificationType.CLASSIFICATION_UPDATE;
-						break;
-					case CLASSIFICATION_DELETE:
-						ret = NotificationType.CLASSIFICATION_DELETE;
-						break;
-					default:
-						LOG.error("Received OperationType [" + opType + "], converting to UNKNOWN");
-						break;
-				}
-				break;
-			}
-			case ENTITY_NOTIFICATION_V1: {
-				EntityNotificationV1.OperationType opType = ((EntityNotificationV1) notification).getOperationType();
-				switch (opType) {
-					case ENTITY_CREATE:
-						ret = NotificationType.ENTITY_CREATE;
-						break;
-					case ENTITY_UPDATE:
-						ret = NotificationType.ENTITY_UPDATE;
-						break;
-					case ENTITY_DELETE:
-						ret = NotificationType.ENTITY_DELETE;
-						break;
-					case TRAIT_ADD:
-						ret = NotificationType.CLASSIFICATION_ADD;
-						break;
-					case TRAIT_UPDATE:
-						ret = NotificationType.CLASSIFICATION_UPDATE;
-						break;
-					case TRAIT_DELETE:
-						ret = NotificationType.CLASSIFICATION_DELETE;
-						break;
-					default:
-						LOG.error("Received OperationType [" + opType + "], converting to UNKNOWN");
-						break;
-				}
-				break;
-			}
-			default: {
-				LOG.error("Unknown notification type - [" + notificationType + "]");
-			}
-			break;
-		}
-
-		return ret;
+	public List<RangerAtlasClassification> getClassifications() {
+		return classifications;
 	}
 
-	public Map<String, Map<String, String>> getAllClassifications() {
-		Map<String, Map<String, String>> ret = new HashMap<>();
-
-		switch (notificationType) {
-			case ENTITY_NOTIFICATION_V2: {
-				List<AtlasClassification> allClassifications = ((EntityNotificationV2) notification).getClassifications();
-				if (CollectionUtils.isNotEmpty(allClassifications)) {
-					for (AtlasClassification classification : allClassifications) {
-						String classificationName = classification.getTypeName();
-
-						Map<String, Object> valuesMap = classification.getAttributes();
-						Map<String, String> attributes = new HashMap<>();
-						if (valuesMap != null) {
-							for (Map.Entry<String, Object> value : valuesMap.entrySet()) {
-								if (value.getValue() != null) {
-									attributes.put(value.getKey(), value.getValue().toString());
-								}
-							}
-						}
-						ret.put(classificationName, attributes);
-					}
-				}
-			}
-			break;
-			case ENTITY_NOTIFICATION_V1: {
-				List<Struct> allTraits = ((EntityNotificationV1) notification).getAllTraits();
-				if (CollectionUtils.isNotEmpty(allTraits)) {
-					for (Struct trait : allTraits) {
-						String traitName = trait.getTypeName();
-
-						Map<String, Object> valuesMap = trait.getValuesMap();
-						Map<String, String> attributes = new HashMap<>();
-						if (valuesMap != null) {
-							for (Map.Entry<String, Object> value : valuesMap.entrySet()) {
-								if (value.getValue() != null) {
-									attributes.put(value.getKey(), value.getValue().toString());
-								}
-							}
-						}
-						ret.put(traitName, attributes);
-					}
-				}
-			}
-			break;
-			default: {
-				LOG.error("Unknown notification type - [" + notificationType + "]");
-			}
-			break;
-		}
+    public NotificationOpType getOpType() {
+        return opType;
+    }
 
-		return ret;
-	}
+    public boolean getIsEntityActive() { return isEntityActive; }
+
+    public static List<RangerValiditySchedule> convertTimeSpecFromAtlasToRanger(List<TimeBoundary> atlasTimeSpec) {
+        List<RangerValiditySchedule> rangerTimeSpec = null;
+
+        if (CollectionUtils.isNotEmpty(atlasTimeSpec)) {
+            rangerTimeSpec = new ArrayList<>();
+
+            for (TimeBoundary validityPeriod : atlasTimeSpec) {
+                RangerValiditySchedule validitySchedule = new RangerValiditySchedule();
+
+                validitySchedule.setStartTime(validityPeriod.getStartTime());
+                validitySchedule.setEndTime(validityPeriod.getEndTime());
+                validitySchedule.setTimeZone(validityPeriod.getTimeZone());
+
+                rangerTimeSpec.add(validitySchedule);
+            }
+        }
 
+        return rangerTimeSpec;
+    }
 }

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/tagsync/src/main/java/org/apache/ranger/tagsync/source/atlasrest/AtlasRESTTagSource.java
----------------------------------------------------------------------
diff --git a/tagsync/src/main/java/org/apache/ranger/tagsync/source/atlasrest/AtlasRESTTagSource.java b/tagsync/src/main/java/org/apache/ranger/tagsync/source/atlasrest/AtlasRESTTagSource.java
index b715869..2b4a668 100644
--- a/tagsync/src/main/java/org/apache/ranger/tagsync/source/atlasrest/AtlasRESTTagSource.java
+++ b/tagsync/src/main/java/org/apache/ranger/tagsync/source/atlasrest/AtlasRESTTagSource.java
@@ -25,6 +25,7 @@ import org.apache.atlas.AtlasClientV2;
 import org.apache.atlas.AtlasServiceException;
 import org.apache.atlas.exception.AtlasBaseException;
 import org.apache.atlas.model.SearchFilter;
+import org.apache.atlas.model.TimeBoundary;
 import org.apache.atlas.model.discovery.AtlasSearchResult;
 import org.apache.atlas.model.discovery.SearchParameters;
 import org.apache.atlas.model.instance.AtlasClassification;
@@ -44,6 +45,7 @@ import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
 import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.ranger.plugin.model.RangerValiditySchedule;
 import org.apache.ranger.plugin.util.ServiceTags;
 import org.apache.ranger.tagsync.model.AbstractTagSource;
 import org.apache.ranger.tagsync.model.TagSink;
@@ -51,6 +53,7 @@ import org.apache.ranger.tagsync.process.TagSyncConfig;
 import org.apache.ranger.tagsync.process.TagSynchronizer;
 import org.apache.ranger.tagsync.source.atlas.AtlasNotificationMapper;
 import org.apache.ranger.tagsync.source.atlas.AtlasResourceMapperUtil;
+import org.apache.ranger.tagsync.source.atlas.EntityNotificationWrapper;
 
 import java.io.IOException;
 import java.text.DateFormat;
@@ -229,143 +232,171 @@ public class AtlasRESTTagSource extends AbstractTagSource implements Runnable {
 
 	}
 
-	private List<RangerAtlasEntityWithTags> getAtlasActiveEntities() {
-		if (LOG.isDebugEnabled()) {
-			LOG.debug("==> getAtlasActiveEntities()");
-		}
-		List<RangerAtlasEntityWithTags> ret = null;
-
-		SearchParameters searchParams = new SearchParameters();
-		AtlasTypeRegistry typeRegistry = new AtlasTypeRegistry();
-		AtlasTypeRegistry.AtlasTransientTypeRegistry tty = null;
-		AtlasSearchResult searchResult = null;
-
-		searchParams.setClassification("*");
-		searchParams.setIncludeClassificationAttributes(true);
-		searchParams.setOffset(0);
-		searchParams.setLimit(Integer.MAX_VALUE);
-
-		boolean commitUpdates = false;
-		try {
-			AtlasClientV2 atlasClient = getAtlasClient();
-			searchResult = atlasClient.facetedSearch(searchParams);
-			AtlasTypesDef typesDef = atlasClient.getAllTypeDefs(new SearchFilter());
-			tty = typeRegistry.lockTypeRegistryForUpdate();
-			tty.addTypes(typesDef);
-			commitUpdates = true;
-		} catch (AtlasServiceException | AtlasBaseException | IOException excp) {
-			LOG.error("failed to download tags from Atlas", excp);
-		} catch (Exception unexpectedException) {
-			LOG.error("Failed to download tags from Atlas due to unexpected exception", unexpectedException);
-		} finally {
-			if (tty != null) {
-				typeRegistry.releaseTypeRegistryForUpdate(tty, commitUpdates);
-			}
-		}
+    private List<RangerAtlasEntityWithTags> getAtlasActiveEntities() {
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("==> getAtlasActiveEntities()");
+        }
+        List<RangerAtlasEntityWithTags> ret = null;
+
+        SearchParameters searchParams = new SearchParameters();
+        AtlasTypeRegistry typeRegistry = new AtlasTypeRegistry();
+        AtlasTypeRegistry.AtlasTransientTypeRegistry tty = null;
+        AtlasSearchResult searchResult = null;
+
+        searchParams.setClassification("*");
+        searchParams.setIncludeClassificationAttributes(true);
+        searchParams.setOffset(0);
+        searchParams.setLimit(Integer.MAX_VALUE);
+
+        boolean commitUpdates = false;
+        try {
+            AtlasClientV2 atlasClient = getAtlasClient();
+            searchResult = atlasClient.facetedSearch(searchParams);
+            AtlasTypesDef typesDef = atlasClient.getAllTypeDefs(new SearchFilter());
+            tty = typeRegistry.lockTypeRegistryForUpdate();
+            tty.addTypes(typesDef);
+            commitUpdates = true;
+        } catch (AtlasServiceException | AtlasBaseException | IOException excp) {
+            LOG.error("failed to download tags from Atlas", excp);
+        } catch (Exception unexpectedException) {
+            LOG.error("Failed to download tags from Atlas due to unexpected exception", unexpectedException);
+        } finally {
+            if (tty != null) {
+                typeRegistry.releaseTypeRegistryForUpdate(tty, commitUpdates);
+            }
+        }
 
-		if (commitUpdates && searchResult != null) {
-			if (LOG.isDebugEnabled()) {
-				LOG.debug(AtlasType.toJson(searchResult));
-			}
-			ret = new ArrayList<>();
-			List<AtlasEntityHeader> entityHeaders = searchResult.getEntities();
-			if (CollectionUtils.isNotEmpty(entityHeaders)) {
-				for (AtlasEntityHeader header : entityHeaders) {
-					if (!header.getStatus().equals(AtlasEntity.Status.ACTIVE)) {
-						if (LOG.isDebugEnabled()) {
-							LOG.debug("Skipping entity because it is not ACTIVE, header:[" + header + "]");
-						}
-						continue;
-					}
+        if (commitUpdates && searchResult != null) {
+            if (LOG.isDebugEnabled()) {
+                LOG.debug(AtlasType.toJson(searchResult));
+            }
+            ret = new ArrayList<>();
+            List<AtlasEntityHeader> entityHeaders = searchResult.getEntities();
+            if (CollectionUtils.isNotEmpty(entityHeaders)) {
+                for (AtlasEntityHeader header : entityHeaders) {
+                    if (!header.getStatus().equals(AtlasEntity.Status.ACTIVE)) {
+                        if (LOG.isDebugEnabled()) {
+                            LOG.debug("Skipping entity because it is not ACTIVE, header:[" + header + "]");
+                        }
+                        continue;
+                    }
+
+                    String typeName = header.getTypeName();
+                    if (!AtlasResourceMapperUtil.isEntityTypeHandled(typeName)) {
+                        if (LOG.isDebugEnabled()) {
+                            LOG.debug("Not fetching Atlas entities of type:[" + typeName + "]");
+                        }
+                        continue;
+                    }
+
+                    List<EntityNotificationWrapper.RangerAtlasClassification>          allTagsForEntity       = new ArrayList<>();
+
+                    for (AtlasClassification classification : header.getClassifications()) {
+                        List<EntityNotificationWrapper.RangerAtlasClassification> tags = resolveTag(typeRegistry, classification);
+                        if (tags != null) {
+                            allTagsForEntity.addAll(tags);
+                        }
+                    }
+
+                    if (CollectionUtils.isNotEmpty(allTagsForEntity)) {
+                        RangerAtlasEntity         entity         = new RangerAtlasEntity(typeName, header.getGuid(), header.getAttributes());
+                        RangerAtlasEntityWithTags entityWithTags = new RangerAtlasEntityWithTags(entity, allTagsForEntity, typeRegistry);
+
+                        ret.add(entityWithTags);
+                    }
+                }
+            }
+        }
 
-					String typeName = header.getTypeName();
-					if (!AtlasResourceMapperUtil.isEntityTypeHandled(typeName)) {
-						if (LOG.isDebugEnabled()) {
-							LOG.debug("Not fetching Atlas entities of type:[" + typeName + "]");
-						}
-						continue;
-					}
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("<== getAtlasActiveEntities()");
+        }
 
-					Map<String, Map<String, String>> allTagsForEntity = new HashMap<>();
+        return ret;
+    }
 
-					for (AtlasClassification classification : header.getClassifications()) {
-						Map<String, Map<String, String>> tags = resolveTag(typeRegistry, classification.getTypeName(), classification.getAttributes());
-						if (tags != null) {
-							allTagsForEntity.putAll(tags);
-						}
-					}
+    /*
+     * Returns a list of <EntityNotificationWrapper.RangerAtlasClassification>
+     */
+    private List<EntityNotificationWrapper.RangerAtlasClassification> resolveTag(AtlasTypeRegistry typeRegistry, AtlasClassification classification) {
+        List<EntityNotificationWrapper.RangerAtlasClassification>   ret         = new ArrayList<>();
+        String                                                      typeName    = classification.getTypeName();
+        Map<String, Object>                                         attributes  = classification.getAttributes();
 
-					if (MapUtils.isNotEmpty(allTagsForEntity)) {
+        try {
+            AtlasClassificationType classificationType = typeRegistry.getClassificationTypeByName(typeName);
 
-						RangerAtlasEntity entity = new RangerAtlasEntity(typeName, header.getGuid(), header.getAttributes());
-						RangerAtlasEntityWithTags entityWithTags = new RangerAtlasEntityWithTags(entity, allTagsForEntity, typeRegistry);
-						ret.add(entityWithTags);
-					}
-				}
-			}
-		}
+            if (classificationType != null) {
 
-		if (LOG.isDebugEnabled()) {
-			LOG.debug("<== getAtlasActiveEntities()");
-		}
+                Map<String, String> allAttributes = new HashMap<>();
 
-		return ret;
-	}
+                if (MapUtils.isNotEmpty(attributes) && MapUtils.isNotEmpty(classificationType.getAllAttributes())) {
+                    for (Map.Entry<String, Object> attribute : attributes.entrySet()) {
 
-    /*
-     * Returns a map of <tag-name, List<attributeName, [attributeValue, attributeType]>>
-     */
-	private Map<String, Map<String, String>> resolveTag(AtlasTypeRegistry typeRegistry, String typeName, Map<String, Object> attributes) {
-		Map<String, Map<String, String>> ret = new HashMap<>();
-
-		try {
-			AtlasClassificationType classificationType = typeRegistry.getClassificationTypeByName(typeName);
-			if (classificationType != null) {
-				Map<String, String> allAttributes = new HashMap<>();
-				if (MapUtils.isNotEmpty(attributes) && MapUtils.isNotEmpty(classificationType.getAllAttributes())) {
-					for (Map.Entry<String, Object> attribute : attributes.entrySet()) {
-						String name = attribute.getKey();
-						Object value = attribute.getValue();
-						if (value != null) {
-							String stringValue = value.toString();
-							AtlasStructType.AtlasAttribute atlasAttribute = classificationType.getAttribute(name);
-							if (atlasAttribute != null) {
-								if (value instanceof Number) {
-									if (atlasAttribute.getAttributeType() instanceof AtlasBuiltInTypes.AtlasDateType) {
-										stringValue = DATE_FORMATTER.get().format(value);
-									}
-								}
-								allAttributes.put(name, stringValue);
-							}
-						}
-					}
-				}
-				// Put most derived classificationType with all attributes
-				ret.put(typeName, allAttributes);
-
-				// Find base classification types
-				Set<String> superTypeNames = classificationType.getAllSuperTypes();
-				for (String superTypeName : superTypeNames) {
-					AtlasClassificationType superType = typeRegistry.getClassificationTypeByName(superTypeName);
-					if (superType != null) {
-						Map<String, String> attributeMap = new HashMap<>();
-						if (MapUtils.isNotEmpty(attributes) && MapUtils.isNotEmpty(superType.getAllAttributes())) {
-							for (String name : superType.getAllAttributes().keySet()) {
-								String stringValue = allAttributes.get(name);
-								if (stringValue != null) {
-									attributeMap.put(name, stringValue);
-								}
-							}
-						}
-						ret.put(superTypeName, attributeMap);
-					}
-				}
-			}
-		} catch (Exception exception) {
-			LOG.error("Error in resolving tags for type:[" + typeName + "]", exception);
-		}
-		return ret;
-	}
+                        String name     = attribute.getKey();
+                        Object value    = attribute.getValue();
+
+                        if (value != null) {
+
+                            String stringValue                              = value.toString();
+                            AtlasStructType.AtlasAttribute atlasAttribute   = classificationType.getAttribute(name);
+
+                            if (atlasAttribute != null) {
+                                if (value instanceof Number) {
+                                    if (atlasAttribute.getAttributeType() instanceof AtlasBuiltInTypes.AtlasDateType) {
+                                        stringValue = DATE_FORMATTER.get().format(value);
+                                    }
+                                }
+                                allAttributes.put(name, stringValue);
+                            }
+                        }
+                    }
+                }
+                List<TimeBoundary> validityPeriods              = classification.getValidityPeriods();
+                List<RangerValiditySchedule> validitySchedules  = null;
+
+                if (CollectionUtils.isNotEmpty(validityPeriods)) {
+                    validitySchedules = EntityNotificationWrapper.convertTimeSpecFromAtlasToRanger(validityPeriods);
+                }
+
+                // Add most derived classificationType with all attributes
+                ret.add(new EntityNotificationWrapper.RangerAtlasClassification(typeName, allAttributes, validitySchedules));
+
+                // Find base classification types
+                Set<String> superTypeNames = classificationType.getAllSuperTypes();
+                for (String superTypeName : superTypeNames) {
+
+                    AtlasClassificationType superType = typeRegistry.getClassificationTypeByName(superTypeName);
+
+                    if (superType != null) {
+
+                        Map<String, String> attributeMap = new HashMap<>();
+
+                        if (MapUtils.isNotEmpty(attributes) && MapUtils.isNotEmpty(superType.getAllAttributes())) {
+                            for (String name : superType.getAllAttributes().keySet()) {
+
+                                String stringValue = allAttributes.get(name);
+
+                                if (stringValue != null) {
+                                    attributeMap.put(name, stringValue);
+                                }
+                            }
+                        }
+                        validityPeriods     = classification.getValidityPeriods();
+                        validitySchedules   = null;
+
+                        if (CollectionUtils.isNotEmpty(validityPeriods)) {
+                            validitySchedules = EntityNotificationWrapper.convertTimeSpecFromAtlasToRanger(validityPeriods);
+                        }
+                        ret.add(new EntityNotificationWrapper.RangerAtlasClassification(superTypeName, attributeMap, validitySchedules));
+                    }
+                }
+            }
+        } catch (Exception exception) {
+            LOG.error("Error in resolving tags for type:[" + typeName + "]", exception);
+        }
+        return ret;
+    }
 
 	private AtlasClientV2 getAtlasClient() throws IOException {
 		final AtlasClientV2 ret;

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/tagsync/src/main/java/org/apache/ranger/tagsync/source/atlasrest/RangerAtlasEntityWithTags.java
----------------------------------------------------------------------
diff --git a/tagsync/src/main/java/org/apache/ranger/tagsync/source/atlasrest/RangerAtlasEntityWithTags.java b/tagsync/src/main/java/org/apache/ranger/tagsync/source/atlasrest/RangerAtlasEntityWithTags.java
index ecbc502..c31c73c 100644
--- a/tagsync/src/main/java/org/apache/ranger/tagsync/source/atlasrest/RangerAtlasEntityWithTags.java
+++ b/tagsync/src/main/java/org/apache/ranger/tagsync/source/atlasrest/RangerAtlasEntityWithTags.java
@@ -21,28 +21,28 @@ package org.apache.ranger.tagsync.source.atlasrest;
 import org.apache.atlas.type.AtlasClassificationType;
 import org.apache.atlas.type.AtlasStructType;
 import org.apache.atlas.type.AtlasTypeRegistry;
+import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang.StringUtils;
+import org.apache.ranger.plugin.model.RangerValiditySchedule;
 import org.apache.ranger.tagsync.source.atlas.EntityNotificationWrapper;
 
+import java.util.List;
 import java.util.Map;
 
 public class RangerAtlasEntityWithTags {
-    private final RangerAtlasEntity entity;
-    private final Map<String, Map<String, String>> tags;
-    private final AtlasTypeRegistry typeRegistry;
+    private final RangerAtlasEntity                                         entity;
+    private final List<EntityNotificationWrapper.RangerAtlasClassification> tags;
+    private final AtlasTypeRegistry                                         typeRegistry;
 
     public RangerAtlasEntityWithTags(EntityNotificationWrapper notification ) {
-
-        this.entity = notification.getRangerAtlasEntity();
-
-        this.tags = notification.getAllClassifications();
-
+        this.entity       = notification.getRangerAtlasEntity();
+        this.tags         = notification.getClassifications();
         this.typeRegistry = null;
     }
 
-    public RangerAtlasEntityWithTags(RangerAtlasEntity entity, Map<String, Map<String, String>> tags, AtlasTypeRegistry typeRegistry) {
-        this.entity = entity;
-        this.tags = tags;
+    public RangerAtlasEntityWithTags(RangerAtlasEntity entity, List<EntityNotificationWrapper.RangerAtlasClassification> tags, AtlasTypeRegistry typeRegistry) {
+        this.entity       = entity;
+        this.tags         = tags;
         this.typeRegistry = typeRegistry;
     }
 
@@ -50,26 +50,26 @@ public class RangerAtlasEntityWithTags {
         return entity;
     }
 
-    public Map<String, Map<String, String>> getTags() {
+    public List<EntityNotificationWrapper.RangerAtlasClassification> getTags() {
         return tags;
     }
 
-	public String getTagAttributeType(String tagTypeName, String tagAttributeName) {
-		String ret = StringUtils.EMPTY;
+    public String getTagAttributeType(String tagTypeName, String tagAttributeName) {
+        String ret = StringUtils.EMPTY;
 
-		if (typeRegistry != null) {
-			AtlasClassificationType classificationType = typeRegistry.getClassificationTypeByName(tagTypeName);
-			if (classificationType != null) {
-				AtlasStructType.AtlasAttribute atlasAttribute = classificationType.getAttribute(tagAttributeName);
+        if (typeRegistry != null) {
+            AtlasClassificationType classificationType = typeRegistry.getClassificationTypeByName(tagTypeName);
+            if (classificationType != null) {
+                AtlasStructType.AtlasAttribute atlasAttribute = classificationType.getAttribute(tagAttributeName);
 
-				if (atlasAttribute != null) {
-					ret = atlasAttribute.getTypeName();
-				}
-			}
-		}
+                if (atlasAttribute != null) {
+                    ret = atlasAttribute.getTypeName();
+                }
+            }
+        }
 
-		return ret;
-	}
+        return ret;
+    }
 
     @Override
     public String toString() {
@@ -78,18 +78,31 @@ public class RangerAtlasEntityWithTags {
         if (entity != null) {
             sb.append("{entity=").append(entity).append(", ");
         }
-        sb.append("tags={");
-        for (Map.Entry<String, Map<String, String>> tag : tags.entrySet()) {
-            sb.append("{tagName=").append(tag.getKey());
-            sb.append(", attributes={");
-            for (Map.Entry<String, String> attribute : tag.getValue().entrySet()) {
-                sb.append("{attributeName=").append(attribute.getKey());
-                sb.append(",attributeValue=").append(attribute.getValue());
+        sb.append(", classifications={");
+
+        if (CollectionUtils.isNotEmpty(tags)) {
+            for (EntityNotificationWrapper.RangerAtlasClassification tag : tags) {
+                sb.append("classificationName=").append(tag.getName());
+                sb.append(", attributes={");
+                for (Map.Entry<String, String> attribute : tag.getAttributes().entrySet()) {
+                    sb.append("{attributeName=").append(attribute.getKey());
+                    sb.append(",attributeValue=").append(attribute.getValue());
+                    sb.append("}");
+                }
+                sb.append("}");
+                sb.append(", validityPeriods={");
+                if (CollectionUtils.isNotEmpty(tag.getValidityPeriods())) {
+                    for (RangerValiditySchedule period : tag.getValidityPeriods()) {
+                        sb.append("{").append(period).append("}");
+                    }
+                }
                 sb.append("}");
             }
-            sb.append("}");
-            sb.append("}");
         }
+
+        sb.append("}");
+        sb.append("}");
+
         return sb.toString();
     }
 }


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

Posted by ab...@apache.org.
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 }
+    }
+
+    ]
+}


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

Posted by ab...@apache.org.
RANGER-2000: Policy effective dates to support time-bound and temporary authorization


Project: http://git-wip-us.apache.org/repos/asf/ranger/repo
Commit: http://git-wip-us.apache.org/repos/asf/ranger/commit/844315cd
Tree: http://git-wip-us.apache.org/repos/asf/ranger/tree/844315cd
Diff: http://git-wip-us.apache.org/repos/asf/ranger/diff/844315cd

Branch: refs/heads/master
Commit: 844315cdbc5e4589f5a4f873c33533d8f7bb014e
Parents: 7037920
Author: Abhay Kulkarni <ak...@hortonworks.com>
Authored: Sun Mar 4 22:08:21 2018 -0800
Committer: Abhay Kulkarni <ak...@hortonworks.com>
Committed: Sun Mar 4 22:08:21 2018 -0800

----------------------------------------------------------------------
 .../ranger/authorization/utils/JsonUtils.java   | 121 ++++
 .../contextenricher/RangerTagEnricher.java      |   8 +-
 .../contextenricher/RangerTagForEval.java       | 129 +++-
 .../plugin/errors/ValidationErrorCode.java      |   2 +
 .../ranger/plugin/model/RangerPolicy.java       | 105 +++-
 .../model/RangerPolicyResourceSignature.java    |  10 +-
 .../apache/ranger/plugin/model/RangerTag.java   |  77 ++-
 .../plugin/model/RangerValidityRecurrence.java  | 231 +++++++
 .../plugin/model/RangerValiditySchedule.java    |  93 +++
 .../model/validation/RangerPolicyValidator.java |  51 +-
 .../model/validation/RangerValidator.java       |   2 +-
 .../RangerValidityScheduleValidator.java        | 480 +++++++++++++++
 .../policyengine/PolicyEvaluatorForTag.java     |  61 +-
 .../plugin/policyengine/RangerAccessResult.java |   6 +
 .../policyengine/RangerPolicyEngineImpl.java    |  67 ++-
 .../policyengine/RangerPolicyRepository.java    |  35 +-
 .../RangerAbstractPolicyEvaluator.java          |   5 +
 ...angerDefaultDataMaskPolicyItemEvaluator.java |   5 +-
 .../RangerDefaultPolicyEvaluator.java           |  49 +-
 .../RangerDefaultPolicyItemEvaluator.java       |   4 +-
 ...ngerDefaultRowFilterPolicyItemEvaluator.java |   4 +-
 .../policyevaluator/RangerPolicyEvaluator.java  |  35 ++
 .../RangerPolicyItemEvaluator.java              |   3 +-
 .../RangerValidityScheduleEvaluator.java        | 588 ++++++++++++++++++
 .../ScheduledTimeAlwaysMatcher.java             |  27 +
 .../ScheduledTimeExactMatcher.java              |  32 +
 .../resourcematcher/ScheduledTimeMatcher.java   |  24 +
 .../ScheduledTimeRangeMatcher.java              |  34 ++
 .../plugin/store/AbstractPredicateUtil.java     | 116 ++--
 .../apache/ranger/plugin/util/SearchFilter.java |   1 +
 .../plugin/policyengine/TestPolicyEngine.java   |  99 ++-
 agents-common/src/test/resources/log4j.xml      |   4 +-
 .../test_policyengine_temporary.json            | 347 +++++++++++
 .../test-validity-schedules-invalid.json        | 146 +++++
 ...validity-schedules-valid-and-applicable.json | 507 ++++++++++++++++
 .../test-validity-schedules-valid.json          | 152 +++++
 .../optimized/current/ranger_core_db_mysql.sql  |   3 +
 ...policy-and-tag-for-time-based-processing.sql |  44 ++
 .../optimized/current/ranger_core_db_oracle.sql |   3 +
 ...policy-and-tag-for-time-based-processing.sql |  34 ++
 .../current/ranger_core_db_postgres.sql         |   3 +
 ...policy-and-tag-for-time-based-processing.sql |  53 ++
 .../current/ranger_core_db_sqlanywhere.sql      |   3 +
 ...policy-and-tag-for-time-based-processing.sql |  29 +
 .../current/ranger_core_db_sqlserver.sql        |   3 +
 ...policy-and-tag-for-time-based-processing.sql |  35 ++
 .../ranger/biz/RangerPolicyRetriever.java       |  96 +--
 .../apache/ranger/biz/RangerTagDBRetriever.java |  17 +-
 .../java/org/apache/ranger/biz/TagDBStore.java  |  41 +-
 .../apache/ranger/common/RangerSearchUtil.java  |   4 +-
 .../org/apache/ranger/entity/XXPolicyBase.java  |  67 ++-
 .../java/org/apache/ranger/entity/XXTag.java    |  20 +-
 .../ranger/service/RangerPolicyServiceBase.java |  47 +-
 .../ranger/service/RangerTagServiceBase.java    |  32 +
 src/main/assembly/tagsync.xml                   |   1 +
 .../source/atlas/AtlasNotificationMapper.java   | 603 ++++++++++---------
 .../tagsync/source/atlas/AtlasTagSource.java    |   2 +-
 .../source/atlas/EntityNotificationWrapper.java | 395 ++++++------
 .../source/atlasrest/AtlasRESTTagSource.java    | 287 +++++----
 .../atlasrest/RangerAtlasEntityWithTags.java    |  81 +--
 60 files changed, 4738 insertions(+), 825 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/agents-common/src/main/java/org/apache/ranger/authorization/utils/JsonUtils.java
----------------------------------------------------------------------
diff --git a/agents-common/src/main/java/org/apache/ranger/authorization/utils/JsonUtils.java b/agents-common/src/main/java/org/apache/ranger/authorization/utils/JsonUtils.java
new file mode 100644
index 0000000..bc4a8b5
--- /dev/null
+++ b/agents-common/src/main/java/org/apache/ranger/authorization/utils/JsonUtils.java
@@ -0,0 +1,121 @@
+/*
+ * 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.authorization.utils;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.reflect.TypeToken;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.collections.MapUtils;
+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 java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class JsonUtils {
+    private static final Log LOG = LogFactory.getLog(JsonUtils.class);
+
+    private static final HashMap<String, String> MAP_STRING_STRING = new HashMap<>();
+
+    private static final Gson gson;
+
+    static {
+        gson = new GsonBuilder().setDateFormat("yyyyMMdd-HH:mm:ss.SSS-Z")
+                .setPrettyPrinting()
+                .create();
+    }
+
+    public static String mapToJson(Map<?, ?> map) {
+        String ret = null;
+        if (MapUtils.isNotEmpty(map)) {
+            try {
+                ret = gson.toJson(map);
+            } catch (Exception e) {
+                LOG.error("Invalid input data: ", e);
+            }
+        }
+        return ret;
+    }
+
+    public static String listToJson(List<?> list) {
+        String ret = null;
+        if (CollectionUtils.isNotEmpty(list)) {
+            try {
+                ret = gson.toJson(list);
+            } catch (Exception e) {
+                LOG.error("Invalid input data: ", e);
+            }
+        }
+        return ret;
+    }
+
+    public static <T> T jsonToObject(String jsonStr, Class<T> clz) {
+        T ret = null;
+
+        if(StringUtils.isNotEmpty(jsonStr)) {
+            try {
+                ret = gson.fromJson(jsonStr, clz);
+            } catch(Exception excp) {
+                LOG.warn("jsonToObject() failed to convert json to object: " + jsonStr, excp);
+            }
+        }
+
+        return ret;
+    }
+
+    public static Map<String, String> jsonToMapStringString(String jsonStr) {
+        Map<String, String> ret = null;
+
+        if(StringUtils.isNotEmpty(jsonStr)) {
+            try {
+                ret = gson.fromJson(jsonStr, MAP_STRING_STRING.getClass());
+            } catch(Exception excp) {
+                LOG.warn("jsonToObject() failed to convert json to object: " + jsonStr, excp);
+            }
+        }
+
+        return ret;
+    }
+
+    public static List<RangerValiditySchedule> jsonToRangerValiditySchedule(String jsonStr) {
+        try {
+            Type listType = new TypeToken<List<RangerValiditySchedule>>() {
+            }.getType();
+            return gson.fromJson(jsonStr, listType);
+        } catch (Exception e) {
+            LOG.error("Cannot get List<RangerValiditySchedule> from " + jsonStr, e);
+            return null;
+        }
+    }
+    public static List<RangerValidityRecurrence> jsonToRangerValidityRecurringSchedule(String jsonStr) {
+        try {
+            Type listType = new TypeToken<List<RangerValidityRecurrence>>() {
+            }.getType();
+            return gson.fromJson(jsonStr, listType);
+        } catch (Exception e) {
+            LOG.error("Cannot get List<RangerValidityRecurrence> from " + jsonStr, e);
+            return null;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerTagEnricher.java
----------------------------------------------------------------------
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerTagEnricher.java b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerTagEnricher.java
index 415d4a4..d5d14a2 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerTagEnricher.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerTagEnricher.java
@@ -335,10 +335,12 @@ public class RangerTagEnricher extends RangerAbstractContextEnricher {
 			}
 		}
 
-		if (LOG.isDebugEnabled()) {
-			if (CollectionUtils.isEmpty(ret)) {
+		if (CollectionUtils.isEmpty(ret)) {
+			if (LOG.isDebugEnabled()) {
 				LOG.debug("RangerTagEnricher.findMatchingTags(" + resource + ") - No tags Found ");
-			} else {
+			}
+		} else {
+			if (LOG.isDebugEnabled()) {
 				LOG.debug("RangerTagEnricher.findMatchingTags(" + resource + ") - " + ret.size() + " tags Found ");
 			}
 		}

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerTagForEval.java
----------------------------------------------------------------------
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerTagForEval.java b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerTagForEval.java
index e31efa3..524605d 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerTagForEval.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerTagForEval.java
@@ -19,9 +19,17 @@
 
 package org.apache.ranger.plugin.contextenricher;
 
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.collections.MapUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.ranger.authorization.utils.JsonUtils;
 import org.apache.ranger.plugin.model.RangerTag;
+import org.apache.ranger.plugin.model.RangerValiditySchedule;
+import org.apache.ranger.plugin.policyevaluator.RangerValidityScheduleEvaluator;
 import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher;
 import org.codehaus.jackson.annotate.JsonAutoDetect;
+import org.codehaus.jackson.annotate.JsonIgnore;
 import org.codehaus.jackson.annotate.JsonIgnoreProperties;
 import org.codehaus.jackson.map.annotate.JsonSerialize;
 
@@ -29,6 +37,10 @@ import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlRootElement;
 import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
 import java.util.Map;
 
 @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY)
@@ -41,26 +53,80 @@ import java.util.Map;
 // from JSON specification
 
 public class RangerTagForEval implements Serializable {
-    private String type;
-    private Map<String, String> attributes;
+    private static final Log LOG = LogFactory.getLog(RangerTagForEval.class);
+
+    private String                                type;
+    private Map<String, String>                   attributes;
+    private Map<String, Object>                   options;
     private RangerPolicyResourceMatcher.MatchType matchType = RangerPolicyResourceMatcher.MatchType.SELF;
+    @JsonIgnore
+    private List<RangerValiditySchedule>          validityPeriods;
+    @JsonIgnore
+    private List<RangerValidityScheduleEvaluator> validityPeriodEvaluators;
+
 
     private RangerTagForEval() {}
 
     public RangerTagForEval(RangerTag tag, RangerPolicyResourceMatcher.MatchType matchType) {
-        this.type = tag.getType();
-        this.attributes = tag.getAttributes();
-        this.matchType = matchType;
-    }
+        this.type            = tag.getType();
+        this.attributes      = tag.getAttributes();
+        this.options         = tag.getOptions();
+        this.matchType       = matchType;
+        this.validityPeriods = tag.getValidityPeriods();
 
-    public RangerPolicyResourceMatcher.MatchType getMatchType() {
-        return matchType;
+        this.validityPeriodEvaluators = createValidityPeriodEvaluators();
     }
 
     public String getType() { return type;}
 
-    public Map<String, String> getAttributes() {
-        return attributes;
+    public Map<String, String> getAttributes() { return attributes; }
+
+    public Map<String, Object> getOptions() { return options; }
+
+    public RangerPolicyResourceMatcher.MatchType getMatchType() { return matchType; }
+
+    public List<RangerValiditySchedule> getValidityPeriods() { return validityPeriods; }
+
+    public boolean isApplicable(Date accessTime) {
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("==> RangerTagForEval.isApplicable(type=" + type + ", " + accessTime + ")");
+        }
+
+        boolean ret = false;
+
+        List<RangerValidityScheduleEvaluator> validityPeriodEvaluators = this.validityPeriodEvaluators;
+
+        // Specifically for unit-testing using TestPolicyEngine
+        if (MapUtils.isNotEmpty(options) && CollectionUtils.isEmpty(validityPeriodEvaluators)) {
+            Object value = getOption(RangerTag.OPTION_TAG_VALIDITY_PERIODS);
+
+            if (value != null && value instanceof String) {
+                this.validityPeriods = JsonUtils.jsonToRangerValiditySchedule((String) value);
+
+                validityPeriodEvaluators = createValidityPeriodEvaluators();
+            } else {
+                validityPeriodEvaluators = Collections.emptyList();
+            }
+
+            this.validityPeriodEvaluators = validityPeriodEvaluators;
+        }
+
+        if (accessTime != null && CollectionUtils.isNotEmpty(validityPeriodEvaluators)) {
+            for (RangerValidityScheduleEvaluator evaluator : validityPeriodEvaluators) {
+                if (evaluator.isApplicable(accessTime.getTime())) {
+                    ret = true;
+                    break;
+                }
+            }
+        } else {
+            ret = true;
+        }
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("<== RangerTagForEval.isApplicable(type=" + type + ", " + accessTime + ") : " + ret);
+        }
+
+        return ret;
     }
 
     @Override
@@ -83,6 +149,14 @@ public class RangerTagForEval implements Serializable {
         }
         sb.append(" }");
         sb.append(", matchType=").append(matchType);
+
+        if (options != null) {
+            sb.append(", options={").append(options).append("}");
+        }
+
+        if (validityPeriods != null) {
+            sb.append(", validityPeriods=").append(validityPeriods);
+        }
         sb.append(" }");
         return sb;
     }
@@ -97,6 +171,11 @@ public class RangerTagForEval implements Serializable {
                 + ((attributes == null) ? 0 : attributes.hashCode());
         result = prime * result
                 + ((matchType == null) ? 0 : matchType.hashCode());
+        result = prime * result
+                + ((validityPeriods == null) ? 0 : validityPeriods.hashCode());
+        result = prime * result
+                + ((options == null) ? 0 : options.hashCode());
+
         return result;
     }
 
@@ -124,7 +203,37 @@ public class RangerTagForEval implements Serializable {
                 return false;
         } else if (!matchType.equals(other.matchType))
             return false;
+        if (options == null) {
+            if (other.options != null)
+                return false;
+        } else if (!options.equals(other.options))
+            return false;
+        if (validityPeriods == null) {
+            if (other.validityPeriods != null)
+                return false;
+        } else if (!validityPeriods.equals(other.validityPeriods))
+            return false;
 
         return true;
     }
+
+    private Object getOption(String name) {
+        return options != null ? options.get(name) : null;
+    }
+
+    private List<RangerValidityScheduleEvaluator> createValidityPeriodEvaluators() {
+        final List<RangerValidityScheduleEvaluator> ret;
+
+        if (CollectionUtils.isNotEmpty(validityPeriods)) {
+            ret = new ArrayList<>();
+
+            for (RangerValiditySchedule schedule : validityPeriods) {
+                ret.add(new RangerValidityScheduleEvaluator(schedule));
+            }
+        } else {
+            ret = Collections.emptyList();
+        }
+
+        return ret;
+    }
 }

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/agents-common/src/main/java/org/apache/ranger/plugin/errors/ValidationErrorCode.java
----------------------------------------------------------------------
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/errors/ValidationErrorCode.java b/agents-common/src/main/java/org/apache/ranger/plugin/errors/ValidationErrorCode.java
index a7f7c39..ab120b7 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/errors/ValidationErrorCode.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/errors/ValidationErrorCode.java
@@ -91,6 +91,8 @@ public enum ValidationErrorCode {
     POLICY_VALIDATION_ERR_MISSING_RESOURCE_LIST(3026, "Resource list was empty or contains null. At least one resource must be specified"),
     POLICY_VALIDATION_ERR_POLICY_UPDATE_MOVE_SERVICE_NOT_ALLOWED(3027, "attempt to move policy id={0} from service={1} to service={2} is not allowed"),
     POLICY_VALIDATION_ERR_POLICY_TYPE_CHANGE_NOT_ALLOWED(3028, "attempt to change type of policy id={0} from type={1} to type={2} is not allowed"),
+    POLICY_VALIDATION_ERR_POLICY_INVALID_VALIDITY_SCHEDULE(3029, "Invalid validity schedule specification"),
+    POLICY_VALIDATION_ERR_POLICY_INVALID_PRIORITY(3030, "Invalid priority value"),
    ;
 
 

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPolicy.java
----------------------------------------------------------------------
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPolicy.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPolicy.java
index 534fe49..c2185a7 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPolicy.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPolicy.java
@@ -28,6 +28,7 @@ import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlRootElement;
 
+import org.apache.commons.collections.CollectionUtils;
 import org.codehaus.jackson.annotate.JsonAutoDetect;
 import org.codehaus.jackson.annotate.JsonIgnoreProperties;
 import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility;
@@ -54,12 +55,19 @@ public class RangerPolicy extends RangerBaseModelObject implements java.io.Seria
 	public static final String MASK_TYPE_NONE   = "MASK_NONE";
 	public static final String MASK_TYPE_CUSTOM = "CUSTOM";
 
+	public static final int POLICY_PRIORITY_NORMAL   = 0;
+	public static final int POLICY_PRIORITY_OVERRIDE = 1;
+
+	public static final String POLICY_PRIORITY_NAME_NORMAL   = "NORMAL";
+	public static final String POLICY_PRIORITY_NAME_OVERRIDE = "OVERRIDE";
+
 	// For future use
 	private static final long serialVersionUID = 1L;
 
 	private String                            service;
 	private String                            name;
 	private Integer                           policyType;
+	private Integer                           policyPriority;
 	private String                            description;
 	private String							  resourceSignature;
 	private Boolean                           isAuditEnabled;
@@ -70,15 +78,13 @@ public class RangerPolicy extends RangerBaseModelObject implements java.io.Seria
 	private List<RangerPolicyItem>            denyExceptions;
 	private List<RangerDataMaskPolicyItem>    dataMaskPolicyItems;
 	private List<RangerRowFilterPolicyItem>   rowFilterPolicyItems;
-  private String                            serviceType;
-  private List<String> policyLabels;
+	private String                            serviceType;
+	private Map<String, Object>               options;
+	private List<RangerValiditySchedule>      validitySchedules;
+	private List<String>                      policyLabels;
 
-
-	/**
-	 * @param
-	 */
 	public RangerPolicy() {
-                this(null, null, null, null, null, null, null, null);
+		this(null, null, null, null, null, null, null, null, null, null, null);
 	}
 
 	/**
@@ -90,12 +96,13 @@ public class RangerPolicy extends RangerBaseModelObject implements java.io.Seria
 	 * @param policyItems
 	 * @param resourceSignature TODO
 	 */
-        public RangerPolicy(String service, String name, Integer policyType, String description, Map<String, RangerPolicyResource> resources, List<RangerPolicyItem> policyItems, String resourceSignature, List<String> policyLables) {
+	public RangerPolicy(String service, String name, Integer policyType, Integer policyPriority, String description, Map<String, RangerPolicyResource> resources, List<RangerPolicyItem> policyItems, String resourceSignature, Map<String, Object> options, List<RangerValiditySchedule> validitySchedules, List<String> policyLables) {
 		super();
 
 		setService(service);
 		setName(name);
 		setPolicyType(policyType);
+		setPolicyPriority(policyPriority);
 		setDescription(description);
 		setResourceSignature(resourceSignature);
 		setIsAuditEnabled(null);
@@ -106,7 +113,10 @@ public class RangerPolicy extends RangerBaseModelObject implements java.io.Seria
 		setDenyExceptions(null);
 		setDataMaskPolicyItems(null);
 		setRowFilterPolicyItems(null);
-                setPolicyLabels(policyLables);
+		setOptions(options);
+		setValiditySchedules(validitySchedules);
+		setPolicyLabels(policyLables);
+
 	}
 
 	/**
@@ -118,6 +128,7 @@ public class RangerPolicy extends RangerBaseModelObject implements java.io.Seria
 		setService(other.getService());
 		setName(other.getName());
 		setPolicyType(other.getPolicyType());
+		setPolicyPriority(other.getPolicyPriority());
 		setDescription(other.getDescription());
 		setResourceSignature(other.getResourceSignature());
 		setIsAuditEnabled(other.getIsAuditEnabled());
@@ -128,7 +139,9 @@ public class RangerPolicy extends RangerBaseModelObject implements java.io.Seria
 		setDenyExceptions(other.getDenyExceptions());
 		setDataMaskPolicyItems(other.getDataMaskPolicyItems());
 		setRowFilterPolicyItems(other.getRowFilterPolicyItems());
-                setPolicyLabels(other.getPolicyLabels());
+		setOptions(other.getOptions());
+		setValiditySchedules(other.getValiditySchedules());
+		setPolicyLabels(other.getPolicyLabels());
 	}
 
 	/**
@@ -174,6 +187,20 @@ public class RangerPolicy extends RangerBaseModelObject implements java.io.Seria
 	}
 
 	/**
+	 * @return the policyPriority
+	 */
+	public Integer getPolicyPriority() {
+		return policyPriority;
+	}
+
+	/**
+	 * @param policyPriority the policyPriority to set
+	 */
+	public void setPolicyPriority(Integer policyPriority) {
+		this.policyPriority = policyPriority == null ? RangerPolicy.POLICY_PRIORITY_NORMAL : policyPriority;
+	}
+
+	/**
 	 * @return the description
 	 */
 	public String getDescription() {
@@ -415,6 +442,40 @@ public class RangerPolicy extends RangerBaseModelObject implements java.io.Seria
 		}
 	}
 
+    public Map<String, Object> getOptions() { return options; }
+
+    public void setOptions(Map<String, Object> options) {
+	    if (this.options == null) {
+	        this.options = new HashMap<>();
+        }
+        if (this.options == options) {
+	        return;
+        }
+        this.options.clear();
+
+        if(options != null) {
+            for(Map.Entry<String, Object> e : options.entrySet()) {
+                this.options.put(e.getKey(), e.getValue());
+            }
+        }
+    }
+
+    public List<RangerValiditySchedule> getValiditySchedules() { return validitySchedules; }
+
+    public void setValiditySchedules(List<RangerValiditySchedule> validitySchedules) {
+        if (this.validitySchedules == null) {
+            this.validitySchedules = new ArrayList<>();
+        }
+        if (this.validitySchedules == validitySchedules) {
+            return;
+        }
+        this.validitySchedules.clear();
+
+        if(validitySchedules != null) {
+            this.validitySchedules.addAll(validitySchedules);
+        }
+    }
+
 	@Override
 	public String toString( ) {
 		StringBuilder sb = new StringBuilder();
@@ -432,10 +493,11 @@ public class RangerPolicy extends RangerBaseModelObject implements java.io.Seria
 		sb.append("service={").append(service).append("} ");
 		sb.append("name={").append(name).append("} ");
 		sb.append("policyType={").append(policyType).append("} ");
+		sb.append("policyPriority={").append(policyPriority).append("} ");
 		sb.append("description={").append(description).append("} ");
 		sb.append("resourceSignature={").append(resourceSignature).append("} ");
 		sb.append("isAuditEnabled={").append(isAuditEnabled).append("} ");
-                sb.append("serviceType={").append(serviceType).append("} ");
+		sb.append("serviceType={").append(serviceType).append("} ");
 
 		sb.append("resources={");
 		if(resources != null) {
@@ -515,6 +577,27 @@ public class RangerPolicy extends RangerBaseModelObject implements java.io.Seria
 		}
 		sb.append("} ");
 
+        sb.append("options={");
+        if(options != null) {
+            for(Map.Entry<String, Object> e : options.entrySet()) {
+                sb.append(e.getKey()).append("={");
+                sb.append(e.getValue().toString());
+                sb.append("} ");
+            }
+        }
+        sb.append("} ");
+
+        //sb.append("validitySchedules={").append(validitySchedules).append("} ");
+        sb.append("validitySchedules={");
+        if (CollectionUtils.isNotEmpty(validitySchedules)) {
+            for (RangerValiditySchedule schedule : validitySchedules) {
+                if (schedule != null) {
+                    sb.append("schedule={").append(schedule).append("}");
+                }
+            }
+        }
+        sb.append("}");
+
 		sb.append("}");
 
 		return sb;

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPolicyResourceSignature.java
----------------------------------------------------------------------
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPolicyResourceSignature.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPolicyResourceSignature.java
index 00e60e2..b56b8dd 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPolicyResourceSignature.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPolicyResourceSignature.java
@@ -27,6 +27,7 @@ import java.util.Objects;
 import java.util.TreeMap;
 
 import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource;
@@ -45,7 +46,7 @@ public class RangerPolicyResourceSignature {
 		_policy = policy;
 		PolicySerializer serializer = new PolicySerializer(_policy);
 		_string = serializer.toString();
-                _hash = DigestUtils.sha256Hex(_string);
+		_hash = DigestUtils.sha256Hex(_string);
 	}
 
 	/**
@@ -136,6 +137,13 @@ public class RangerPolicyResourceSignature {
 				resources.put(resourceName, resourceView);
 			}
 			String resource = resources.toString();
+			if (CollectionUtils.isNotEmpty(_policy.getValiditySchedules())) {
+				resource += _policy.getValiditySchedules().toString();
+			}
+			if (_policy.getPolicyPriority() != null && _policy.getPolicyPriority() != RangerPolicy.POLICY_PRIORITY_NORMAL) {
+				resource += _policy.getPolicyPriority();
+			}
+
 			String result = String.format("{version=%d,type=%d,resource=%s}", _SignatureVersion, type, resource);
 			return result;
 		}

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerTag.java
----------------------------------------------------------------------
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerTag.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerTag.java
index 743b028..9e620c3 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerTag.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerTag.java
@@ -26,7 +26,9 @@ import org.codehaus.jackson.map.annotate.JsonSerialize;
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlRootElement;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY)
@@ -40,12 +42,15 @@ public class RangerTag extends RangerBaseModelObject {
 	public static final short OWNER_SERVICERESOURCE = 0;
 	public static final short OWNER_GLOBAL          = 1;
 
-	private String              type;
-    private Short               owner = OWNER_SERVICERESOURCE;
+	public static final String OPTION_TAG_VALIDITY_PERIODS = "TAG_VALIDITY_PERIODS";
 
-	private Map<String, String> attributes;
+	private String                       type;
+	private Short                        owner = OWNER_SERVICERESOURCE;
+	private Map<String, String>          attributes;
+	private Map<String, Object>          options;
+	private List<RangerValiditySchedule> validityPeriods;
 
-	public RangerTag(String guid, String type, Map<String, String> attributes, Short owner) {
+	public RangerTag(String guid, String type, Map<String, String> attributes, Short owner, Map<String, Object> options, List<RangerValiditySchedule> validityPeriods) {
 		super();
 
 		setGuid(guid);
@@ -53,14 +58,20 @@ public class RangerTag extends RangerBaseModelObject {
 		setOwner(owner);
 		setAttributes(attributes);
 		setOwner(owner);
+		setOptions(options);
+		setValidityPeriods(validityPeriods);
+	}
+
+	public RangerTag(String guid, String type, Map<String, String> attributes, Short owner) {
+		this(guid, type, attributes, owner, null, null);
 	}
 
 	public RangerTag(String type, Map<String, String> attributes) {
-		this(null, type, attributes, OWNER_SERVICERESOURCE);
+		this(null, type, attributes, OWNER_SERVICERESOURCE, null, null);
 	}
 
 	public RangerTag() {
-		this(null, null, null, OWNER_SERVICERESOURCE);
+		this(null, null, null, OWNER_SERVICERESOURCE, null, null);
 	}
 
 	public String getType() {
@@ -87,6 +98,39 @@ public class RangerTag extends RangerBaseModelObject {
 		this.owner = owner;
 	}
 
+	public Map<String, Object> getOptions() { return options; }
+
+	public void setOptions(Map<String, Object> options) {
+		if (this.options == null) {
+			this.options = new HashMap<>();
+		}
+		if (this.options == options) {
+			return;
+		}
+		this.options.clear();
+
+		if(options != null) {
+			for(Map.Entry<String, Object> e : options.entrySet()) {
+				this.options.put(e.getKey(), e.getValue());
+			}
+		}
+	}
+
+	public List<RangerValiditySchedule> getValidityPeriods() { return validityPeriods; }
+
+	public void setValidityPeriods(List<RangerValiditySchedule> validityPeriods) {
+		if (this.validityPeriods == null) {
+			this.validityPeriods = new ArrayList<>();
+		}
+		if (this.validityPeriods == validityPeriods) {
+			return;
+		}
+		this.validityPeriods.clear();
+
+		if(validityPeriods != null) {
+			this.validityPeriods.addAll(validityPeriods);
+		}
+	}
 	@Override
 	public String toString() {
 		StringBuilder sb = new StringBuilder();
@@ -114,7 +158,12 @@ public class RangerTag extends RangerBaseModelObject {
 		}
 		sb.append("} ");
 
-		sb.append(" }");
+		if (validityPeriods != null) {
+            sb.append("validityPeriods={").append(validityPeriods).append("} ");
+        }
+		sb.append("options={").append(options).append("} ");
+
+        sb.append(" }");
 
 		return sb;
 	}
@@ -129,6 +178,10 @@ public class RangerTag extends RangerBaseModelObject {
 				+ ((owner == null) ? 0 : owner.hashCode());
 		result = prime * result
 				+ ((attributes == null) ? 0 : attributes.hashCode());
+		result = prime * result
+				+ ((options == null) ? 0 : options.hashCode());
+		result = prime * result
+				+ ((validityPeriods == null) ? 0 : validityPeriods.hashCode());
 		return result;
 	}
 
@@ -156,6 +209,16 @@ public class RangerTag extends RangerBaseModelObject {
 				return false;
 		} else if (!attributes.equals(other.attributes))
 			return false;
+		if (options == null) {
+			if (other.options != null)
+				return false;
+		} else if (!options.equals(other.options))
+			return false;
+		if (validityPeriods == null) {
+			if (other.validityPeriods != null)
+				return false;
+		} else if (!validityPeriods.equals(other.validityPeriods))
+			return false;
 		return true;
 	}
 }

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerValidityRecurrence.java
----------------------------------------------------------------------
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerValidityRecurrence.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerValidityRecurrence.java
new file mode 100644
index 0000000..4bff3ea
--- /dev/null
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerValidityRecurrence.java
@@ -0,0 +1,231 @@
+/*
+ * 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.model;
+
+import org.codehaus.jackson.annotate.JsonAutoDetect;
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+
+@JsonAutoDetect(fieldVisibility= JsonAutoDetect.Visibility.ANY)
+@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown=true)
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+
+public class RangerValidityRecurrence {
+
+    @JsonAutoDetect(fieldVisibility= JsonAutoDetect.Visibility.ANY)
+    @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
+    @JsonIgnoreProperties(ignoreUnknown=true)
+    @XmlRootElement
+    @XmlAccessorType(XmlAccessType.FIELD)
+    public static class ValidityInterval {
+        private final int days;
+        private final int hours;
+        private final int minutes;
+
+        public static int getValidityIntervalInMinutes(ValidityInterval interval) {
+            return interval != null ?
+                    (interval.getDays()*24 + interval.getHours())*60 + interval.getMinutes() : 0;
+        }
+
+        public ValidityInterval() {
+            this.days = 0;
+            this.hours = 0;
+            this.minutes = 0;
+        }
+        public ValidityInterval(int days, int hours, int minutes) {
+            this.days = days;
+            this.hours = hours;
+            this.minutes = minutes;
+        }
+        public int getDays() { return days; }
+        public int getHours() { return hours; }
+        public int getMinutes() { return minutes; }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append("ValidityInterval={");
+
+            sb.append(" Interval={");
+            sb.append("days=").append(days);
+            sb.append(", hours=").append(hours);
+            sb.append(", minutes=").append(minutes);
+            sb.append(" }");
+
+            return sb.toString();
+        }
+    }
+
+    @JsonAutoDetect(fieldVisibility= JsonAutoDetect.Visibility.ANY)
+    @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
+    @JsonIgnoreProperties(ignoreUnknown=true)
+    @XmlRootElement
+    @XmlAccessorType(XmlAccessType.FIELD)
+    public static class RecurrenceSchedule {
+        static final String PERMITTED_SPECIAL_CHARACTERS = "*,-";
+        static final String PERMITTED_SPECIAL_CHARACTERS_FOR_MINUTES = ",";
+        public static final String WILDCARD = "*";
+
+        public enum ScheduleFieldSpec {
+            minute(0, 59, PERMITTED_SPECIAL_CHARACTERS_FOR_MINUTES),
+            hour(0, 23, PERMITTED_SPECIAL_CHARACTERS),
+            dayOfMonth(1, 31, PERMITTED_SPECIAL_CHARACTERS),
+            dayOfWeek(1, 7, PERMITTED_SPECIAL_CHARACTERS),
+            month(0, 11, PERMITTED_SPECIAL_CHARACTERS),
+            year(2017, 2100, PERMITTED_SPECIAL_CHARACTERS),
+            ;
+
+            public final int minimum;
+            public final int maximum;
+            public final String specialChars;
+
+            ScheduleFieldSpec(int minimum, int maximum, String specialChars) {
+                this.minimum = minimum;
+                this.maximum = maximum;
+                this.specialChars = specialChars;
+            }
+        }
+
+
+        private String minute;
+        private String hour;
+        private String dayOfMonth;
+        private String dayOfWeek;
+        private String month;
+        private String year;
+
+        public RecurrenceSchedule() {}
+
+        public RecurrenceSchedule(String minute, String hour, String dayOfMonth, String dayOfWeek, String month, String year) {
+            setMinute(minute);
+            setHour(hour);
+            setDayOfMonth(dayOfMonth);
+            setDayOfWeek(dayOfWeek);
+            setMonth(month);
+            setYear(year);
+        }
+        public String getMinute() { return minute;}
+        public String getHour() { return hour;}
+        public String getDayOfMonth() { return dayOfMonth;}
+        public String getDayOfWeek() { return dayOfWeek;}
+        public String getMonth() { return month;}
+        public String getYear() { return year;}
+
+        public void setMinute(String minute) { this.minute = minute;}
+        public void setHour(String hour) { this.hour = hour;}
+        public void setDayOfMonth(String dayOfMonth) { this.dayOfMonth = dayOfMonth;}
+        public void setDayOfWeek(String dayOfWeek) { this.dayOfWeek = dayOfWeek;}
+        public void setMonth(String month) { this.month = month;}
+        public void setYear(String year) { this.year = year;}
+
+        public void setFieldValue(ScheduleFieldSpec field, String value) {
+            switch (field) {
+                case minute:
+                    setMinute(value);
+                    break;
+                case hour:
+                    setHour(value);
+                    break;
+                case dayOfMonth:
+                    setDayOfMonth(value);
+                    break;
+                case dayOfWeek:
+                    setDayOfWeek(value);
+                    break;
+                case month:
+                    setMonth(value);
+                    break;
+                case year:
+                    setYear(value);
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        public String getFieldValue(ScheduleFieldSpec field) {
+            switch (field) {
+                case minute:
+                    return getMinute();
+                case hour:
+                    return getHour();
+                case dayOfMonth:
+                    return getDayOfMonth();
+                case dayOfWeek:
+                    return getDayOfWeek();
+                case month:
+                    return getMonth();
+                case year:
+                    return getYear();
+                default:
+                    return null;
+            }
+        }
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append(" Schedule={");
+            sb.append(" minute=").append(minute);
+            sb.append(", hour=").append(hour);
+            sb.append(", dayOfMonth=").append(dayOfMonth);
+            sb.append(", dayOfWeek=").append(dayOfWeek);
+            sb.append(", month=").append(month);
+            sb.append(", year=").append(year);
+            sb.append(" }");
+
+            return sb.toString();
+        }
+    }
+
+    private RecurrenceSchedule schedule;
+    private ValidityInterval interval;
+
+    public RangerValidityRecurrence() {
+    }
+
+    public RangerValidityRecurrence(RecurrenceSchedule schedule, ValidityInterval interval) {
+        setSchedule(schedule);
+        setInterval(interval);
+    }
+
+    public void setSchedule(RecurrenceSchedule schedule) { this.schedule = schedule;}
+
+    public void setInterval(ValidityInterval interval) { this.interval = interval; }
+
+    public RecurrenceSchedule getSchedule() { return schedule;}
+
+    public ValidityInterval getInterval() {
+        return interval;
+    }
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("{RangerValidityRecurrence= {");
+        sb.append(schedule).append(interval);
+        sb.append(" }");
+        return sb.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerValiditySchedule.java
----------------------------------------------------------------------
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerValiditySchedule.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerValiditySchedule.java
new file mode 100644
index 0000000..8fe3231
--- /dev/null
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerValiditySchedule.java
@@ -0,0 +1,93 @@
+/*
+ * 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.model;
+
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.codehaus.jackson.annotate.JsonAutoDetect;
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.TimeZone;
+
+@JsonAutoDetect(fieldVisibility=Visibility.ANY)
+@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown=true)
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+
+public class RangerValiditySchedule {
+
+    private static final Log LOG = LogFactory.getLog(RangerValiditySchedule.class);
+
+    public static final String VALIDITY_SCHEDULE_DATE_STRING_SPECIFICATION = "yyyy/MM/dd HH:mm:ss";
+
+    private String startTime;
+    private String endTime;
+    private String timeZone;
+
+    private List<RangerValidityRecurrence> recurrences;
+
+    public RangerValiditySchedule(String startTime, String endTime, String timeZone, List<RangerValidityRecurrence> recurrences) {
+        setTimeZone(timeZone);
+        setStartTime(startTime);
+        setEndTime(endTime);
+        setRecurrences(recurrences);
+    }
+
+    public RangerValiditySchedule() {
+        this(null, null, null, null);
+    }
+
+    public String getTimeZone() { return timeZone; }
+    public String getStartTime() { return startTime;}
+    public String getEndTime() { return endTime;}
+    public List<RangerValidityRecurrence> getRecurrences() { return recurrences;}
+
+    public void setTimeZone(String timeZone) { this.timeZone = timeZone; }
+    public void setStartTime(String startTime) { this.startTime = startTime;}
+    public void setEndTime(String endTime) { this.endTime = endTime;}
+    public void setRecurrences(List<RangerValidityRecurrence> recurrences) { this.recurrences = recurrences == null ? new ArrayList<>() : recurrences;}
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("RangerValiditySchedule={");
+
+        sb.append(", startTime=").append(startTime);
+        sb.append(", endTime=").append(endTime);
+        sb.append(", timeZone=").append(timeZone);
+
+        sb.append(", recurrences=").append(Arrays.toString(getRecurrences().toArray()));
+
+        sb.append("}");
+
+        return sb.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerPolicyValidator.java
----------------------------------------------------------------------
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerPolicyValidator.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerPolicyValidator.java
index 95eeca7..4d2fa23 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerPolicyValidator.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerPolicyValidator.java
@@ -37,6 +37,7 @@ import org.apache.ranger.plugin.model.RangerService;
 import org.apache.ranger.plugin.model.RangerServiceDef;
 import org.apache.ranger.plugin.model.RangerServiceDef.RangerAccessTypeDef;
 import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef;
+import org.apache.ranger.plugin.model.RangerValiditySchedule;
 import org.apache.ranger.plugin.store.ServiceStore;
 
 public class RangerPolicyValidator extends RangerValidator {
@@ -123,6 +124,19 @@ public class RangerPolicyValidator extends RangerValidator {
 				.build());
 			valid = false;
 		} else {
+			Integer priority = policy.getPolicyPriority();
+			if (priority != null) {
+				if (priority < RangerPolicy.POLICY_PRIORITY_NORMAL || priority > RangerPolicy.POLICY_PRIORITY_OVERRIDE) {
+					ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_POLICY_INVALID_PRIORITY;
+					failures.add(new ValidationFailureDetailsBuilder()
+							.field("policyPriority")
+							.isSemanticallyIncorrect()
+							.becauseOf(error.getMessage("out of range"))
+							.errorCode(error.getErrorCode())
+							.build());
+					valid = false;
+				}
+			}
 			Long id = policy.getId();
 			RangerPolicy existingPolicy = null;
 
@@ -306,6 +320,7 @@ public class RangerPolicyValidator extends RangerValidator {
 			}
 
 			if (serviceNameValid) { // resource checks can't be done meaningfully otherwise
+				valid = isValidValiditySchedule(policy, failures, action) && valid;
 				valid = isValidResources(policy, failures, action, isAdmin, serviceDef) && valid;
 				valid = isValidAccessTypeDef(policy, failures, action, isAdmin, serviceDef) && valid;
 			}
@@ -413,7 +428,41 @@ public class RangerPolicyValidator extends RangerValidator {
 		}
 		return valid;
 	}
-	
+
+	boolean isValidValiditySchedule(RangerPolicy policy, final List<ValidationFailureDetails> failures, Action action) {
+
+		boolean valid = true;
+		if (LOG.isDebugEnabled()) {
+			LOG.debug(String.format("==> RangerPolicyValidator.isValidValiditySchedule(%s, %s, %s)", policy, failures, action));
+		}
+		List<RangerValiditySchedule> validitySchedules = policy.getValiditySchedules();
+		List<RangerValiditySchedule> normalizedValiditySchedules = null;
+
+		for (RangerValiditySchedule entry : validitySchedules) {
+			RangerValidityScheduleValidator validator = new RangerValidityScheduleValidator(entry);
+
+			RangerValiditySchedule normalizedValiditySchedule = validator.validate(failures);
+			if (normalizedValiditySchedule == null) {
+				valid = false;
+				if (LOG.isDebugEnabled()) {
+					LOG.debug("Invalid Validity-Schedule:[" + entry +"]");
+				}
+			} else {
+				if (normalizedValiditySchedules == null) {
+					normalizedValiditySchedules = new ArrayList<>();
+				}
+				normalizedValiditySchedules.add(normalizedValiditySchedule);
+			}
+		}
+		if (valid && CollectionUtils.isNotEmpty(normalizedValiditySchedules)) {
+			policy.setValiditySchedules(normalizedValiditySchedules);
+		}
+		if (LOG.isDebugEnabled()) {
+			LOG.debug(String.format("<== RangerPolicyValidator.isValidValiditySchedule(%s, %s, %s): %s", policy, failures, action, valid));
+		}
+		return valid;
+	}
+
 	boolean isPolicyResourceUnique(RangerPolicy policy, final List<ValidationFailureDetails> failures, Action action) {
 		
 		if(LOG.isDebugEnabled()) {

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerValidator.java
----------------------------------------------------------------------
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerValidator.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerValidator.java
index 509c188..55973f5 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerValidator.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerValidator.java
@@ -96,7 +96,7 @@ public abstract class RangerValidator {
 		return false;
 	}
 
-	String serializeFailures(List<ValidationFailureDetails> failures) {
+	public static String serializeFailures(List<ValidationFailureDetails> failures) {
 		if(LOG.isDebugEnabled()) {
 			LOG.debug("==> RangerValidator.getFailureMessage()");
 		}

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerValidityScheduleValidator.java
----------------------------------------------------------------------
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerValidityScheduleValidator.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerValidityScheduleValidator.java
new file mode 100644
index 0000000..63e8bc0
--- /dev/null
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerValidityScheduleValidator.java
@@ -0,0 +1,480 @@
+/*
+ * 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.model.validation;
+
+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 java.io.Serializable;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.TimeZone;
+
+import org.apache.ranger.plugin.model.RangerValidityRecurrence;
+import org.apache.ranger.plugin.model.RangerValidityRecurrence.RecurrenceSchedule;
+import org.apache.ranger.plugin.model.RangerValiditySchedule;
+
+import javax.annotation.Nonnull;
+
+public class RangerValidityScheduleValidator {
+    private static final Log LOG = LogFactory.getLog(RangerValidityScheduleValidator.class);
+
+    private static final ThreadLocal<DateFormat> DATE_FORMATTER = new ThreadLocal<DateFormat>() {
+        @Override
+        protected DateFormat initialValue() {
+            return new SimpleDateFormat(RangerValiditySchedule.VALIDITY_SCHEDULE_DATE_STRING_SPECIFICATION);
+        }
+    };
+
+    private static final Set<String> validTimeZoneIds = new HashSet<>(Arrays.asList(TimeZone.getAvailableIDs()));
+
+    private final RangerValiditySchedule validitySchedule;
+    private Date                         startTime;
+    private Date                         endTime;
+    private RecurrenceSchedule           validityPeriodEstimator;
+    private RangerValiditySchedule       normalizedValiditySchedule;
+
+    public RangerValidityScheduleValidator(@Nonnull RangerValiditySchedule validitySchedule) {
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("==> RangerValidityScheduleValidator:: " + validitySchedule);
+        }
+
+        this.validitySchedule = validitySchedule;
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("<== RangerValidityScheduleValidator:: " + validitySchedule);
+        }
+    }
+
+    public RangerValiditySchedule validate(List<ValidationFailureDetails> validationFailures) {
+        RangerValiditySchedule ret = null;
+
+        if (StringUtils.isEmpty(validitySchedule.getStartTime()) && StringUtils.isEmpty(validitySchedule.getEndTime()) && CollectionUtils.isEmpty(validitySchedule.getRecurrences())) {
+            validationFailures.add(new ValidationFailureDetails(0, "startTime,endTime,recurrences", "", true, true, false, "empty values"));
+        } else {
+
+            if (validitySchedule.getStartTime() != null) {
+                try {
+                    startTime = DATE_FORMATTER.get().parse(validitySchedule.getStartTime());
+                } catch (ParseException exception) {
+                    LOG.error("Error parsing startTime:[" + validitySchedule.getStartTime() + "]", exception);
+                    validationFailures.add(new ValidationFailureDetails(0, "startTime", "", true, true, false, "invalid value"));
+                }
+            } else {
+                startTime = new Date();
+            }
+
+            if (validitySchedule.getEndTime() != null) {
+                try {
+                    endTime = DATE_FORMATTER.get().parse(validitySchedule.getEndTime());
+                } catch (ParseException exception) {
+                    LOG.error("Error parsing endTime:[" + validitySchedule.getEndTime() + "]", exception);
+                    validationFailures.add(new ValidationFailureDetails(0, "endTime", "", true, true, false, "invalid value"));
+                }
+            } else {
+                endTime = new Date(Long.MAX_VALUE);
+            }
+
+            if (startTime != null && endTime != null) {
+                validityPeriodEstimator = new RangerValidityRecurrence.RecurrenceSchedule();
+                normalizedValiditySchedule = new RangerValiditySchedule();
+
+                boolean isValid = validateTimeRangeSpec(validationFailures);
+
+                if (isValid) {
+                    if (LOG.isDebugEnabled()) {
+                        LOG.debug("validityPeriodEstimator:[" + validityPeriodEstimator + "]");
+                    }
+
+                    normalizedValiditySchedule.setStartTime(validitySchedule.getStartTime());
+                    normalizedValiditySchedule.setEndTime(validitySchedule.getEndTime());
+                    normalizedValiditySchedule.setTimeZone(validitySchedule.getTimeZone());
+
+                    ret = normalizedValiditySchedule;
+                } else {
+                    normalizedValiditySchedule = null;
+                }
+            }
+        }
+
+        return ret;
+    }
+
+    private boolean validateTimeRangeSpec(List<ValidationFailureDetails> validationFailures) {
+        boolean ret;
+        if (startTime.getTime() >= endTime.getTime()) {
+            validationFailures.add(new ValidationFailureDetails(0, "startTime", "", false, true, false, "endTime is not later than startTime"));
+            ret = false;
+        } else {
+            ret = true;
+        }
+        ret = validateTimeZone(validitySchedule.getTimeZone(), validationFailures) && ret;
+
+        for (RangerValidityRecurrence recurrence : validitySchedule.getRecurrences()) {
+            ret = validateValidityInterval(recurrence, validationFailures) && ret;
+
+            if (RangerValidityRecurrence.ValidityInterval.getValidityIntervalInMinutes(recurrence.getInterval()) > 0) {
+                ret = validateFieldSpec(recurrence, RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.minute, validationFailures) && ret;
+                ret = validateFieldSpec(recurrence, RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.hour, validationFailures) && ret;
+                ret = validateFieldSpec(recurrence, RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.dayOfMonth, validationFailures) && ret;
+                ret = validateFieldSpec(recurrence, RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.dayOfWeek, validationFailures) && ret;
+                ret = validateFieldSpec(recurrence, RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.month, validationFailures) && ret;
+                ret = validateFieldSpec(recurrence, RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.year, validationFailures) && ret;
+                ret = ret && validateIntervalDuration(recurrence, validationFailures);
+
+                if (ret) {
+                    RangerValidityRecurrence.RecurrenceSchedule schedule = new RangerValidityRecurrence.RecurrenceSchedule(getNormalizedValue(recurrence, RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.minute), getNormalizedValue(recurrence, RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.hour),
+                            getNormalizedValue(recurrence, RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.dayOfMonth), getNormalizedValue(recurrence, RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.dayOfWeek),
+                            getNormalizedValue(recurrence, RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.month), getNormalizedValue(recurrence, RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.year));
+                    RangerValidityRecurrence normalizedRecurrence = new RangerValidityRecurrence(schedule, recurrence.getInterval());
+                    normalizedValiditySchedule.getRecurrences().add(normalizedRecurrence);
+
+                }
+            }
+        }
+
+        return ret;
+    }
+
+    private boolean validateTimeZone(String timeZone, List<ValidationFailureDetails> validationFailures) {
+        boolean ret = !StringUtils.isNotBlank(timeZone) || validTimeZoneIds.contains(timeZone);
+
+        if (!ret) {
+            validationFailures.add(new ValidationFailureDetails(0, "timeZone", "", false, true, false, "invalid timeZone"));
+        }
+        return ret;
+    }
+
+    private boolean validateValidityInterval(RangerValidityRecurrence recurrence, List<ValidationFailureDetails> validationFailures) {
+        boolean ret = true;
+        RangerValidityRecurrence.ValidityInterval validityInterval = recurrence.getInterval();
+        if (validityInterval != null) {
+            if (validityInterval.getDays() < 0
+                    || (validityInterval.getHours() < 0 || validityInterval.getHours() > 23)
+                    || (validityInterval.getMinutes() < 0 || validityInterval.getMinutes() > 59)) {
+                validationFailures.add(new ValidationFailureDetails(0, "interval", "", false, true, false, "invalid interval"));
+                ret = false;
+            }
+        }
+        int validityIntervalInMinutes = RangerValidityRecurrence.ValidityInterval.getValidityIntervalInMinutes(validityInterval);
+        if (validityIntervalInMinutes > 0) {
+            if (StringUtils.isBlank(recurrence.getSchedule().getDayOfMonth()) && StringUtils.isBlank(recurrence.getSchedule().getDayOfWeek())) {
+                validationFailures.add(new ValidationFailureDetails(0, "validitySchedule", "", false, true, false, "empty dayOfMonth and dayOfWeek"));
+                ret = false;
+            }
+        }
+        return ret;
+    }
+
+    private boolean validateFieldSpec(RangerValidityRecurrence recurrence, RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec field, List<ValidationFailureDetails> validationFailures) {
+        boolean ret = true;
+
+        String fieldValue = recurrence.getSchedule().getFieldValue(field);
+        if (StringUtils.isBlank(fieldValue)) {
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("No value provided for [" + field + "]");
+            }
+            if (StringUtils.equals(field.name(), RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.dayOfWeek.name())
+                    || StringUtils.equals(field.name(), RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.dayOfMonth.name())) {
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("Allow blank value for dayOfWeek or dayOfMonth here. Check for both being null is done elsewhere.");
+                }
+            } else {
+                validationFailures.add(new ValidationFailureDetails(0, field.toString(), "", false, true, false, "No value provided"));
+            }
+        }
+        ret = validateCharacters(fieldValue, field.specialChars);
+
+        if (!ret) {
+            validationFailures.add(new ValidationFailureDetails(0, field.toString(), "", false, true, false, "invalid character(s)"));
+        } else {
+            // Valid month values in java.util.Date are from 1-12, in java.util.Calendar from 0 to 11
+            // Internally we use Calendar values for validation and evaluation
+            int minimum = field == RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.month ? field.minimum + 1 : field.minimum;
+            int maximum = field == RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.month ? field.maximum + 1 : field.maximum;
+            ret = validateRanges(recurrence, field, minimum, maximum, validationFailures);
+        }
+        return ret;
+    }
+
+    private boolean validateCharacters(String str, String permittedCharacters) {
+        boolean ret = true;
+        if (StringUtils.isNotBlank(str)) {
+            char[] chars = str.toCharArray();
+            for (char c : chars) {
+                if (!(Character.isDigit(c) || Character.isWhitespace(c) || StringUtils.contains(permittedCharacters, c))) {
+                    ret = false;
+                    break;
+                }
+            }
+        }
+        return ret;
+    }
+
+    private boolean validateIntervalDuration(RangerValidityRecurrence recurrence, List<ValidationFailureDetails> validationFailures) {
+        boolean ret = true;
+
+        if (!validationFailures.isEmpty() || validityPeriodEstimator == null) {
+            ret = false;
+        } else {
+            int minSchedulingInterval = 1; // In minutes
+
+            String minutes = validityPeriodEstimator.getFieldValue(RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.minute);
+            if (!StringUtils.equals(minutes, RangerValidityRecurrence.RecurrenceSchedule.WILDCARD)) {
+                minSchedulingInterval = StringUtils.isBlank(minutes) ? RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.minute.maximum + 1 : Integer.valueOf(minutes);
+
+                if (minSchedulingInterval == RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.minute.maximum + 1) {
+                    String hours = validityPeriodEstimator.getFieldValue(RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.hour);
+                    if (!StringUtils.equals(hours, RangerValidityRecurrence.RecurrenceSchedule.WILDCARD)) {
+                        int hour = StringUtils.isBlank(hours) ? RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.hour.maximum + 1 :Integer.valueOf(hours);
+                        minSchedulingInterval = hour * (RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.minute.maximum+1);
+
+                        if (hour == RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.hour.maximum + 1) {
+                            String dayOfMonths = validityPeriodEstimator.getFieldValue(RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.dayOfMonth);
+                            String dayOfWeeks = validityPeriodEstimator.getFieldValue(RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.dayOfWeek);
+
+                            int dayOfMonth = 1, dayOfWeek = 1;
+                            if (!StringUtils.equals(dayOfMonths, RangerValidityRecurrence.RecurrenceSchedule.WILDCARD)) {
+                                dayOfMonth = StringUtils.isBlank(dayOfMonths) ? RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.dayOfMonth.maximum + 1 : Integer.valueOf(dayOfMonths);
+                            }
+                            if (!StringUtils.equals(dayOfWeeks, RangerValidityRecurrence.RecurrenceSchedule.WILDCARD)) {
+                                dayOfWeek = StringUtils.isBlank(dayOfWeeks) ? RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.dayOfWeek.maximum + 1 : Integer.valueOf(dayOfWeeks);
+                            }
+                            if (!StringUtils.equals(dayOfMonths, RangerValidityRecurrence.RecurrenceSchedule.WILDCARD) || !StringUtils.equals(dayOfWeeks, RangerValidityRecurrence.RecurrenceSchedule.WILDCARD)) {
+                                int minDays = dayOfMonth > dayOfWeek ? dayOfWeek : dayOfMonth;
+                                minSchedulingInterval = minDays*(RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.hour.maximum+1)*(RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.minute.maximum+1);
+
+                                if (dayOfMonth == (RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.dayOfMonth.maximum+1) && dayOfWeek == (RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.dayOfWeek.maximum+1)) {
+                                    String months = validityPeriodEstimator.getFieldValue(RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.month);
+                                    if (!StringUtils.equals(months, RangerValidityRecurrence.RecurrenceSchedule.WILDCARD)) {
+                                        int month = StringUtils.isBlank(months) ? RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.month.maximum + 1 :Integer.valueOf(months);
+                                        minSchedulingInterval = month * 28 * (RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.hour.maximum + 1) * (RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.minute.maximum + 1);
+
+                                        if (month == RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.month.maximum + 1) {
+                                            // Maximum interval is 1 year
+                                            minSchedulingInterval = 365 * (RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.hour.maximum + 1) * (RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.minute.maximum + 1);
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            if (RangerValidityRecurrence.ValidityInterval.getValidityIntervalInMinutes(recurrence.getInterval()) > minSchedulingInterval) {
+                if (LOG.isDebugEnabled()) {
+                    LOG.warn("Specified scheduling interval:" + RangerValidityRecurrence.ValidityInterval.getValidityIntervalInMinutes(recurrence.getInterval()) + " minutes] is more than minimum possible scheduling interval:[" + minSchedulingInterval + " minutes].");
+                    LOG.warn("This may turn this (expected to be temporary) policy into effectively permanent policy.");
+                }
+            }
+        }
+        return ret;
+    }
+
+    private boolean validateRanges(RangerValidityRecurrence recurrence, RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec field, int minValidValue, int maxValidValue, List<ValidationFailureDetails> validationFailures) {
+        boolean ret = true;
+
+        String value = null;
+        String fieldName = field.toString();
+
+        String noWhiteSpace = StringUtils.deleteWhitespace(recurrence.getSchedule().getFieldValue(field));
+        String[] specs = StringUtils.split(noWhiteSpace, ",");
+        class Range {
+            private int lower;
+            private int upper;
+            private Range(int lower, int upper) {
+                this.lower = lower;
+                this.upper = upper;
+            }
+        }
+        class RangeComparator implements Comparator<Range>, Serializable {
+            @Override
+            public int compare(Range me, Range other) {
+                int result;
+                result = Integer.compare(me.lower, other.lower);
+                if (result == 0) {
+                    result = Integer.compare(me.upper, other.upper);
+                }
+                return result;
+            }
+        }
+
+        List<Range> rangeOfValues = new ArrayList<>();
+
+        List<Integer> values = new ArrayList<>();
+
+        for (String spec : specs) {
+
+            if (StringUtils.isNotEmpty(spec)) {
+                // Range
+                if (spec.startsWith("-") || spec.endsWith("-")) {
+                    validationFailures.add(new ValidationFailureDetails(0, fieldName, "", false, true, false, "incorrect range spec"));
+                    ret = false;
+                } else {
+                    String[] ranges = StringUtils.split(spec, "-");
+                    if (ranges.length > 2) {
+                        validationFailures.add(new ValidationFailureDetails(0, fieldName, "", false, true, false, "incorrect range spec"));
+                        ret = false;
+                    } else if (ranges.length == 2) {
+                        int val1 = minValidValue, val2 = maxValidValue;
+                        if (!StringUtils.equals(ranges[0], RangerValidityRecurrence.RecurrenceSchedule.WILDCARD)) {
+                            val1 = Integer.valueOf(ranges[0]);
+                            if (val1 < minValidValue || val1 > maxValidValue) {
+                                validationFailures.add(new ValidationFailureDetails(0, fieldName, "", false, true, false, "incorrect lower range value"));
+                                ret = false;
+                            }
+                        } else {
+                            value = RangerValidityRecurrence.RecurrenceSchedule.WILDCARD;
+                        }
+                        if (!StringUtils.equals(ranges[1], RangerValidityRecurrence.RecurrenceSchedule.WILDCARD)) {
+                            val2 = Integer.valueOf(ranges[1]);
+                            if (val1 < minValidValue || val2 > maxValidValue) {
+                                validationFailures.add(new ValidationFailureDetails(0, fieldName, "", false, true, false, "incorrect upper range value"));
+                                ret = false;
+                            }
+                        } else {
+                            value = RangerValidityRecurrence.RecurrenceSchedule.WILDCARD;
+                        }
+                        if (ret) {
+                            if (val1 >= val2) {
+                                validationFailures.add(new ValidationFailureDetails(0, fieldName, "", false, true, false, "incorrect range"));
+                                ret = false;
+                            } else {
+                                value = RangerValidityRecurrence.RecurrenceSchedule.WILDCARD;
+                                for (Range range : rangeOfValues) {
+                                    if (range.lower == val1 || range.upper == val2) {
+                                        validationFailures.add(new ValidationFailureDetails(0, fieldName, "", false, true, false, "duplicate range"));
+                                        ret = false;
+                                        break;
+                                    }
+                                }
+                                if (ret) {
+                                    rangeOfValues.add(new Range(val1, val2));
+                                }
+                            }
+                        }
+                    } else if (ranges.length == 1) {
+                        if (!StringUtils.equals(ranges[0], RangerValidityRecurrence.RecurrenceSchedule.WILDCARD)) {
+                            int val = Integer.valueOf(ranges[0]);
+                            if (val < minValidValue || val > maxValidValue) {
+                                validationFailures.add(new ValidationFailureDetails(0, fieldName, "", false, true, false, "incorrect value"));
+                                ret = false;
+                            } else {
+                                if (!StringUtils.equals(value, RangerValidityRecurrence.RecurrenceSchedule.WILDCARD)) {
+                                    values.add(Integer.valueOf(ranges[0]));
+                                }
+                            }
+                        } else {
+                            value = RangerValidityRecurrence.RecurrenceSchedule.WILDCARD;
+                        }
+                    } else {
+                        ret = false;
+                    }
+                }
+            }
+        }
+        //if (ret) {
+            if (CollectionUtils.isNotEmpty(rangeOfValues)) {
+                rangeOfValues.sort( new RangeComparator());
+            }
+            for (int i = 0; i < rangeOfValues.size(); i++) {
+                Range range = rangeOfValues.get(i);
+                int upper = range.upper;
+                for (int j = i+1; j < rangeOfValues.size(); j++) {
+                    Range r = rangeOfValues.get(j);
+                    if (upper < r.upper) {
+                        validationFailures.add(new ValidationFailureDetails(0, fieldName, "", false, true, false, "overlapping range value"));
+                        ret = false;
+                    }
+                }
+            }
+        //}
+        if (ret) {
+            if (!StringUtils.equals(value, RangerValidityRecurrence.RecurrenceSchedule.WILDCARD)) {
+
+                int minDiff = (values.size() <= 1) ?  maxValidValue + 1 : Integer.MAX_VALUE;
+
+                if (values.size() > 1) {
+                    Collections.sort(values);
+                    for (int i = 0; i < values.size() - 1; i++) {
+                        int diff = values.get(i + 1) - values.get(i);
+                        if (diff < minDiff) {
+                            minDiff = diff;
+                        }
+                        int firstLastDiff = values.get(0) + (maxValidValue - minValidValue + 1) - values.get(values.size() - 1);
+
+                        if (minDiff > firstLastDiff) {
+                            minDiff = firstLastDiff;
+                        }
+                    }
+                }
+                if (values.size() > 0) {
+                    value = Integer.toString(minDiff);
+                }
+            }
+            validityPeriodEstimator.setFieldValue(field, value);
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("Set " + field + " to " + value);
+            }
+        }
+        return ret;
+    }
+
+    private String getNormalizedValue(RangerValidityRecurrence recurrence, RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec field) {
+        String ret = null;
+
+        if (RangerValidityRecurrence.ValidityInterval.getValidityIntervalInMinutes(recurrence.getInterval()) > 0) {
+            String noWhiteSpace = StringUtils.deleteWhitespace(recurrence.getSchedule().getFieldValue(field));
+            String[] specs = StringUtils.split(noWhiteSpace, ",");
+
+            List<String> values = new ArrayList<>();
+
+            for (String spec : specs) {
+                if (StringUtils.isNotBlank(spec)) {
+                    values.add(spec);
+                }
+            }
+            if (values.size() > 0) {
+                Collections.sort(values);
+                StringBuilder sb = new StringBuilder();
+                for (int i = 0; i < values.size(); i++) {
+                    if (i != 0) {
+                        sb.append(",");
+                    }
+                    sb.append(values.get(i));
+                }
+                ret = sb.toString();
+            }
+        }
+        return ret;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/PolicyEvaluatorForTag.java
----------------------------------------------------------------------
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/PolicyEvaluatorForTag.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/PolicyEvaluatorForTag.java
index 1aad049..1d961a1 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/PolicyEvaluatorForTag.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/PolicyEvaluatorForTag.java
@@ -26,33 +26,36 @@ import java.io.Serializable;
 import java.util.Comparator;
 
 public class PolicyEvaluatorForTag {
-    private final RangerPolicyEvaluator evaluator;
-    private final RangerTagForEval      tag;
-
-    PolicyEvaluatorForTag(RangerPolicyEvaluator evaluator, RangerTagForEval tag) {
-        this.evaluator = evaluator;
-        this.tag       = tag;
-    }
-
-    RangerPolicyEvaluator getEvaluator() {
-        return evaluator;
-    }
-
-    RangerTagForEval getTag() {
-        return tag;
-    }
-
-    public static class PolicyNameComparator implements Comparator<PolicyEvaluatorForTag>, Serializable {
-        @Override
-        public int compare(PolicyEvaluatorForTag me, PolicyEvaluatorForTag other) {
-            return me.getEvaluator().getPolicy().getName().compareTo(other.getEvaluator().getPolicy().getName());
-        }
-    }
-
-    public static class PolicyEvalOrderComparator implements Comparator<PolicyEvaluatorForTag>, Serializable {
-        @Override
-        public int compare(PolicyEvaluatorForTag me, PolicyEvaluatorForTag other) {
-            return RangerPolicyEvaluator.EVAL_ORDER_COMPARATOR.compare(me.getEvaluator(), other.getEvaluator());
-        }
-    }
+	public static final Comparator<PolicyEvaluatorForTag> EVAL_ORDER_COMPARATOR = new PolicyEvalOrderComparator();
+	public static final Comparator<PolicyEvaluatorForTag> NAME_COMPARATOR       = new PolicyNameComparator();
+
+	private final RangerPolicyEvaluator evaluator;
+	private final RangerTagForEval      tag;
+
+	PolicyEvaluatorForTag(RangerPolicyEvaluator evaluator, RangerTagForEval tag) {
+		this.evaluator = evaluator;
+		this.tag       = tag;
+	}
+
+	RangerPolicyEvaluator getEvaluator() {
+		return evaluator;
+	}
+
+	RangerTagForEval getTag() {
+		return tag;
+	}
+
+	static class PolicyNameComparator implements Comparator<PolicyEvaluatorForTag>, Serializable {
+		@Override
+		public int compare(PolicyEvaluatorForTag me, PolicyEvaluatorForTag other) {
+			return RangerPolicyEvaluator.NAME_COMPARATOR.compare(me.getEvaluator(), other.getEvaluator());
+		}
+	}
+
+	static class PolicyEvalOrderComparator implements Comparator<PolicyEvaluatorForTag>, Serializable {
+		@Override
+		public int compare(PolicyEvaluatorForTag me, PolicyEvaluatorForTag other) {
+			return RangerPolicyEvaluator.EVAL_ORDER_COMPARATOR.compare(me.getEvaluator(), other.getEvaluator());
+		}
+	}
 }

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerAccessResult.java
----------------------------------------------------------------------
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerAccessResult.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerAccessResult.java
index 1b01ea4..8e7844f 100644
--- a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerAccessResult.java
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerAccessResult.java
@@ -45,6 +45,7 @@ public class RangerAccessResult {
 	private boolean  isAudited;
 	private long     auditPolicyId  = -1;
 	private long     policyId  = -1;
+	private int      policyPriority;
 	private long     evaluatedPoliciesCount;
 	private String   reason;
 	private Map<String, Object> additionalInfo;
@@ -60,6 +61,7 @@ public class RangerAccessResult {
 		this.isAudited   = false;
 		this.auditPolicyId = -1;
 		this.policyId    = -1;
+		this.policyPriority = RangerPolicy.POLICY_PRIORITY_NORMAL;
 		this.evaluatedPoliciesCount = 0;
 		this.reason      = null;
 	}
@@ -68,6 +70,7 @@ public class RangerAccessResult {
 		this.isAccessDetermined = other.getIsAccessDetermined();
 		this.isAllowed   = other.getIsAllowed();
 		this.policyId    = other.getPolicyId();
+		this.policyPriority = other.getPolicyPriority();
 		this.evaluatedPoliciesCount = other.evaluatedPoliciesCount;
 		this.reason      = other.getReason();
 		this.additionalInfo = other.additionalInfo == null ? new HashMap<String, Object>() : new HashMap<>(other.additionalInfo);
@@ -123,6 +126,9 @@ public class RangerAccessResult {
 
 		this.isAllowed = isAllowed;
 	}
+	public int getPolicyPriority() { return policyPriority;}
+
+	public void setPolicyPriority(int policyPriority) { this.policyPriority = policyPriority; }
 
 	/**
 	 * @param reason the reason to set


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

Posted by ab...@apache.org.
http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/agents-common/src/test/resources/policyengine/validityscheduler/test-validity-schedules-invalid.json
----------------------------------------------------------------------
diff --git a/agents-common/src/test/resources/policyengine/validityscheduler/test-validity-schedules-invalid.json b/agents-common/src/test/resources/policyengine/validityscheduler/test-validity-schedules-invalid.json
new file mode 100644
index 0000000..ed62828
--- /dev/null
+++ b/agents-common/src/test/resources/policyengine/validityscheduler/test-validity-schedules-invalid.json
@@ -0,0 +1,146 @@
+[
+  {
+    "name": "Empty validity-schedule",
+    "validitySchedules": [
+      {}
+    ],
+    "accessTime": "20180112-14:32:27.000-0800",
+    "result": {
+      "isValid": false,
+      "isApplicable": false,
+      "validationFailureCount": 1
+    }
+  },
+  {
+    "name": "Invalid characters in validity-schedule",
+    "validitySchedules": [
+      {
+        "startTime": "2018/01/12 14:32:00",
+        "endTime": "2020/01/12 17:19:00",
+        "recurrences": [
+          {
+            "schedule": {"minute": "MINUTE", "hour": "HOUR", "dayOfMonth": "DAYOFMONTH", "dayOfWeek": "DAYOFWEEK", "month": "MONTH", "year": "YEAR"},
+            "interval": {"days": 10, "hours": 5, "minutes": 5}
+          }
+        ]
+      }
+    ],
+    "accessTime": "20180112-14:32:27.000-0800",
+    "result": {
+      "isValid": false,
+      "isApplicable": false,
+      "validationFailureCount": 6
+    }
+  },
+  {
+    "name": "Invalid characters in fields in validity-schedule",
+
+    "validitySchedules": [
+      {
+        "startTime": "2018/01/12 14:32:00",
+        "endTime": "2020/01/23 06:30:00",
+        "recurrences": [
+          {
+            "schedule": {"minute": "*", "hour": "@", "dayOfMonth": "!", "dayOfWeek": "#", "month": "%", "year": "^&()"},
+            "interval": {"minutes": 10  }
+          }
+        ]
+      }
+    ],
+    "accessTime": "20180112-14:32:27.000-0800",
+    "result": {
+      "isValid": false,
+      "isApplicable": false,
+      "validationFailureCount": 6
+    }
+  },
+  {
+    "name": "Incorrect validityInterval",
+
+    "validitySchedules": [
+      {
+        "startTime": "2018/01/12 14:32:00",
+        "endTime": "2020/01/23 06:30:00",
+        "timeZone":"",
+        "recurrences": [
+          {
+            "schedule": {"minute": "10", "hour": "*", "dayOfMonth": "*", "dayOfWeek": "*", "month": "*", "year": "2018"},
+            "interval": {"days": -1, "hours": 24, "minutes": 60}
+          }
+        ]
+      }
+    ],
+    "accessTime": "20180112-14:32:27.000-0800",
+    "result": {
+      "isValid": false,
+      "isApplicable": false,
+      "validationFailureCount": 1
+    }
+  },
+  {
+    "name": "endTime earlier than startTime",
+
+    "validitySchedules": [
+      {
+        "startTime": "2018/01/12 14:32:00",
+        "endTime": "2018/01/02 22:30:00",
+        "recurrences": [
+          {
+            "schedule": {"minute": "45, 30, 1, 14", "hour": "*, 0,20, 21", "dayOfMonth": "1-14, 15,31", "dayOfWeek": "0,4, 6", "month": "11, 0-1", "year": "2018"}
+          }
+        ]
+      }
+    ],
+    "accessTime": "20180112-14:32:27.000-0800",
+    "result": {
+      "isValid": false,
+      "isApplicable": false,
+      "validationFailureCount": 1
+    }
+  },
+  {
+    "name": "Overlapping ranges, incorrect range, invalid field value in validity-schedule",
+
+    "validitySchedules": [
+      {
+        "startTime": "2018/01/12 14:32:00",
+        "endTime": "2020/01/22 22:30:00",
+        "timeZone":"",
+        "recurrences": [
+          {
+            "schedule": {"minute": "1,10,5", "hour": "0-3, 1-4, 9-8,20, 21", "dayOfMonth": "1-14, 15,31", "dayOfWeek": "0,4, 6", "month": "12, 1-2", "year": "2018"},
+            "interval": {"minutes": 10 }
+          }
+        ]
+      }
+    ],
+    "accessTime": "20180112-14:32:27.000-0800",
+    "result": {
+      "isValid": false,
+      "isApplicable": false,
+      "validationFailureCount": 3
+    }
+  },
+  {
+    "name": "Both dayOfWeek and dayOfMonth blank",
+
+    "validitySchedules": [
+      {
+        "startTime": "2018/01/12 14:32:00",
+        "endTime": "2020/01/23 06:30:00",
+        "recurrences": [
+          {
+            "schedule": {"minute": "45, 30, 1, 14", "hour": "*, 0,20, 21", "dayOfMonth": "", "dayOfWeek": "", "month": "12, 1-2", "year": "2018"},
+            "interval": {"minutes": 10 }
+          }
+        ]
+      }
+    ],
+    "accessTime": "20180112-14:32:27.000-0800",
+    "result": {
+      "isValid": false,
+      "isApplicable": false,
+      "validationFailureCount": 1
+    }
+  }
+]
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/agents-common/src/test/resources/policyengine/validityscheduler/test-validity-schedules-valid-and-applicable.json
----------------------------------------------------------------------
diff --git a/agents-common/src/test/resources/policyengine/validityscheduler/test-validity-schedules-valid-and-applicable.json b/agents-common/src/test/resources/policyengine/validityscheduler/test-validity-schedules-valid-and-applicable.json
new file mode 100644
index 0000000..38921d7
--- /dev/null
+++ b/agents-common/src/test/resources/policyengine/validityscheduler/test-validity-schedules-valid-and-applicable.json
@@ -0,0 +1,507 @@
+[
+  {
+    "name": "Non-repeating validity-schedule",
+    "validitySchedules": [
+      {
+        "startTime": "2018/01/12 14:32:00",
+        "endTime": "2018/02/03 12:16:00",
+        "recurrences": [
+          {
+            "schedule": {
+              "minute": "60",
+              "hour": "*",
+              "dayOfMonth": "*",
+              "dayOfWeek": "5, 7",
+              "month": "*",
+              "year": "2018"
+            },
+            "interval": {
+              "minutes": 0
+            }
+          }
+        ]
+      }
+    ],
+    "accessTime": "20180113-14:32:27.000-0800",
+    "result": {
+      "isValid": true,
+      "isApplicable": true,
+      "validationFailureCount": 0
+    }
+  },
+  {
+    "name": "Applicable for 9 minutes in a 10 minute interval",
+    "validitySchedules": [
+      {
+        "startTime": "2018/01/12 14:32:00",
+        "endTime": "2018/02/03 12:16:00",
+        "recurrences": [
+          {
+            "schedule": {
+              "minute": "0, 10, 20, 30, 40, 50",
+              "hour": "*",
+              "dayOfMonth": "*",
+              "dayOfWeek": "5, 7",
+              "month": "*",
+              "year": "2018"
+            },
+            "inteval": {
+              "minutes": 9
+            }
+          }
+        ]
+      }
+    ],
+    "accessTime": "20180112-14:32:27.000-0800",
+    "result": {
+      "isValid": true,
+      "isApplicable": true,
+      "validationFailureCount": 0
+    }
+  },
+  {
+    "name": "Applicable every Wednesday or Saturday or 1st of month starting at 10 past noon hour PST for 2 days and 10 hours",
+    "validitySchedules": [
+      {
+        "startTime": "2018/01/12 14:32:00",
+        "endTime": "2018/02/03 12:16:00",
+        "recurrences": [
+          {
+            "schedule": {
+              "minute": "10",
+              "hour": "12",
+              "dayOfMonth": "1",
+              "dayOfWeek": "4, 7",
+              "month": "*",
+              "year": "2018"
+            },
+            "interval": {
+              "days": 2,
+              "hours": 10
+            }
+          }
+        ]
+      }
+      ],
+    "accessTime": "20180115-19:32:27.000-0800",
+    "result": {
+      "isValid": true,
+      "isApplicable": true,
+      "validationFailureCount": 0
+    }
+  },
+  {
+    "name": "Applicable every Wednesday or Saturday or 1st of month starting at 59 minutes past midnight PST for 1 day and 2 minutes",
+    "validitySchedules": [
+      {
+        "startTime": "2017/01/12 14:32:00",
+        "endTime": "2018/02/03 12:16:00",
+        "recurrences": [
+          {
+            "schedule": {
+              "minute": "59",
+              "hour": "0",
+              "dayOfMonth": "1",
+              "dayOfWeek": "4, 7",
+              "month": "*",
+              "year": "2017-2018"
+            },
+            "interval": {
+              "days": 1,
+              "minutes": 2
+            }
+          }
+        ]
+      }
+      ],
+    "accessTime": "20180102-01:00:00.000-0800",
+    "result": {
+      "isValid": true,
+      "isApplicable": true,
+      "validationFailureCount": 0
+    }
+  },
+  {
+    "name": "Applicable every 31st of month starting at noon Zulu for 1 day, starting from 1/1/18",
+    "validitySchedules": [
+      {
+        "startTime": "2018/01/01 00:00:00",
+        "endTime": "2050/02/03 12:16:00",
+        "recurrences": [
+          {
+            "schedule": {
+              "minute": "0",
+              "hour": "12",
+              "dayOfMonth": "31",
+              "dayOfWeek": "*",
+              "month": "*",
+              "year": "2017-2018"
+            },
+            "interval": {
+              "days": 1
+            }
+          }
+        ]
+      }
+      ],
+    "accessTime": "20180301-00:00:00.000-0800",
+    "result": {
+      "isValid": true,
+      "isApplicable": true,
+      "validationFailureCount": 0
+    }
+  }
+  ,
+  {
+    "name": "validity-interval longer than minimum diff in  - still not applicable",
+    "validitySchedules": [
+      {
+        "startTime": "2018/01/12 14:32:00",
+        "endTime": "2020/01/23 06:30:00",
+        "recurrences": [
+          {
+            "schedule": {
+              "minute": "0",
+              "hour": "12",
+              "dayOfMonth": "1",
+              "dayOfWeek": "2, 3",
+              "month": "*",
+              "year": "*"
+            },
+            "interval": {
+              "days": 1,
+              "hours": 23,
+              "minutes": 59
+            }
+          }
+        ]
+      }
+      ],
+    "accessTime": "20180304-14:40:00.000-0800",
+    "result": {
+      "isValid": true,
+      "isApplicable": false,
+      "validationFailureCount": 0
+    }
+  },
+  {
+    "name": "validity-interval longer than minimum diff in schedule - applicable",
+    "validitySchedules": [
+      {
+        "startTime": "2018/01/12 14:32:00",
+        "endTime": "2020/01/23 06:30:00",
+        "recurrences": [
+          {
+            "schedule": {
+              "minute": "0",
+              "hour": "12",
+              "dayOfMonth": "1",
+              "dayOfWeek": "2, 3",
+              "month": "*",
+              "year": "*"
+            },
+            "interval": {
+              "days": 1,
+              "hours": 23,
+              "minutes": 59
+            }
+          }
+        ]
+      }
+      ],
+    "accessTime": "20180303-10:40:00.000-0800",
+    "result": {
+      "isValid": true,
+      "isApplicable": true,
+      "validationFailureCount": 0
+    }
+  },
+  {
+    "name": "dayOfWeek non blank and dayOfMonth blank - applicable",
+    "validitySchedules": [
+      {
+        "startTime": "2018/01/12 14:32:00",
+        "endTime": "2020/01/23 06:30:00",
+        "recurrences": [
+          {
+            "schedule": {
+              "minute": "45, 30, 1, 14",
+              "hour": "0,20, 21",
+              "dayOfMonth": "",
+              "dayOfWeek": "1",
+              "month": "12, 1-2",
+              "year": "2018"
+            },
+            "interval": {
+              "hours": 5
+            }
+          }
+        ]
+      }
+      ],
+    "accessTime": "20180114-04:32:27.000-0800",
+    "result": {
+      "isValid": true,
+      "isApplicable": true,
+      "validationFailureCount": 0
+    }
+  },
+  {
+    "name": "dayOfWeek non blank and dayOfMonth blank - not applicable",
+    "validitySchedules": [
+      {
+        "startTime": "2018/01/12 14:32:00",
+        "endTime": "2020/01/23 06:30:00",
+        "recurrences": [
+          {
+            "schedule": {
+              "minute": "45, 30, 1, 14",
+              "hour": "0,20, 21",
+              "dayOfMonth": "",
+              "dayOfWeek": "1",
+              "month": "12, 1-2",
+              "year": "2018"
+            },
+            "interval": {
+              "hours": 5
+            }
+          }
+        ]
+      }
+      ],
+    "accessTime": "20180119-04:32:27.000-0800",
+    "result": {
+      "isValid": true,
+      "isApplicable": false,
+      "validationFailureCount": 0
+    }
+  },
+  {
+    "name": "dayOfWeek blank and dayOfMonth not blank -  applicable",
+    "validitySchedules": [
+      {
+        "startTime": "2018/01/12 14:32:00",
+        "endTime": "2020/01/23 06:30:00",
+        "recurrences": [
+          {
+            "schedule": {
+              "minute": "45, 30, 1, 14",
+              "hour": "0,20, 21",
+              "dayOfMonth": "15, 20",
+              "dayOfWeek": "",
+              "month": "12, 1-2",
+              "year": "2018"
+            },
+            "interval": {
+              "days": 5
+            }
+          }
+        ]
+      }
+      ],
+    "accessTime": "20180119-04:32:27.000-0800",
+    "result": {
+      "isValid": true,
+      "isApplicable": true,
+      "validationFailureCount": 0
+    }
+  },
+  {
+    "name": "dayOfWeek blank and dayOfMonth not blank -  not applicable",
+    "validitySchedules": [
+      {
+        "startTime": "2018/01/12 14:32:00",
+        "endTime": "2020/01/23 06:30:00",
+        "recurrences": [
+          {
+            "schedule": {
+              "minute": "45, 30, 1, 14",
+              "hour": "0,20, 21",
+              "dayOfMonth": "15, 20",
+              "dayOfWeek": "",
+              "month": "12, 1-2",
+              "year": "2018"
+            },
+            "interval": {
+              "days": 3
+            }
+          }
+        ]
+      }
+      ],
+    "accessTime": "20180119-04:32:27.000-0800",
+    "result": {
+      "isValid": true,
+      "isApplicable": false,
+      "validationFailureCount": 0
+    }
+  },
+  {
+    "name": "Applicable every 31st of month or every Thursday starting at noon Zulu for 1 day, starting from 1/1/18 - not applicable",
+    "validitySchedules": [
+      {
+        "startTime": "2018/01/01 00:00:00",
+        "endTime": "2050/02/03 12:16:00",
+        "recurrences": [
+          {
+            "schedule": {
+              "minute": "0",
+              "hour": "12",
+              "dayOfMonth": "31",
+              "dayOfWeek": "5",
+              "month": "*",
+              "year": "2017-2018"
+            },
+            "interval": {
+              "days": 1
+            }
+          }
+        ]
+      }
+      ],
+    "accessTime": "20180301-00:00:00.000-0800",
+    "result": {
+      "isValid": true,
+      "isApplicable": false,
+      "validationFailureCount": 0
+    }
+  },
+  {
+    "name": "Applicable every 31st of month or every Thursday starting at noon Zulu for 1 day, starting from 1/1/18 -  applicable",
+    "validitySchedules": [
+      {
+        "startTime": "2018/01/01 00:00:00",
+        "endTime": "2050/02/03 12:16:00",
+        "recurrences": [
+          {
+            "schedule": {
+              "minute": "0",
+              "hour": "12",
+              "dayOfMonth": "31",
+              "dayOfWeek": "5",
+              "month": "*",
+              "year": "2017-2018"
+            },
+            "interval": {
+              "days": 1
+            }
+          }
+        ]
+      }
+      ],
+    "accessTime": "20180216-00:00:00.000-0800",
+    "result": {
+      "isValid": true,
+      "isApplicable": true,
+      "validationFailureCount": 0
+    }
+  },
+  {
+    "name": "Applicable on Feb 29",
+    "validitySchedules": [
+      {
+        "startTime": "2020/01/01 00:00:00",
+        "endTime": "2050/02/03 12:16:00",
+        "recurrences": [
+          {
+            "schedule": {
+              "minute": "0",
+              "hour": "*",
+              "dayOfMonth": "29",
+              "dayOfWeek": "",
+              "month": "2",
+              "year": "2020-2026"
+            },
+            "interval": {
+              "days": 1
+            }
+          }
+        ]
+      }
+      ],
+    "accessTime": "20200301-00:00:00.000-0800",
+    "result": {
+      "isValid": true,
+      "isApplicable": true,
+      "validationFailureCount": 0
+    }
+  },
+  {
+    "name": "Not Applicable on Mar 29",
+    "validitySchedules": [
+      {
+        "startTime": "2020/01/01 00:00:00",
+        "endTime": "2050/02/03 12:16:00",
+        "recurrences": [
+          {
+            "schedule": {
+              "minute": "0",
+              "hour": "*",
+              "dayOfMonth": "29",
+              "dayOfWeek": "",
+              "month": "3",
+              "year": "2020-2026"
+            },
+            "interval": {
+              "days": 1
+            }
+          }
+        ]
+      }
+      ],
+    "accessTime": "20200301-00:00:00.000-0800",
+    "result": {
+      "isValid": true,
+      "isApplicable": false,
+      "validationFailureCount": 0
+    }
+  },
+  {
+    "name": "Applicable on Feb 29 with a timezone specified in the schedule",
+    "validitySchedules": [
+      {
+        "startTime": "2020/01/01 00:00:00",
+        "endTime": "2050/02/03 12:16:00",
+        "timeZone": "IST",
+        "recurrences": [
+          {
+            "schedule": {
+              "minute": "0",
+              "hour": "*",
+              "dayOfMonth": "29",
+              "dayOfWeek": "",
+              "month": "2",
+              "year": "2020-2026"
+            },
+            "interval": {
+              "days": 1
+            }
+          }
+        ]
+      }
+      ],
+    "accessTime": "20200229-23:30:00.000-0800",
+    "result": {
+      "isValid": true,
+      "isApplicable": true,
+      "validationFailureCount": 0
+    }
+  },
+  {
+    "name": "Not applicable with a timezone specified in the schedule; no recurrences",
+    "validitySchedules": [
+      {
+        "startTime": "2020/01/01 00:00:00",
+        "endTime": "2050/02/03 12:16:00",
+        "timeZone": "GMT"
+      }
+    ],
+    "accessTime": "20191231-16:00:00.000-0700",
+    "result": {
+      "isValid": true,
+      "isApplicable": false,
+      "validationFailureCount": 0
+    }
+  }
+
+]

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/agents-common/src/test/resources/policyengine/validityscheduler/test-validity-schedules-valid.json
----------------------------------------------------------------------
diff --git a/agents-common/src/test/resources/policyengine/validityscheduler/test-validity-schedules-valid.json b/agents-common/src/test/resources/policyengine/validityscheduler/test-validity-schedules-valid.json
new file mode 100644
index 0000000..d42d3e6
--- /dev/null
+++ b/agents-common/src/test/resources/policyengine/validityscheduler/test-validity-schedules-valid.json
@@ -0,0 +1,152 @@
+[
+  {
+    "name": "AccessTime before startTime",
+    "validitySchedules": [
+      {
+        "startTime": "2018/01/12 14:32:00",
+        "endTime": "2020/01/12 14:32:00"
+      }
+    ],
+    "accessTime": "20170112-14:32:27.000-0800",
+    "result": {
+      "isValid": true,
+      "isApplicable": false,
+      "validationFailureCount": 0
+    }
+  },
+  {
+    "name": "AccessTime after endTime",
+    "validitySchedules": [
+      {
+        "startTime": "2018/01/12 14:32:00",
+        "endTime": "2018/02/12 14:32:00"
+      }
+    ],
+    "accessTime": "20180312-14:32:27.000-0800",
+    "result": {
+      "isValid": true,
+      "isApplicable": false,
+      "validationFailureCount": 0
+    }
+  },
+  {
+    "name": "Not applicable for minute field",
+    "validitySchedules": [
+      {
+        "startTime": "2018/01/12 14:32:00",
+        "endTime": "2020/01/12 14:32:00",
+        "recurrences": [
+          {
+            "schedule": {
+              "minute": "59, 15, 30",
+              "hour": "*",
+              "dayOfMonth": "*",
+              "dayOfWeek": "*",
+              "month": "*",
+              "year": "2018-2020"
+            },
+            "interval": {
+              "minutes": 10
+            }
+          }
+        ]
+      }
+    ],
+    "accessTime": "20180212-14:26:00.000-0800",
+    "result": {
+      "isValid": true,
+      "isApplicable": false,
+      "validationFailureCount": 0
+    }
+  },
+  {
+    "name": "Not applicable as accessTime earlier than startTime",
+    "validitySchedules": [
+      {
+        "startTime": "2018/01/12 14:32:00",
+        "endTime": "2020/01/12 14:32:00",
+        "recurrences": [
+          {
+            "schedule": {
+              "minute": "3",
+              "hour": "10, 21",
+              "dayOfMonth": "*",
+              "dayOfWeek": "*",
+              "month": "*",
+              "year": "2018-2020"
+            },
+            "interval": {
+              "hours": 2
+            }
+          }
+        ]
+      }
+    ],
+    "accessTime": "20170112-14:26:27.000-0800",
+    "result": {
+      "isValid": true,
+      "isApplicable": false,
+      "validationFailureCount": 0
+    }
+  },
+  {
+    "name": "dayOfWeek blank and dayOfMonth not blank",
+    "validitySchedules": [
+      {
+        "startTime": "2018/01/12 14:32:00",
+        "endTime": "2020/01/23 06:30:00",
+        "recurrences": [
+          {
+            "schedule": {
+              "minute": "45, 30, 1, 14",
+              "hour": "0,20, 21",
+              "dayOfMonth": "12",
+              "dayOfWeek": "",
+              "month": "12, 1-2",
+              "year": "2018"
+            },
+            "interval": {
+              "minutes": 10
+            }
+          }
+        ]
+      }
+    ],
+    "accessTime": "20180112-18:32:27.000-0800",
+    "result": {
+      "isValid": true,
+      "isApplicable": false,
+      "validationFailureCount": 0
+    }
+  },
+  {
+    "name": "dayOfWeek non blank and dayOfMonth blank",
+    "validitySchedules": [
+      {
+        "startTime": "2018/01/12 14:32:00",
+        "endTime": "2020/01/23 06:30:00",
+        "recurrences": [
+          {
+            "schedule": {
+              "minute": "45, 30, 1, 14",
+              "hour": "0,20, 21",
+              "dayOfMonth": "",
+              "dayOfWeek": "1",
+              "month": "12, 1-2",
+              "year": "2018"
+            },
+            "interval": {
+              "minutes": 10
+            }
+          }
+        ]
+      }
+      ],
+        "accessTime": "20180112-18:32:27.000-0800",
+        "result": {
+          "isValid": true,
+          "isApplicable": false,
+          "validationFailureCount": 0
+        }
+      }
+]

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/security-admin/db/mysql/optimized/current/ranger_core_db_mysql.sql
----------------------------------------------------------------------
diff --git a/security-admin/db/mysql/optimized/current/ranger_core_db_mysql.sql b/security-admin/db/mysql/optimized/current/ranger_core_db_mysql.sql
index d516d64..9e8fcad 100644
--- a/security-admin/db/mysql/optimized/current/ranger_core_db_mysql.sql
+++ b/security-admin/db/mysql/optimized/current/ranger_core_db_mysql.sql
@@ -551,6 +551,8 @@ CREATE TABLE  `x_policy` (
 `resource_signature` varchar(128) DEFAULT NULL,
 `is_enabled` tinyint(1) NOT NULL DEFAULT '0',
 `is_audit_enabled` tinyint(1) NOT NULL DEFAULT '0',
+`options` varchar(4000) NULL DEFAULT NULL,
+`policy_priority` int NOT NULL DEFAULT '0',
 primary key (`id`),
 KEY `x_policy_added_by_id` (`added_by_id`),
 KEY `x_policy_upd_by_id` (`upd_by_id`),
@@ -983,6 +985,7 @@ CREATE TABLE IF NOT EXISTS `x_tag` (
 `version` BIGINT(20) NULL DEFAULT NULL,
 `type` BIGINT(20) NOT NULL,
 `owned_by` SMALLINT DEFAULT 0 NOT NULL,
+`options` varchar(4000) NULL DEFAULT NULL,
 PRIMARY KEY (`id`),
 UNIQUE KEY `x_tag_UK_guid` (`guid`),
 KEY `x_tag_IDX_type` (`type`),

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/security-admin/db/mysql/patches/032-add-options-to-policy-and-tag-for-time-based-processing.sql
----------------------------------------------------------------------
diff --git a/security-admin/db/mysql/patches/032-add-options-to-policy-and-tag-for-time-based-processing.sql b/security-admin/db/mysql/patches/032-add-options-to-policy-and-tag-for-time-based-processing.sql
new file mode 100644
index 0000000..f8bedc4
--- /dev/null
+++ b/security-admin/db/mysql/patches/032-add-options-to-policy-and-tag-for-time-based-processing.sql
@@ -0,0 +1,44 @@
+-- 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.
+
+drop procedure if exists add_x_policy_columns_for_time_based_classification;
+delimiter ;;
+
+create procedure add_x_policy_columns_for_time_based_classification() begin
+if not exists (select * from information_schema.columns where table_schema=database() and table_name = 'x_policy' and column_name='options') then
+        ALTER TABLE x_policy ADD options varchar(4000) NULL DEFAULT NULL;
+end if;
+if not exists (select * from information_schema.columns where table_schema=database() and table_name = 'x_policy' and column_name='policy_priority') then
+        ALTER TABLE x_policy ADD policy_priority int NOT NULL DEFAULT '0';
+end if;
+end;;
+delimiter ;
+call add_x_policy_columns_for_time_based_classification();
+
+drop procedure if exists add_x_policy_columns_for_time_based_classification;
+
+drop procedure if exists add_x_policy_columns_for_time_based_classification;
+delimiter ;;
+
+create procedure add_x_tag_columns_for_time_based_classification() begin
+if not exists (select * from information_schema.columns where table_schema=database() and table_name = 'x_tag' and column_name='options') then
+        ALTER TABLE x_tag ADD options varchar(4000) NULL DEFAULT NULL;
+end if;
+end;;
+delimiter ;
+call add_x_tag_columns_for_time_based_classification();
+
+drop procedure if exists add_x_tag_columns_for_time_based_classification;
+

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/security-admin/db/oracle/optimized/current/ranger_core_db_oracle.sql
----------------------------------------------------------------------
diff --git a/security-admin/db/oracle/optimized/current/ranger_core_db_oracle.sql b/security-admin/db/oracle/optimized/current/ranger_core_db_oracle.sql
index abc7d59..55d44a1 100644
--- a/security-admin/db/oracle/optimized/current/ranger_core_db_oracle.sql
+++ b/security-admin/db/oracle/optimized/current/ranger_core_db_oracle.sql
@@ -455,6 +455,8 @@ description VARCHAR(1024) DEFAULT NULL NULL,
 resource_signature VARCHAR(128) DEFAULT NULL NULL,
 is_enabled NUMBER(1) DEFAULT '0' NOT NULL,
 is_audit_enabled NUMBER(1) DEFAULT '0' NOT NULL,
+options varchar(4000) DEFAULT NULL NULL,
+policy_priority NUMBER(11) DEFAULT 0 NOT NULL,
 primary key (id),
 CONSTRAINT x_policy_FK_added_by_id FOREIGN KEY (added_by_id) REFERENCES x_portal_user (id),
 CONSTRAINT x_policy_FK_upd_by_id FOREIGN KEY (upd_by_id) REFERENCES x_portal_user (id),
@@ -873,6 +875,7 @@ upd_by_id NUMBER(20) DEFAULT NULL NULL,
 version NUMBER(20) DEFAULT NULL NULL,
 type NUMBER(20) NOT NULL,
 owned_by NUMBER(6) DEFAULT 0 NOT NULL,
+options varchar(4000) DEFAULT NULL NULL,
 primary key (id),
 CONSTRAINT x_tag_UK_guid UNIQUE (guid),
 CONSTRAINT x_tag_FK_type FOREIGN KEY (type) REFERENCES x_tag_def (id),

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/security-admin/db/oracle/patches/032-add-options-to-policy-and-tag-for-time-based-processing.sql
----------------------------------------------------------------------
diff --git a/security-admin/db/oracle/patches/032-add-options-to-policy-and-tag-for-time-based-processing.sql b/security-admin/db/oracle/patches/032-add-options-to-policy-and-tag-for-time-based-processing.sql
new file mode 100644
index 0000000..4183572
--- /dev/null
+++ b/security-admin/db/oracle/patches/032-add-options-to-policy-and-tag-for-time-based-processing.sql
@@ -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.
+
+DECLARE
+        v_count number:=0;
+BEGIN   
+        select count(*) into v_count from user_tab_cols where table_name='X_POLICY' and column_name='OPTIONS';
+        if (v_count = 0) then 
+                execute immediate 'ALTER TABLE x_policy ADD options varchar(4000) DEFAULT NULL NULL';
+        end if; 
+
+        select count(*) into v_count from user_tab_cols where table_name='X_POLICY' and column_name='POLICY_PRIORITY';
+        if (v_count = 0) then 
+                execute immediate 'ALTER TABLE x_policy ADD policy_priority NUMBER(11) DEFAULT 0 NOT NULL';
+        end if; 
+
+        select count(*) into v_count from user_tab_cols where table_name='X_TAG' and column_name='OPTIONS';
+        if (v_count = 0) then 
+                execute immediate 'ALTER TABLE x_tag ADD options varchar(4000) DEFAULT NULL NULL';
+        end if; 
+        commit; 
+END;/

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/security-admin/db/postgres/optimized/current/ranger_core_db_postgres.sql
----------------------------------------------------------------------
diff --git a/security-admin/db/postgres/optimized/current/ranger_core_db_postgres.sql b/security-admin/db/postgres/optimized/current/ranger_core_db_postgres.sql
index 8862946..ef9ece7 100644
--- a/security-admin/db/postgres/optimized/current/ranger_core_db_postgres.sql
+++ b/security-admin/db/postgres/optimized/current/ranger_core_db_postgres.sql
@@ -463,6 +463,8 @@ description VARCHAR(1024) DEFAULT NULL NULL,
 resource_signature VARCHAR(128) DEFAULT NULL NULL,
 is_enabled BOOLEAN DEFAULT '0' NOT NULL,
 is_audit_enabled BOOLEAN DEFAULT '0' NOT NULL,
+options VARCHAR(4000) DEFAULT NULL NULL,
+policy_priority INT DEFAULT 0 NOT NULL,
 primary key(id),
 CONSTRAINT x_policy_FK_added_by_id FOREIGN KEY(added_by_id) REFERENCES x_portal_user(id),
 CONSTRAINT x_policy_FK_upd_by_id FOREIGN KEY(upd_by_id) REFERENCES x_portal_user(id),
@@ -951,6 +953,7 @@ upd_by_id BIGINT DEFAULT NULL NULL,
 version BIGINT DEFAULT NULL NULL,
 type BIGINT NOT NULL,
 owned_by SMALLINT DEFAULT 0 NOT NULL,
+options VARCHAR(4000) DEFAULT NULL NULL,
 primary key (id),
 CONSTRAINT x_tag_UK_guid UNIQUE (guid),
 CONSTRAINT x_tag_FK_type FOREIGN KEY (type) REFERENCES x_tag_def (id),

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/security-admin/db/postgres/patches/032-add-options-to-policy-and-tag-for-time-based-processing.sql
----------------------------------------------------------------------
diff --git a/security-admin/db/postgres/patches/032-add-options-to-policy-and-tag-for-time-based-processing.sql b/security-admin/db/postgres/patches/032-add-options-to-policy-and-tag-for-time-based-processing.sql
new file mode 100644
index 0000000..53ddacc
--- /dev/null
+++ b/security-admin/db/postgres/patches/032-add-options-to-policy-and-tag-for-time-based-processing.sql
@@ -0,0 +1,53 @@
+-- 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.
+
+-- function add_x_policy_columns_for_time_based_classification
+select 'delimiter start';
+CREATE OR REPLACE FUNCTION add_x_policy_columns_for_time_based_classification()
+RETURNS void AS $$
+DECLARE
+ v_column_exists integer := 0;
+BEGIN
+ select count(*) into v_column_exists from pg_attribute where attrelid in(select oid from pg_class where relname='x_policy') and attname='options';
+ IF v_column_exists = 0 THEN
+  ALTER TABLE x_policy ADD COLUMN options VARCHAR(4000) DEFAULT NULL NULL;
+ END IF;
+  select count(*) into v_column_exists from pg_attribute where attrelid in(select oid from pg_class where relname='x_policy') and attname='policy_priority';
+ IF v_column_exists = 0 THEN
+  ALTER TABLE x_policy ADD COLUMN policy_priority INT DEFAULT 0 NOT NULL;
+ END IF;
+END;
+$$ LANGUAGE plpgsql;
+select 'delimiter end';
+
+select add_x_policy_columns_for_time_based_classification();
+select 'delimiter end';
+
+select 'delimiter start';
+CREATE OR REPLACE FUNCTION add_x_tag_columns_for_time_based_classification()
+RETURNS void AS $$
+DECLARE
+ v_column_exists integer := 0;
+BEGIN
+ select count(*) into v_column_exists from pg_attribute where attrelid in(select oid from pg_class where relname='x_tag') and attname='options';
+ IF v_column_exists = 0 THEN
+  ALTER TABLE x_tag ADD COLUMN options VARCHAR(4000) DEFAULT NULL NULL;
+ END IF;
+END;
+$$ LANGUAGE plpgsql;
+select 'delimiter end';
+
+select add_x_tag_columns_for_time_based_classification();
+select 'delimiter end';

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/security-admin/db/sqlanywhere/optimized/current/ranger_core_db_sqlanywhere.sql
----------------------------------------------------------------------
diff --git a/security-admin/db/sqlanywhere/optimized/current/ranger_core_db_sqlanywhere.sql b/security-admin/db/sqlanywhere/optimized/current/ranger_core_db_sqlanywhere.sql
index bf3d954..c98d362 100644
--- a/security-admin/db/sqlanywhere/optimized/current/ranger_core_db_sqlanywhere.sql
+++ b/security-admin/db/sqlanywhere/optimized/current/ranger_core_db_sqlanywhere.sql
@@ -341,6 +341,8 @@ create table dbo.x_policy (
 	resource_signature varchar(128) DEFAULT NULL NULL,
 	is_enabled tinyint DEFAULT 0 NOT NULL,
 	is_audit_enabled tinyint DEFAULT 0 NOT NULL,
+	options varchar(4000) DEFAULT NULL NULL,
+	policy_priority int DEFAULT 0 NOT NULL,
 	CONSTRAINT x_policy_PK_id PRIMARY KEY CLUSTERED(id)
 )
 GO
@@ -690,6 +692,7 @@ CREATE TABLE dbo.x_tag(
 	version bigint DEFAULT NULL NULL,
 	type bigint NOT NULL,
 	owned_by smallint DEFAULT 0 NOT NULL,
+	options varchar(4000) DEFAULT NULL NULL,
 	CONSTRAINT x_tag_PK_id PRIMARY KEY CLUSTERED(id),
 	CONSTRAINT x_tag_UK_guid UNIQUE NONCLUSTERED (guid)
 )

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/security-admin/db/sqlanywhere/patches/032-add-options-to-policy-and-tag-for-time-based-processing.sql
----------------------------------------------------------------------
diff --git a/security-admin/db/sqlanywhere/patches/032-add-options-to-policy-and-tag-for-time-based-processing.sql b/security-admin/db/sqlanywhere/patches/032-add-options-to-policy-and-tag-for-time-based-processing.sql
new file mode 100644
index 0000000..1314ecb
--- /dev/null
+++ b/security-admin/db/sqlanywhere/patches/032-add-options-to-policy-and-tag-for-time-based-processing.sql
@@ -0,0 +1,29 @@
+-- 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.
+
+IF NOT EXISTS(select * from SYS.SYSCOLUMNS where tname = 'x_policy' and cname = 'options') THEN
+		ALTER TABLE dbo.x_policy ADD options varchar(4000) DEFAULT NULL NULL;
+END IF;
+GO
+IF NOT EXISTS(select * from SYS.SYSCOLUMNS where tname = 'x_policy' and cname = 'policy_priority') THEN
+		ALTER TABLE dbo.x_policy ADD policy_priority int DEFAULT 0 NOT NULL;
+END IF;
+GO
+IF NOT EXISTS(select * from SYS.SYSCOLUMNS where tname = 'x_tag' and cname = 'options') THEN
+		ALTER TABLE dbo.x_tag ADD options varchar(4000) DEFAULT NULL NULL;
+END IF;
+GO
+
+exit

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/security-admin/db/sqlserver/optimized/current/ranger_core_db_sqlserver.sql
----------------------------------------------------------------------
diff --git a/security-admin/db/sqlserver/optimized/current/ranger_core_db_sqlserver.sql b/security-admin/db/sqlserver/optimized/current/ranger_core_db_sqlserver.sql
index 56e2e99..61e81a0 100644
--- a/security-admin/db/sqlserver/optimized/current/ranger_core_db_sqlserver.sql
+++ b/security-admin/db/sqlserver/optimized/current/ranger_core_db_sqlserver.sql
@@ -1151,6 +1151,8 @@ CREATE TABLE [dbo].[x_policy] (
         [resource_signature] [varchar](128) DEFAULT NULL NULL,
         [is_enabled] [tinyint] DEFAULT 0 NOT NULL,
         [is_audit_enabled] [tinyint] DEFAULT 0 NOT NULL,
+        [options] [varchar](4000) DEFAULT NULL NULL,
+        [policy_priority] [int] DEFAULT 0 NOT NULL,
 PRIMARY KEY CLUSTERED
 (
         [id] ASC
@@ -1613,6 +1615,7 @@ CREATE TABLE [dbo].[x_tag](
         [version] [bigint] DEFAULT NULL NULL,
         [type] [bigint] NOT NULL,
         [owned_by] [smallint] DEFAULT 0 NOT NULL,
+        [options] [varchar](4000) DEFAULT NULL NULL,
         PRIMARY KEY CLUSTERED
 (
         [id] ASC

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/security-admin/db/sqlserver/patches/032-add-options-to-policy-and-tag-for-time-based-processing.sql
----------------------------------------------------------------------
diff --git a/security-admin/db/sqlserver/patches/032-add-options-to-policy-and-tag-for-time-based-processing.sql b/security-admin/db/sqlserver/patches/032-add-options-to-policy-and-tag-for-time-based-processing.sql
new file mode 100644
index 0000000..932861c
--- /dev/null
+++ b/security-admin/db/sqlserver/patches/032-add-options-to-policy-and-tag-for-time-based-processing.sql
@@ -0,0 +1,35 @@
+-- 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.
+
+GO
+IF NOT EXISTS(select * from INFORMATION_SCHEMA.columns where table_name = 'x_policy' and column_name = 'options')
+BEGIN
+	ALTER TABLE [dbo].[x_policy] ADD [options] [varchar](4000) DEFAULT NULL NULL;
+END
+GO
+GO
+IF NOT EXISTS(select * from INFORMATION_SCHEMA.columns where table_name = 'x_policy' and column_name = 'policy_priority')
+BEGIN
+	ALTER TABLE [dbo].[x_policy] ADD [policy_priority] [int] DEFAULT 0 NOT NULL;
+END
+GO
+GO
+IF NOT EXISTS(select * from INFORMATION_SCHEMA.columns where table_name = 'x_tag' and column_name = 'options')
+BEGIN
+	ALTER TABLE [dbo].[x_tag] ADD [options] [varchar](4000) DEFAULT NULL NULL;
+END
+GO
+
+exit

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/security-admin/src/main/java/org/apache/ranger/biz/RangerPolicyRetriever.java
----------------------------------------------------------------------
diff --git a/security-admin/src/main/java/org/apache/ranger/biz/RangerPolicyRetriever.java b/security-admin/src/main/java/org/apache/ranger/biz/RangerPolicyRetriever.java
index 2c4241d..463957c 100644
--- a/security-admin/src/main/java/org/apache/ranger/biz/RangerPolicyRetriever.java
+++ b/security-admin/src/main/java/org/apache/ranger/biz/RangerPolicyRetriever.java
@@ -26,9 +26,11 @@ import java.util.ListIterator;
 import java.util.Map;
 
 import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.collections.MapUtils;
 import org.apache.commons.lang.StringUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.apache.ranger.authorization.utils.JsonUtils;
 import org.apache.ranger.authorization.utils.StringUtil;
 import org.apache.ranger.db.RangerDaoManager;
 import org.apache.ranger.entity.*;
@@ -41,9 +43,10 @@ import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemDataMaskInfo;
 import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemRowFilterInfo;
 import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource;
 import org.apache.ranger.plugin.model.RangerPolicy.RangerRowFilterPolicyItem;
+import org.apache.ranger.plugin.model.RangerValiditySchedule;
 import org.apache.ranger.plugin.policyevaluator.RangerPolicyItemEvaluator;
 import org.apache.ranger.plugin.util.RangerPerfTracer;
-
+import org.apache.ranger.service.RangerPolicyService;
 
 public class RangerPolicyRetriever {
 	static final Log LOG      = LogFactory.getLog(RangerPolicyRetriever.class);
@@ -235,7 +238,7 @@ public class RangerPolicyRetriever {
 		final Map<Long, String> conditions      = new HashMap<Long, String>();
 		final Map<Long, String> resourceDefs    = new HashMap<Long, String>();
 		final Map<Long, String> dataMasks       = new HashMap<Long, String>();
-                final Map<Long, String> policyLabels    = new HashMap<Long, String>();
+		final Map<Long, String> policyLabels    = new HashMap<Long, String>();
 
 		String getUserName(Long userId) {
 			String ret = null;
@@ -257,25 +260,25 @@ public class RangerPolicyRetriever {
 			return ret;
 		}
 
-                String getPolicyLabelName(Long policyLabelId) {
-                        String ret = null;
+		String getPolicyLabelName(Long policyLabelId) {
+			String ret = null;
 
-                        if(policyLabelId != null) {
-                                ret = policyLabels.get(policyLabelId);
+			if (policyLabelId != null) {
+				ret = policyLabels.get(policyLabelId);
 
-                                if(ret == null) {
-                                        XXPolicyLabel xxPolicyLabel = daoMgr.getXXPolicyLabels().getById(policyLabelId);
+				if (ret == null) {
+					XXPolicyLabel xxPolicyLabel = daoMgr.getXXPolicyLabels().getById(policyLabelId);
 
-                                        if(xxPolicyLabel != null) {
-                                                ret = xxPolicyLabel.getPolicyLabel();
+					if (xxPolicyLabel != null) {
+						ret = xxPolicyLabel.getPolicyLabel();
 
-                                                policyLabels.put(policyLabelId,  ret);
-                                        }
-                                }
-                        }
+						policyLabels.put(policyLabelId, ret);
+					}
+				}
+			}
 
-                        return ret;
-                }
+			return ret;
+		}
 
 		String getUserScreenName(Long userId) {
 			String ret = null;
@@ -449,7 +452,7 @@ public class RangerPolicyRetriever {
 			List<XXPolicyItemCondition> xConditions   = daoMgr.getXXPolicyItemCondition().findByServiceId(serviceId);
 			List<XXPolicyItemDataMaskInfo>  xDataMaskInfos  = daoMgr.getXXPolicyItemDataMaskInfo().findByServiceId(serviceId);
 			List<XXPolicyItemRowFilterInfo> xRowFilterInfos = daoMgr.getXXPolicyItemRowFilterInfo().findByServiceId(serviceId);
-                        List<XXPolicyLabelMap> xxPolicyLabelMap = daoMgr.getXXPolicyLabelMap().findByServiceId(serviceId);
+			List<XXPolicyLabelMap> xxPolicyLabelMap = daoMgr.getXXPolicyLabelMap().findByServiceId(serviceId);
 
 			this.service          = xService;
 			this.iterPolicy       = xPolicies.listIterator();
@@ -482,7 +485,7 @@ public class RangerPolicyRetriever {
 			List<XXPolicyItemCondition> xConditions   = daoMgr.getXXPolicyItemCondition().findByPolicyId(policyId);
 			List<XXPolicyItemDataMaskInfo>  xDataMaskInfos  = daoMgr.getXXPolicyItemDataMaskInfo().findByPolicyId(policyId);
 			List<XXPolicyItemRowFilterInfo> xRowFilterInfos = daoMgr.getXXPolicyItemRowFilterInfo().findByPolicyId(policyId);
-                        List<XXPolicyLabelMap> xPolicyLabelMap = daoMgr.getXXPolicyLabelMap().findByPolicyId(policyId);
+			List<XXPolicyLabelMap> xPolicyLabelMap = daoMgr.getXXPolicyLabelMap().findByPolicyId(policyId);
 
 			this.service          = xService;
 			this.iterPolicy       = xPolicies.listIterator();
@@ -495,7 +498,7 @@ public class RangerPolicyRetriever {
 			this.iterConditions   = xConditions.listIterator();
 			this.iterDataMaskInfos  = xDataMaskInfos.listIterator();
 			this.iterRowFilterInfos = xRowFilterInfos.listIterator();
-                        this.iterPolicyLabels = xPolicyLabelMap.listIterator();
+			this.iterPolicyLabels = xPolicyLabelMap.listIterator();
 		}
 
 		RangerPolicy getNextPolicy() {
@@ -518,11 +521,24 @@ public class RangerPolicyRetriever {
 					ret.setService(service == null ? null : service.getName());
 					ret.setName(StringUtils.trim(xPolicy.getName()));
 					ret.setPolicyType(xPolicy.getPolicyType() == null ? RangerPolicy.POLICY_TYPE_ACCESS : xPolicy.getPolicyType());
+					ret.setPolicyPriority(xPolicy.getPolicyPriority() == null ? RangerPolicy.POLICY_PRIORITY_NORMAL : xPolicy.getPolicyPriority());
 					ret.setDescription(xPolicy.getDescription());
 					ret.setResourceSignature(xPolicy.getResourceSignature());
 					ret.setIsAuditEnabled(xPolicy.getIsAuditEnabled());
 
-                                        getPolicyLabels(ret);
+					Map<String, String> mapOfOptions = JsonUtils.jsonToMapStringString(xPolicy.getOptions());
+
+					if (MapUtils.isNotEmpty(mapOfOptions)) {
+						String validitySchedulesStr = mapOfOptions.get(RangerPolicyService.OPTION_POLICY_VALIDITY_SCHEDULES);
+
+						if (StringUtils.isNotEmpty(validitySchedulesStr)) {
+							List<RangerValiditySchedule> validitySchedules = JsonUtils.jsonToRangerValiditySchedule(validitySchedulesStr);
+
+							ret.setValiditySchedules(validitySchedules);
+						}
+					}
+
+					getPolicyLabels(ret);
 					getResource(ret);
 					getPolicyItems(ret);
 				}
@@ -531,24 +547,24 @@ public class RangerPolicyRetriever {
 			return ret;
 		}
 
-                private void getPolicyLabels(RangerPolicy ret) {
-                        List<String> xPolicyLabels = new ArrayList<String>();
-                        while(iterPolicyLabels.hasNext()) {
-                                XXPolicyLabelMap xPolicyLabel = iterPolicyLabels.next();
-                                if(xPolicyLabel.getPolicyId().equals(ret.getId())) {
-                                        String policyLabel = lookupCache.getPolicyLabelName(xPolicyLabel.getPolicyLabelId());
-                                        if (policyLabel != null) {
-                                                xPolicyLabels.add(policyLabel);
-                                        }
-                                        ret.setPolicyLabels(xPolicyLabels);
-                                } else {
-                                        if(iterPolicyLabels.hasPrevious()) {
-                                                iterPolicyLabels.previous();
-                                        }
-                                        break;
-                                }
-                        }
-                }
+		private void getPolicyLabels(RangerPolicy ret) {
+			List<String> xPolicyLabels = new ArrayList<String>();
+			while (iterPolicyLabels.hasNext()) {
+				XXPolicyLabelMap xPolicyLabel = iterPolicyLabels.next();
+				if (xPolicyLabel.getPolicyId().equals(ret.getId())) {
+					String policyLabel = lookupCache.getPolicyLabelName(xPolicyLabel.getPolicyLabelId());
+					if (policyLabel != null) {
+						xPolicyLabels.add(policyLabel);
+					}
+					ret.setPolicyLabels(xPolicyLabels);
+				} else {
+					if (iterPolicyLabels.hasPrevious()) {
+						iterPolicyLabels.previous();
+					}
+					break;
+				}
+			}
+		}
 
 		List<RangerPolicy> getAllPolicies() {
 			List<RangerPolicy> ret = new ArrayList<RangerPolicy>();
@@ -604,8 +620,8 @@ public class RangerPolicyRetriever {
 									|| iterAccesses.hasNext()
 									|| iterConditions.hasNext()
 									|| iterDataMaskInfos.hasNext()
-                                                                        || iterRowFilterInfos.hasNext()
-                                                                        || iterPolicyLabels.hasNext();
+									|| iterRowFilterInfos.hasNext()
+									|| iterPolicyLabels.hasNext();
 
 			return !moreToProcess;
 		}

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/security-admin/src/main/java/org/apache/ranger/biz/RangerTagDBRetriever.java
----------------------------------------------------------------------
diff --git a/security-admin/src/main/java/org/apache/ranger/biz/RangerTagDBRetriever.java b/security-admin/src/main/java/org/apache/ranger/biz/RangerTagDBRetriever.java
index 52c1288..5550572 100644
--- a/security-admin/src/main/java/org/apache/ranger/biz/RangerTagDBRetriever.java
+++ b/security-admin/src/main/java/org/apache/ranger/biz/RangerTagDBRetriever.java
@@ -26,9 +26,12 @@ import java.util.ListIterator;
 import java.util.Map;
 
 import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.collections.MapUtils;
+import org.apache.commons.lang.StringUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.ranger.authorization.hadoop.config.RangerConfiguration;
+import org.apache.ranger.authorization.utils.JsonUtils;
 import org.apache.ranger.authorization.utils.StringUtil;
 import org.apache.ranger.db.RangerDaoManager;
 import org.apache.ranger.entity.*;
@@ -36,7 +39,6 @@ import org.apache.ranger.plugin.model.*;
 import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource;
 import org.apache.ranger.plugin.util.RangerPerfTracer;
 
-
 public class RangerTagDBRetriever {
 	static final Log LOG = LogFactory.getLog(RangerTagDBRetriever.class);
 	static final Log PERF_LOG = RangerPerfTracer.getPerfLogger("db.RangerTagDBRetriever");
@@ -537,6 +539,18 @@ public class RangerTagDBRetriever {
 					ret.setUpdateTime(xTag.getUpdateTime());
 					ret.setVersion(xTag.getVersion());
 
+					Map<String, String> mapOfOptions = JsonUtils.jsonToMapStringString(xTag.getOptions());
+
+					if (MapUtils.isNotEmpty(mapOfOptions)) {
+						String validityPeriodsStr = mapOfOptions.get(RangerTag.OPTION_TAG_VALIDITY_PERIODS);
+
+						if (StringUtils.isNotEmpty(validityPeriodsStr)) {
+							List<RangerValiditySchedule> validityPeriods = JsonUtils.jsonToRangerValiditySchedule(validityPeriodsStr);
+
+							ret.setValidityPeriods(validityPeriods);
+						}
+					}
+
 					Map<Long, RangerTagDef> tagDefs = getTagDefs();
 					if (tagDefs != null) {
 						RangerTagDef tagDef = tagDefs.get(xTag.getType());
@@ -560,7 +574,6 @@ public class RangerTagDBRetriever {
 					String attributeName = xTagAttribute.getName();
 					String attributeValue = xTagAttribute.getValue();
 
-
 					tag.getAttributes().put(attributeName, attributeValue);
 				} else if (xTagAttribute.getTagId().compareTo(tag.getId()) > 0) {
 					if (iterTagAttribute.hasPrevious()) {

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/security-admin/src/main/java/org/apache/ranger/biz/TagDBStore.java
----------------------------------------------------------------------
diff --git a/security-admin/src/main/java/org/apache/ranger/biz/TagDBStore.java b/security-admin/src/main/java/org/apache/ranger/biz/TagDBStore.java
index 1583018..8341a73 100644
--- a/security-admin/src/main/java/org/apache/ranger/biz/TagDBStore.java
+++ b/security-admin/src/main/java/org/apache/ranger/biz/TagDBStore.java
@@ -20,6 +20,7 @@
 package org.apache.ranger.biz;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -49,6 +50,8 @@ import org.apache.ranger.entity.XXTagResourceMap;
 import org.apache.ranger.plugin.model.*;
 import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource;
 import org.apache.ranger.plugin.model.RangerTagDef.RangerTagAttributeDef;
+import org.apache.ranger.plugin.model.validation.RangerValidityScheduleValidator;
+import org.apache.ranger.plugin.model.validation.ValidationFailureDetails;
 import org.apache.ranger.plugin.store.AbstractTagStore;
 import org.apache.ranger.plugin.store.PList;
 import org.apache.ranger.plugin.store.RangerServiceResourceSignature;
@@ -298,6 +301,8 @@ public class TagDBStore extends AbstractTagStore {
 			LOG.debug("==> TagDBStore.createTag(" + tag + ")");
 		}
 
+		tag = validateTag(tag);
+
 		RangerTag ret = rangerTagService.create(tag);
 
 		createTagAttributes(ret.getId(), tag.getAttributes());
@@ -317,6 +322,8 @@ public class TagDBStore extends AbstractTagStore {
 			LOG.debug("==> TagDBStore.updateTag(" + tag + ")");
 		}
 
+		tag = validateTag(tag);
+
 		RangerTag existing = rangerTagService.read(tag.getId());
 
 		if (existing == null) {
@@ -1088,7 +1095,7 @@ public class TagDBStore extends AbstractTagStore {
 		}
 	}
 
-	private List<XXTagAttribute> createTagAttributes(Long tagId, Map<String, String> attributes) {
+	private List<XXTagAttribute> createTagAttributes(Long tagId, Map<String, String> attributes) throws Exception {
 		List<XXTagAttribute> ret = new ArrayList<XXTagAttribute>();
 
 		if(MapUtils.isNotEmpty(attributes)) {
@@ -1281,4 +1288,36 @@ public class TagDBStore extends AbstractTagStore {
 			LOG.debug("<== TagDBStore.deleteAllTagObjectsForService(" + serviceName + ")");
 		}
 	}
+
+	private RangerTag validateTag(RangerTag tag) throws Exception {
+		List<RangerValiditySchedule> validityPeriods = tag.getValidityPeriods();
+
+		if (CollectionUtils.isNotEmpty(validityPeriods)) {
+			List<RangerValiditySchedule>   normalizedValidityPeriods = new ArrayList<>();
+			List<ValidationFailureDetails> failures                  = new ArrayList<>();
+
+			for (RangerValiditySchedule validityPeriod : validityPeriods) {
+				RangerValidityScheduleValidator validator                = new RangerValidityScheduleValidator(validityPeriod);
+				RangerValiditySchedule          normalizedValidityPeriod = validator.validate(failures);
+
+				if (normalizedValidityPeriod != null && CollectionUtils.isEmpty(failures)) {
+					if (LOG.isDebugEnabled()) {
+						LOG.debug("Normalized ValidityPeriod:[" + normalizedValidityPeriod + "]");
+					}
+
+					normalizedValidityPeriods.add(normalizedValidityPeriod);
+				} else {
+					String error = "Incorrect time-specification:[" + Arrays.asList(failures) + "]";
+
+					LOG.error(error);
+
+					throw new Exception(error);
+				}
+			}
+
+			tag.setValidityPeriods(normalizedValidityPeriods);
+		}
+
+		return tag;
+	}
 }

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/security-admin/src/main/java/org/apache/ranger/common/RangerSearchUtil.java
----------------------------------------------------------------------
diff --git a/security-admin/src/main/java/org/apache/ranger/common/RangerSearchUtil.java b/security-admin/src/main/java/org/apache/ranger/common/RangerSearchUtil.java
index a2250ec..c6686ff 100644
--- a/security-admin/src/main/java/org/apache/ranger/common/RangerSearchUtil.java
+++ b/security-admin/src/main/java/org/apache/ranger/common/RangerSearchUtil.java
@@ -64,8 +64,8 @@ public class RangerSearchUtil extends SearchUtil {
 		ret.setParam(SearchFilter.POL_RESOURCE, request.getParameter(SearchFilter.POL_RESOURCE));
 		ret.setParam(SearchFilter.RESOURCE_SIGNATURE, request.getParameter(SearchFilter.RESOURCE_SIGNATURE));
 		ret.setParam(SearchFilter.POLICY_TYPE, request.getParameter(SearchFilter.POLICY_TYPE));
-                ret.setParam(SearchFilter.POLICY_LABEL, request.getParameter(SearchFilter.POLICY_LABEL));
-                ret.setParam(SearchFilter.POLICY_LABELS_PARTIAL, request.getParameter(SearchFilter.POLICY_LABELS_PARTIAL));
+		ret.setParam(SearchFilter.POLICY_LABEL, request.getParameter(SearchFilter.POLICY_LABEL));
+		ret.setParam(SearchFilter.POLICY_LABELS_PARTIAL, request.getParameter(SearchFilter.POLICY_LABELS_PARTIAL));
 		ret.setParam(SearchFilter.PLUGIN_HOST_NAME, request.getParameter(SearchFilter.PLUGIN_HOST_NAME));
 		ret.setParam(SearchFilter.PLUGIN_APP_TYPE, request.getParameter(SearchFilter.PLUGIN_APP_TYPE));
 		ret.setParam(SearchFilter.PLUGIN_ENTITY_TYPE, request.getParameter(SearchFilter.PLUGIN_ENTITY_TYPE));

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/security-admin/src/main/java/org/apache/ranger/entity/XXPolicyBase.java
----------------------------------------------------------------------
diff --git a/security-admin/src/main/java/org/apache/ranger/entity/XXPolicyBase.java b/security-admin/src/main/java/org/apache/ranger/entity/XXPolicyBase.java
index 69d28bb..2616594 100644
--- a/security-admin/src/main/java/org/apache/ranger/entity/XXPolicyBase.java
+++ b/security-admin/src/main/java/org/apache/ranger/entity/XXPolicyBase.java
@@ -75,6 +75,14 @@ public abstract class XXPolicyBase extends XXDBBase {
 	protected Integer policyType;
 
 	/**
+	 * policyPriority of the XXPolicy
+	 * <ul>
+	 * </ul>
+	 *
+	 */
+	@Column(name = "policy_priority")
+	protected Integer policyPriority;
+	/**
 	 * description of the XXPolicy
 	 * <ul>
 	 * </ul>
@@ -110,6 +118,14 @@ public abstract class XXPolicyBase extends XXDBBase {
 	@Column(name = "is_audit_enabled")
 	protected boolean isAuditEnabled;
 
+    /**
+     * options of the XXPolicy
+     * <ul>
+     * </ul>
+     *
+     */
+    @Column(name = "options")
+    protected String options;
 	/**
 	 * @return the gUID
 	 */
@@ -233,7 +249,7 @@ public abstract class XXPolicyBase extends XXDBBase {
 	/**
 	 * Returns the value for the member attribute <b>isEnabled</b>
 	 *
-	 * @return Date - value of member attribute <b>isEnabled</b> .
+	 * @return Value of member attribute <b>isEnabled</b> .
 	 */
 	public boolean getIsEnabled() {
 		return this.isEnabled;
@@ -253,7 +269,7 @@ public abstract class XXPolicyBase extends XXDBBase {
 	/**
 	 * Returns the value for the member attribute <b>isAuditEnabled</b>
 	 *
-	 * @return Date - value of member attribute <b>isAuditEnabled</b> .
+	 * @return Value of member attribute <b>isAuditEnabled</b> .
 	 */
 	public boolean getIsAuditEnabled() {
 		return this.isAuditEnabled;
@@ -267,6 +283,33 @@ public abstract class XXPolicyBase extends XXDBBase {
 		this.policyType = policyType;
 	}
 
+	public Integer getPolicyPriority() {
+		return policyPriority;
+	}
+
+	public void setPolicyPriority(Integer policyPriority) {
+		this.policyPriority = policyPriority;
+	}
+
+	/**
+	 * This method sets the value to the member attribute <b> options</b> .
+	 *
+	 * @param options
+	 *            Value to set member attribute <b> options</b>
+	 */
+	public void setOptions(String options) {
+        this.options = options;
+    }
+
+	/**
+	 * Returns the value for the member attribute <b>options</b>
+	 *
+	 * @return Value of member attribute <b>options</b> .
+	 */
+	public String getOptions() {
+        return this.options;
+    }
+
 	/*
 	 * (non-Javadoc)
 	 *
@@ -342,6 +385,21 @@ public abstract class XXPolicyBase extends XXDBBase {
 		} else if (!policyType.equals(other.policyType)) {
 			return false;
 		}
+		if (policyPriority == null) {
+			if (other.policyPriority != null) {
+				return false;
+			}
+		} else if (!policyPriority.equals(other.policyPriority)) {
+			return false;
+		}
+		if (options == null) {
+			if (other.options != null) {
+				return false;
+			}
+		} else if (!options.equals(other.options)) {
+			return false;
+		}
+
 		return true;
 	}
 
@@ -355,8 +413,9 @@ public abstract class XXPolicyBase extends XXDBBase {
 		String str = "XXPolicyBase={";
 		str += super.toString();
 		str += " [guid=" + guid + ", version=" + version + ", service=" + service + ", name=" + name
-				+ ", policyType=" + policyType + ", description=" + description + ", resourceSignature="
-				+ resourceSignature + ", isEnabled=" + isEnabled + ", isAuditEnabled=" + isAuditEnabled + "]";
+				+ ", policyType=" + policyType + ", policyPriority=" + policyPriority + ", description=" + description + ", resourceSignature="
+				+ resourceSignature + ", isEnabled=" + isEnabled + ", isAuditEnabled=" + isAuditEnabled
+				+ ", options=" + options + "]";
 		str += "}";
 		return str;
 	}

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/security-admin/src/main/java/org/apache/ranger/entity/XXTag.java
----------------------------------------------------------------------
diff --git a/security-admin/src/main/java/org/apache/ranger/entity/XXTag.java b/security-admin/src/main/java/org/apache/ranger/entity/XXTag.java
index 9155385..44d43ef 100644
--- a/security-admin/src/main/java/org/apache/ranger/entity/XXTag.java
+++ b/security-admin/src/main/java/org/apache/ranger/entity/XXTag.java
@@ -60,6 +60,9 @@ public class XXTag extends XXDBBase implements Serializable {
 	@Column(name = "owned_by")
 	protected Short owner;
 
+	@Column(name = "options")
+	protected String options;
+
 	@Override
 	public void setId(Long id) {
 		this.id = id;
@@ -118,7 +121,15 @@ public class XXTag extends XXDBBase implements Serializable {
 	public Short getOwner() { return owner; }
 	public void setOwner(Short owner) { this.owner = owner; }
 
-	@Override
+	public void setOptions(String options) {
+		this.options = options;
+	}
+
+	public String getOptions() {
+		return this.options;
+	}
+
+    @Override
 	public int getMyClassType() {
 		return AppConstants.CLASS_TYPE_XA_TAG;
 	}
@@ -137,6 +148,7 @@ public class XXTag extends XXDBBase implements Serializable {
 		result = prime * result + ((id == null) ? 0 : id.hashCode());
 		result = prime * result + ((type == null) ? 0 : type.hashCode());
 		result = prime * result + ((owner == null) ? 0 : owner.hashCode());
+		result = prime * result + ((options == null) ? 0 : options.hashCode());
 		return result;
 	}
 
@@ -179,6 +191,11 @@ public class XXTag extends XXDBBase implements Serializable {
 				return false;
 		} else if (!owner.equals(other.owner))
 			return false;
+		if (options == null) {
+			if (other.options != null)
+				return false;
+		} else if (!options.equals(other.options))
+			return false;
 		return true;
 	}
 
@@ -201,6 +218,7 @@ public class XXTag extends XXDBBase implements Serializable {
 		sb.append("guid={").append(guid).append("} ");
 		sb.append("type={").append(type).append("} ");
 		sb.append("owned_by={").append(owner).append("} ");
+		sb.append("options={").append(options).append("} ");
 		sb.append(" }");
 
 		return sb;

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/security-admin/src/main/java/org/apache/ranger/service/RangerPolicyServiceBase.java
----------------------------------------------------------------------
diff --git a/security-admin/src/main/java/org/apache/ranger/service/RangerPolicyServiceBase.java b/security-admin/src/main/java/org/apache/ranger/service/RangerPolicyServiceBase.java
index def5033..8d42165 100644
--- a/security-admin/src/main/java/org/apache/ranger/service/RangerPolicyServiceBase.java
+++ b/security-admin/src/main/java/org/apache/ranger/service/RangerPolicyServiceBase.java
@@ -17,7 +17,9 @@
 
 package org.apache.ranger.service;
 
+import org.apache.commons.collections.MapUtils;
 import org.apache.commons.lang.StringUtils;
+import org.apache.ranger.authorization.utils.JsonUtils;
 import org.apache.ranger.common.GUIDUtil;
 import org.apache.ranger.common.MessageEnums;
 import org.apache.ranger.common.SearchField;
@@ -28,15 +30,22 @@ import org.apache.ranger.common.SortField.SORT_ORDER;
 import org.apache.ranger.entity.XXPolicyBase;
 import org.apache.ranger.entity.XXService;
 import org.apache.ranger.plugin.model.RangerPolicy;
+import org.apache.ranger.plugin.model.RangerValiditySchedule;
 import org.apache.ranger.plugin.util.SearchFilter;
 import org.springframework.beans.factory.annotation.Autowired;
 
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 public abstract class RangerPolicyServiceBase<T extends XXPolicyBase, V extends RangerPolicy> extends
 		RangerBaseModelService<T, V> {
 
-	@Autowired
+    public static final String OPTION_POLICY_VALIDITY_SCHEDULES = "POLICY_VALIDITY_SCHEDULES";
+
+    @Autowired
 	GUIDUtil guidUtil;
-	
+
 	public RangerPolicyServiceBase() {
 		super();
 		searchFields.add(new SearchField(SearchFilter.SERVICE_TYPE, "xSvcDef.name", DATA_TYPE.STRING, SEARCH_TYPE.FULL,
@@ -90,12 +99,28 @@ public abstract class RangerPolicyServiceBase<T extends XXPolicyBase, V extends
 		xObj.setService(xService.getId());
 		xObj.setName(StringUtils.trim(vObj.getName()));
 		xObj.setPolicyType(vObj.getPolicyType() == null ? RangerPolicy.POLICY_TYPE_ACCESS : vObj.getPolicyType());
+		xObj.setPolicyPriority(vObj.getPolicyPriority() == null ? RangerPolicy.POLICY_PRIORITY_NORMAL : vObj.getPolicyPriority());
 		xObj.setDescription(vObj.getDescription());
 		xObj.setResourceSignature(vObj.getResourceSignature());
 		xObj.setIsAuditEnabled(vObj.getIsAuditEnabled());
 		xObj.setIsEnabled(vObj.getIsEnabled());
 
-		return xObj;
+		String              validitySchedules = JsonUtils.listToJson(vObj.getValiditySchedules());
+		Map<String, Object> options           = vObj.getOptions();
+
+		if (options == null) {
+			options = new HashMap<>();
+		}
+
+		if (StringUtils.isNotBlank(validitySchedules)) {
+			options.put(OPTION_POLICY_VALIDITY_SCHEDULES, validitySchedules);
+		} else {
+			options.remove(OPTION_POLICY_VALIDITY_SCHEDULES);
+		}
+
+        xObj.setOptions(JsonUtils.mapToJson(options));
+
+        return xObj;
 	}
 
 	@Override
@@ -106,10 +131,26 @@ public abstract class RangerPolicyServiceBase<T extends XXPolicyBase, V extends
 		vObj.setService(xService.getName());
 		vObj.setName(StringUtils.trim(xObj.getName()));
 		vObj.setPolicyType(xObj.getPolicyType() == null ? RangerPolicy.POLICY_TYPE_ACCESS : xObj.getPolicyType());
+		vObj.setPolicyPriority(xObj.getPolicyPriority() == null ? RangerPolicy.POLICY_PRIORITY_NORMAL : xObj.getPolicyPriority());
 		vObj.setDescription(xObj.getDescription());
 		vObj.setResourceSignature(xObj.getResourceSignature());
 		vObj.setIsEnabled(xObj.getIsEnabled());
 		vObj.setIsAuditEnabled(xObj.getIsAuditEnabled());
+
+		Map<String, Object> options = JsonUtils.jsonToObject(xObj.getOptions(), Map.class);
+
+		if (MapUtils.isNotEmpty(options)) {
+			String optionPolicyValiditySchedule = (String)options.remove(OPTION_POLICY_VALIDITY_SCHEDULES);
+
+			if (StringUtils.isNotBlank(optionPolicyValiditySchedule)) {
+				List<RangerValiditySchedule> validitySchedules = JsonUtils.jsonToRangerValiditySchedule(optionPolicyValiditySchedule);
+
+				vObj.setValiditySchedules(validitySchedules);
+			}
+		}
+
+		vObj.setOptions(options);
+
 		return vObj;
 	}
 }

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/security-admin/src/main/java/org/apache/ranger/service/RangerTagServiceBase.java
----------------------------------------------------------------------
diff --git a/security-admin/src/main/java/org/apache/ranger/service/RangerTagServiceBase.java b/security-admin/src/main/java/org/apache/ranger/service/RangerTagServiceBase.java
index e68aa92..2e2c04f 100644
--- a/security-admin/src/main/java/org/apache/ranger/service/RangerTagServiceBase.java
+++ b/security-admin/src/main/java/org/apache/ranger/service/RangerTagServiceBase.java
@@ -25,7 +25,9 @@ import java.util.List;
 import java.util.Map;
 
 import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.collections.MapUtils;
 import org.apache.commons.lang.StringUtils;
+import org.apache.ranger.authorization.utils.JsonUtils;
 import org.apache.ranger.common.GUIDUtil;
 import org.apache.ranger.common.MessageEnums;
 import org.apache.ranger.common.RangerConfigUtil;
@@ -33,6 +35,7 @@ import org.apache.ranger.entity.XXTagAttribute;
 import org.apache.ranger.entity.XXTag;
 import org.apache.ranger.entity.XXTagDef;
 import org.apache.ranger.plugin.model.RangerTag;
+import org.apache.ranger.plugin.model.RangerValiditySchedule;
 import org.apache.ranger.plugin.store.PList;
 import org.apache.ranger.plugin.util.SearchFilter;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -63,6 +66,21 @@ public abstract class RangerTagServiceBase<T extends XXTag, V extends RangerTag>
 		xObj.setGuid(guid);
 		xObj.setType(xTagDef.getId());
 		xObj.setOwner(vObj.getOwner());
+
+		String              validityPeriods = JsonUtils.listToJson(vObj.getValidityPeriods());
+		Map<String, Object> options         = vObj.getOptions();
+
+		if (options == null) {
+			options = new HashMap<>();
+		}
+
+		if (StringUtils.isNotBlank(validityPeriods)) {
+			options.put(RangerTag.OPTION_TAG_VALIDITY_PERIODS, validityPeriods);
+		} else {
+			options.remove(RangerTag.OPTION_TAG_VALIDITY_PERIODS);
+		}
+
+		xObj.setOptions(JsonUtils.mapToJson(options));
 		return xObj;
 	}
 
@@ -79,6 +97,20 @@ public abstract class RangerTagServiceBase<T extends XXTag, V extends RangerTag>
 		vObj.setType(xTagDef.getName());
 		vObj.setOwner(xObj.getOwner());
 
+		Map<String, Object> options = JsonUtils.jsonToObject(xObj.getOptions(), Map.class);
+
+		if (MapUtils.isNotEmpty(options)) {
+			String optionTagValidityPeriod = (String)options.remove(RangerTag.OPTION_TAG_VALIDITY_PERIODS);
+
+			if (StringUtils.isNotBlank(optionTagValidityPeriod)) {
+				List<RangerValiditySchedule> validityPeriods = JsonUtils.jsonToRangerValiditySchedule(optionTagValidityPeriod);
+
+				vObj.setValidityPeriods(validityPeriods);
+			}
+		}
+
+		vObj.setOptions(options);
+
 		Map<String, String> attributes = getAttributesForTag(xObj);
 		vObj.setAttributes(attributes);
 

http://git-wip-us.apache.org/repos/asf/ranger/blob/844315cd/src/main/assembly/tagsync.xml
----------------------------------------------------------------------
diff --git a/src/main/assembly/tagsync.xml b/src/main/assembly/tagsync.xml
index c929395..0788ac1 100644
--- a/src/main/assembly/tagsync.xml
+++ b/src/main/assembly/tagsync.xml
@@ -60,6 +60,7 @@
 					<include>com.fasterxml.jackson.jaxrs:jackson-jaxrs-base:jar:${atlas.jackson.version}</include>
 					<include>com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:jar:${atlas.jackson.version}</include>
 					<include>org.codehaus.jackson:jackson-core-asl:jar:${codehaus.jackson.version}</include>
+					<include>org.codehaus.jackson:jackson-mapper-asl:jar:${codehaus.jackson.version}</include>
 					<include>org.codehaus.jackson:jackson-jaxrs:jar:${codehaus.jackson.version}</include>
 					<include>org.codehaus.jettison:jettison:jar:${jettison.version}</include>
 					<include>org.scala-lang:scala-library:jar:${scala.version}</include>