ranger-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mad...@apache.org
Subject [4/4] incubator-ranger git commit: RANGER-278 move validation classes under org.apache.ranger.plugin.model.validation
Date Sat, 14 Mar 2015 18:52:28 GMT
RANGER-278 move validation classes under org.apache.ranger.plugin.model.validation

Signed-off-by: Madhan Neethiraj <madhan@apache.org>


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

Branch: refs/heads/master
Commit: 7bb686873805b8157644dbd3e9e27632ef8c5590
Parents: 9b35976
Author: Alok Lal <alal@hortonworks.com>
Authored: Sat Mar 14 08:09:54 2015 -0700
Committer: Madhan Neethiraj <madhan@apache.org>
Committed: Sat Mar 14 11:41:44 2015 -0700

----------------------------------------------------------------------
 .../model/validation/RangerPolicyValidator.java | 496 ++++++++++++++++++
 .../validation/RangerServiceDefValidator.java   | 365 +++++++++++++
 .../validation/RangerServiceValidator.java      | 207 ++++++++
 .../model/validation/RangerValidator.java       | 480 +++++++++++++++++
 .../validation/RangerValidatorFactory.java      |  36 ++
 .../validation/ValidationFailureDetails.java    |  91 ++++
 .../ValidationFailureDetailsBuilder.java        |  64 +++
 .../validation/TestRangerPolicyValidator.java   | 524 +++++++++++++++++++
 .../TestRangerServiceDefValidator.java          | 355 +++++++++++++
 .../validation/TestRangerServiceValidator.java  | 240 +++++++++
 .../model/validation/TestRangerValidator.java   | 476 +++++++++++++++++
 .../model/validation/ValidationTestUtils.java   | 371 +++++++++++++
 .../ranger/rest/RangerPolicyValidator.java      | 496 ------------------
 .../ranger/rest/RangerServiceDefValidator.java  | 365 -------------
 .../ranger/rest/RangerServiceValidator.java     | 207 --------
 .../org/apache/ranger/rest/RangerValidator.java | 480 -----------------
 .../ranger/rest/RangerValidatorFactory.java     |  36 --
 .../org/apache/ranger/rest/ServiceREST.java     |   6 +-
 .../ranger/rest/ValidationFailureDetails.java   |  91 ----
 .../rest/ValidationFailureDetailsBuilder.java   |  64 ---
 .../ranger/rest/TestRangerPolicyValidator.java  | 524 -------------------
 .../rest/TestRangerServiceDefValidator.java     | 355 -------------
 .../ranger/rest/TestRangerServiceValidator.java | 240 ---------
 .../apache/ranger/rest/TestRangerValidator.java | 476 -----------------
 .../rest/TestServiceRESTForValidation.java      |   6 +-
 .../apache/ranger/rest/ValidationTestUtils.java | 371 -------------
 26 files changed, 3715 insertions(+), 3707 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/7bb68687/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
new file mode 100644
index 0000000..f5d6bff
--- /dev/null
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerPolicyValidator.java
@@ -0,0 +1,496 @@
+package org.apache.ranger.plugin.model.validation;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+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.RangerPolicy;
+import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItem;
+import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemAccess;
+import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource;
+import org.apache.ranger.plugin.model.RangerService;
+import org.apache.ranger.plugin.model.RangerServiceDef;
+import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef;
+import org.apache.ranger.plugin.store.ServiceStore;
+
+import com.google.common.collect.Sets;
+
+public class RangerPolicyValidator extends RangerValidator {
+
+	private static final Log LOG = LogFactory.getLog(RangerPolicyValidator.class);
+
+	public RangerPolicyValidator(ServiceStore store) {
+		super(store);
+	}
+
+	public void validate(RangerPolicy policy, Action action) throws Exception {
+		if(LOG.isDebugEnabled()) {
+			LOG.debug(String.format("==> RangerPolicyValidator.validate(%s, %s)", policy, action));
+		}
+
+		List<ValidationFailureDetails> failures = new ArrayList<ValidationFailureDetails>();
+		boolean valid = isValid(policy, action, failures);
+		String message = "";
+		try {
+			if (!valid) {
+				message = serializeFailures(failures);
+				throw new Exception(message);
+			}
+		} finally {
+			if(LOG.isDebugEnabled()) {
+				LOG.debug(String.format("<== RangerPolicyValidator.validate(%s, %s): %s, reason[%s]", policy, action, valid, message));
+			}
+		}
+	}
+
+	@Override
+	boolean isValid(Long id, Action action, List<ValidationFailureDetails> failures) {
+		if(LOG.isDebugEnabled()) {
+			LOG.debug(String.format("==> RangerPolicyValidator.isValid(%s, %s, %s)", id, action, failures));
+		}
+
+		boolean valid = true;
+		if (action != Action.DELETE) {
+			failures.add(new ValidationFailureDetailsBuilder()
+				.isAnInternalError()
+				.becauseOf("isValid(Long) is only supported for DELETE")
+				.build());
+			valid = false;
+		} else if (id == null) {
+			failures.add(new ValidationFailureDetailsBuilder()
+				.field("id")
+				.isMissing()
+				.build());
+			valid = false;
+		} else if (getPolicy(id) == null) {
+			failures.add(new ValidationFailureDetailsBuilder()
+				.field("id")
+				.isSemanticallyIncorrect()
+				.becauseOf("no policy found for id[" + id + "]")
+				.build());
+			valid = false;
+		}
+
+		if(LOG.isDebugEnabled()) {
+			LOG.debug(String.format("<== RangerPolicyValidator.isValid(%s, %s, %s): %s", id, action, failures, valid));
+		}
+		return valid;
+	}
+
+	boolean isValid(RangerPolicy policy, Action action, List<ValidationFailureDetails> failures) {
+		if(LOG.isDebugEnabled()) {
+			LOG.debug(String.format("==> RangerPolicyValidator.isValid(%s, %s, %s)", policy, action, failures));
+		}
+
+		if (!(action == Action.CREATE || action == Action.UPDATE)) {
+			throw new IllegalArgumentException("isValid(RangerPolicy, ...) is only supported for create/update");
+		}
+		boolean valid = true;
+		if (policy == null) {
+			String message = "policy object passed in was null";
+			LOG.debug(message);
+			failures.add(new ValidationFailureDetailsBuilder()
+				.field("policy")
+				.isMissing()
+				.becauseOf(message)
+				.build());
+			valid = false;
+		} else {
+			Long id = policy.getId();
+			if (action == Action.UPDATE) { // id is ignored for CREATE
+				if (id == null) {
+					String message = "policy id was null/empty/blank"; 
+					LOG.debug(message);
+					failures.add(new ValidationFailureDetailsBuilder()
+						.field("id")
+						.isMissing()
+						.becauseOf(message)
+						.build());
+					valid = false;
+				} else if (getPolicy(id) == null) {
+					failures.add(new ValidationFailureDetailsBuilder()
+						.field("id")
+						.isSemanticallyIncorrect()
+						.becauseOf("no policy exists with id[" + id +"]")
+						.build());
+					valid = false;
+				}
+			}
+			String policyName = policy.getName();
+			String serviceName = policy.getService();
+			if (StringUtils.isBlank(policyName)) {
+				String message = "policy name was null/empty/blank[" + policyName + "]"; 
+				LOG.debug(message);
+				failures.add(new ValidationFailureDetailsBuilder()
+					.field("name")
+					.isMissing()
+					.becauseOf(message)
+					.build());
+				valid = false;
+			} else {
+				List<RangerPolicy> policies = getPolicies(policyName, serviceName);
+				if (CollectionUtils.isNotEmpty(policies)) {
+					if (policies.size() > 1) {
+						failures.add(new ValidationFailureDetailsBuilder()
+							.isAnInternalError()
+							.becauseOf("multiple policies found with the name[" + policyName + "]")
+							.build());
+						valid = false;
+					} else if (action == Action.CREATE) { // size == 1
+						failures.add(new ValidationFailureDetailsBuilder()
+							.field("name")
+							.isSemanticallyIncorrect()
+							.becauseOf("policy already exists with name[" + policyName + "]; its id is[" + policies.iterator().next().getId() + "]")
+							.build());
+						valid = false;
+					} else if (policies.iterator().next().getId() != id) { // size == 1 && action == UPDATE
+						failures.add(new ValidationFailureDetailsBuilder()
+							.field("id/name")
+							.isSemanticallyIncorrect()
+							.becauseOf("id/name conflict: another policy already exists with name[" + policyName + "], its id is[" + policies.iterator().next().getId() + "]")
+							.build());
+						valid = false;
+					}
+				}
+			}
+			RangerService service = null;
+			if (StringUtils.isBlank(serviceName)) {
+				failures.add(new ValidationFailureDetailsBuilder()
+				.field("service")
+				.isMissing()
+				.becauseOf("service name was null/empty/blank")
+				.build());
+				valid = false;
+			} else {
+				service = getService(serviceName);
+				if (service == null) {
+					failures.add(new ValidationFailureDetailsBuilder()
+						.field("service")
+						.isMissing()
+						.becauseOf("service name was null/empty/blank")
+						.build());
+					valid = false;
+				}
+			}
+			List<RangerPolicyItem> policyItems = policy.getPolicyItems();
+			boolean isAuditEnabled = getIsAuditEnabled(policy);
+			RangerServiceDef serviceDef = null;
+			String serviceDefName = null;
+			if (CollectionUtils.isEmpty(policyItems) && !isAuditEnabled) {
+				failures.add(new ValidationFailureDetailsBuilder()
+					.field("policy items")
+					.isMissing()
+					.becauseOf("at least one policy item must be specified if audit isn't enabled")
+					.build());
+				valid = false;
+			} else if (service != null) {
+				serviceDefName = service.getType();
+				serviceDef = getServiceDef(serviceDefName);
+				if (serviceDef == null) {
+					failures.add(new ValidationFailureDetailsBuilder()
+						.field("policy service def")
+						.isAnInternalError()
+						.becauseOf("Service def of policies service does not exist")
+						.build());
+					valid = false;
+				} else {
+					valid = isValidPolicyItems(policyItems, failures, serviceDef) && valid;
+				}
+			}
+			if (serviceDef != null) {
+				Set<String> mandatoryResources = getMandatoryResourceNames(serviceDef);
+				Set<String> policyResources = getPolicyResources(policy);
+				Set<String> missingResources = Sets.difference(mandatoryResources, policyResources);
+				if (!missingResources.isEmpty()) {
+					failures.add(new ValidationFailureDetailsBuilder()
+						.field("resources")
+						.subField(missingResources.iterator().next()) // we return any one parameter!
+						.isMissing()
+						.becauseOf("required resources[" + missingResources + "] are missing")
+						.build());
+					valid = false;
+				}
+				Set<String> allResource = getAllResourceNames(serviceDef);
+				Set<String> unknownResources = Sets.difference(policyResources, allResource);
+				if (!unknownResources.isEmpty()) {
+					failures.add(new ValidationFailureDetailsBuilder()
+						.field("resources")
+						.subField(unknownResources.iterator().next()) // we return any one parameter!
+						.isSemanticallyIncorrect()
+						.becauseOf("resource[" + unknownResources + "] is not valid for service-def[" + serviceDefName + "]")
+						.build());
+					valid = false;
+				}
+				Map<String, RangerPolicyResource> resourceMap = policy.getResources();
+				valid = isValidResourceValues(resourceMap, failures, serviceDef) && valid;
+				valid = isValidResourceFlags(resourceMap, failures, serviceDef.getResources(), serviceDefName, policyName) && valid;
+			}
+		}
+		
+		if(LOG.isDebugEnabled()) {
+			LOG.debug(String.format("<== RangerPolicyValidator.isValid(%s, %s, %s): %s", policy, action, failures, valid));
+		}
+		return valid;
+	}
+	
+	boolean isValidResourceFlags(final Map<String, RangerPolicyResource> inputPolicyResources, final List<ValidationFailureDetails> failures,
+			final List<RangerResourceDef> resourceDefs, final String serviceDefName, final String policyName) {
+		if(LOG.isDebugEnabled()) {
+			LOG.debug(String.format("==> RangerPolicyValidator.isValidResourceFlags(%s, %s, %s, %s, %s)", inputPolicyResources, failures, resourceDefs, serviceDefName, policyName));
+		}
+
+		boolean valid = true;
+		if (inputPolicyResources == null) {
+			LOG.debug("isValidResourceFlags: resourceMap is null");
+		} else if (resourceDefs == null) {
+			LOG.debug("isValidResourceFlags: service Def is null");
+		} else {
+			Map<String, RangerPolicyResource> policyResources = getPolicyResourceWithLowerCaseKeys(inputPolicyResources);
+			for (RangerResourceDef resourceDef : resourceDefs) {
+				if (resourceDef == null) {
+					failures.add(new ValidationFailureDetailsBuilder()
+						.field("resource-def")
+						.isAnInternalError()
+						.becauseOf("a resource-def on resource def collection of service-def[" + serviceDefName + "] was null")
+						.build());
+					valid = false;
+				} else if (StringUtils.isBlank(resourceDef.getName())) {
+					failures.add(new ValidationFailureDetailsBuilder()
+						.field("resource-def-name")
+						.isAnInternalError()
+						.becauseOf("name of a resource-def on resource def collection of service-def[" + serviceDefName + "] was null")
+						.build());
+					valid = false;
+				} else {
+					String resourceName = resourceDef.getName().toLowerCase();
+					RangerPolicyResource policyResource = policyResources.get(resourceName);
+					if (policyResource == null) {
+						if (LOG.isDebugEnabled()) {
+							LOG.debug("a policy-resource object for resource[" + resourceName + "] on policy [" + policyName + "] was null");
+						}
+					} else {
+						boolean excludesSupported = Boolean.TRUE.equals(resourceDef.getExcludesSupported()); // could be null
+						boolean policyIsExcludes = Boolean.TRUE.equals(policyResource.getIsExcludes()); // could be null
+						if (policyIsExcludes && !excludesSupported) {
+							failures.add(new ValidationFailureDetailsBuilder()
+								.field("isExcludes")
+								.subField(resourceName)
+								.isSemanticallyIncorrect()
+								.becauseOf("isExcludes specified as [" + policyIsExcludes + "] for resource [" + resourceName + "] which doesn't support isExcludes")
+								.build());
+							valid = false;
+						}
+						boolean recursiveSupported = Boolean.TRUE.equals(resourceDef.getRecursiveSupported());
+						boolean policyIsRecursive = Boolean.TRUE.equals(policyResource.getIsRecursive());
+						if (policyIsRecursive && !recursiveSupported) {
+							failures.add(new ValidationFailureDetailsBuilder()
+								.field("isRecursive")
+								.subField(resourceName)
+								.isSemanticallyIncorrect()
+								.becauseOf("isRecursive specified as [" + policyIsRecursive + "] for resource [" + resourceName + "] which doesn't support isRecursive")
+								.build());
+							valid = false;
+						}
+					}
+				}
+			}
+		}
+
+		if(LOG.isDebugEnabled()) {
+			LOG.debug(String.format("<== RangerPolicyValidator.isValidResourceFlags(%s, %s, %s, %s, %s): %s", inputPolicyResources, failures, resourceDefs, serviceDefName, policyName, valid));
+		}
+		return valid;
+	}
+
+	boolean isValidResourceValues(Map<String, RangerPolicyResource> resourceMap, List<ValidationFailureDetails> failures, RangerServiceDef serviceDef) {
+		if(LOG.isDebugEnabled()) {
+			LOG.debug(String.format("==> RangerPolicyValidator.isValidResourceValues(%s, %s, %s)", resourceMap, failures, serviceDef));
+		}
+
+		boolean valid = true;
+		if (resourceMap == null) {
+			LOG.debug("isValidResourceValues: resourceMap is null");
+		} else if (serviceDef == null) {
+			LOG.debug("isValidResourceValues: service Def is null");
+		} else {
+			Map<String, String> validationRegExMap = getValidationRegExes(serviceDef);
+			for (Map.Entry<String, RangerPolicyResource> entry : resourceMap.entrySet()) {
+				String name = entry.getKey();
+				RangerPolicyResource policyResource = entry.getValue();
+				if (validationRegExMap.containsKey(name) && policyResource != null && CollectionUtils.isNotEmpty(policyResource.getValues())) {
+					String regEx = validationRegExMap.get(name);
+					for (String aValue : policyResource.getValues()) {
+						if (StringUtils.isBlank(aValue)) {
+							LOG.debug("resource value was blank");
+						} else if (!aValue.matches(regEx)) {
+							failures.add(new ValidationFailureDetailsBuilder()
+								.field("resource-values")
+								.subField(name)
+								.isSemanticallyIncorrect()
+								.becauseOf("resources value[" + aValue + "] does not match validation regex[" + regEx + "] defined on service-def[" + serviceDef.getName() + "]")
+								.build());
+							valid = false;
+						}
+					}
+				}
+			}
+		}
+
+		if(LOG.isDebugEnabled()) {
+			LOG.debug(String.format("<== RangerPolicyValidator.isValidResourceValues(%s, %s, %s): %s", resourceMap, failures, serviceDef, valid));
+		}
+		return valid;
+	}
+
+	boolean isValidPolicyItems(List<RangerPolicyItem> policyItems, List<ValidationFailureDetails> failures, RangerServiceDef serviceDef) {
+		if(LOG.isDebugEnabled()) {
+			LOG.debug(String.format("==> RangerPolicyValidator.isValid(%s, %s, %s)", policyItems, failures, serviceDef));
+		}
+
+		boolean valid = true;
+		if (CollectionUtils.isEmpty(policyItems)) {
+			LOG.debug("policy items collection was null/empty");
+		} else {
+			for (RangerPolicyItem policyItem : policyItems) {
+				if (policyItem == null) {
+					failures.add(new ValidationFailureDetailsBuilder()
+						.field("policy item")
+						.isMissing()
+						.becauseOf("policy items object was null")
+						.build());
+					valid = false;
+				} else {
+					// we want to go through all elements even though one may be bad so all failures are captured
+					valid = isValidPolicyItem(policyItem, failures, serviceDef) && valid;
+				}
+			}
+		}
+
+		if(LOG.isDebugEnabled()) {
+			LOG.debug(String.format("<== RangerPolicyValidator.isValid(%s, %s, %s): %s", policyItems, failures, serviceDef, valid));
+		}
+		return valid;
+	}
+
+	boolean isValidPolicyItem(RangerPolicyItem policyItem, List<ValidationFailureDetails> failures, RangerServiceDef serviceDef) {
+		if(LOG.isDebugEnabled()) {
+			LOG.debug(String.format("==> RangerPolicyValidator.isValid(%s, %s, %s)", policyItem, failures, serviceDef));
+		}
+		
+		boolean valid = true;
+		if (policyItem == null) {
+			LOG.debug("policy item was null!");
+		} else {
+			// access items collection can't be empty and should be otherwise valid
+			if (CollectionUtils.isEmpty(policyItem.getAccesses())) {
+				failures.add(new ValidationFailureDetailsBuilder()
+					.field("policy item accesses")
+					.isMissing()
+					.becauseOf("policy items accesses collection was null")
+					.build());
+				valid = false;
+			} else {
+				valid = isValidItemAccesses(policyItem.getAccesses(), failures, serviceDef) && valid;
+			}
+			// both users and user-groups collections can't be empty
+			if (CollectionUtils.isEmpty(policyItem.getUsers()) && CollectionUtils.isEmpty(policyItem.getGroups())) {
+				failures.add(new ValidationFailureDetailsBuilder()
+					.field("policy item users/user-groups")
+					.isMissing()
+					.becauseOf("both users and user-groups collections on the policy item were null/empty")
+					.build());
+				valid = false;
+			}
+		}
+
+		if(LOG.isDebugEnabled()) {
+			LOG.debug(String.format("<== RangerPolicyValidator.isValid(%s, %s, %s): %s", policyItem, failures, serviceDef, valid));
+		}
+		return valid;
+	}
+
+	boolean isValidItemAccesses(List<RangerPolicyItemAccess> accesses, List<ValidationFailureDetails> failures, RangerServiceDef serviceDef) {
+		if(LOG.isDebugEnabled()) {
+			LOG.debug(String.format("==> RangerPolicyValidator.isValid(%s, %s, %s)", accesses, failures, serviceDef));
+		}
+		
+		boolean valid = true;
+		if (CollectionUtils.isEmpty(accesses)) {
+			LOG.debug("policy item accesses collection was null/empty!");
+		} else {
+			Set<String> accessTypes = getAccessTypes(serviceDef);
+			for (RangerPolicyItemAccess access : accesses) {
+				if (access == null) {
+					failures.add(new ValidationFailureDetailsBuilder()
+						.field("policy item access")
+						.isMissing()
+						.becauseOf("policy items access object was null")
+						.build());
+					valid = false;
+				} else {
+					// we want to go through all elements even though one may be bad so all failures are captured
+					valid = isValidPolicyItemAccess(access, failures, accessTypes) && valid;
+				}
+			}
+		}
+
+		if(LOG.isDebugEnabled()) {
+			LOG.debug(String.format("<== RangerPolicyValidator.isValid(%s, %s): %s", accesses, failures, serviceDef, valid));
+		}
+		return valid;
+	}
+
+	boolean isValidPolicyItemAccess(RangerPolicyItemAccess access, List<ValidationFailureDetails> failures, Set<String> accessTypes) {
+		if(LOG.isDebugEnabled()) {
+			LOG.debug(String.format("==> RangerPolicyValidator.isValidPolicyItemAccess(%s, %s, %s)", access, failures, accessTypes));
+		}
+		
+		boolean valid = true;
+		if (CollectionUtils.isEmpty(accessTypes)) { // caller should firewall this argument!
+			LOG.debug("isValidPolicyItemAccess: accessTypes was null!");
+		} else if (access == null) {
+			LOG.debug("isValidPolicyItemAccess: policy item access was null!");
+		} else {
+			String accessType = access.getType();
+			if (StringUtils.isBlank(accessType)) {
+				failures.add(new ValidationFailureDetailsBuilder()
+					.field("policy item access type")
+					.isMissing()
+					.becauseOf("policy items access type's name was null/empty/blank")
+					.build());
+				valid = false;
+			} else if (!accessTypes.contains(accessType.toLowerCase())) {
+				String message = String.format("access type[%s] not among valid types for service[%s]", accessType, accessTypes);
+				LOG.debug(message);
+				failures.add(new ValidationFailureDetailsBuilder()
+					.field("policy item access type")
+					.isSemanticallyIncorrect()
+					.becauseOf(message)
+					.build());
+				valid = false;
+			}
+			Boolean isAllowed = access.getIsAllowed();
+			// it can be null (which is treated as allowed) but not false
+			if (isAllowed != null && isAllowed == false) {
+				String message = "access type is set to deny.  Currently deny access types are not supported.";
+				LOG.debug(message);
+				failures.add(new ValidationFailureDetailsBuilder()
+					.field("policy item access type allowed")
+					.isSemanticallyIncorrect()
+					.becauseOf(message)
+					.build());
+				valid = false;
+			}
+		}
+		
+		if(LOG.isDebugEnabled()) {
+			LOG.debug(String.format("<== RangerPolicyValidator.isValidPolicyItemAccess(%s, %s, %s): %s", access, failures, accessTypes, valid));
+		}
+		return valid;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/7bb68687/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefValidator.java
----------------------------------------------------------------------
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefValidator.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefValidator.java
new file mode 100644
index 0000000..a870e28
--- /dev/null
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefValidator.java
@@ -0,0 +1,365 @@
+package org.apache.ranger.plugin.model.validation;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+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.RangerServiceDef;
+import org.apache.ranger.plugin.model.RangerServiceDef.RangerAccessTypeDef;
+import org.apache.ranger.plugin.model.RangerServiceDef.RangerEnumDef;
+import org.apache.ranger.plugin.model.RangerServiceDef.RangerEnumElementDef;
+import org.apache.ranger.plugin.store.ServiceStore;
+
+import com.google.common.collect.Sets;
+
+public class RangerServiceDefValidator extends RangerValidator {
+
+	private static final Log LOG = LogFactory.getLog(RangerServiceDefValidator.class);
+
+	public RangerServiceDefValidator(ServiceStore store) {
+		super(store);
+	}
+
+	public void validate(final RangerServiceDef serviceDef, final Action action) throws Exception {
+		if(LOG.isDebugEnabled()) {
+			LOG.debug(String.format("==> RangerServiceDefValidator.validate(%s, %s)", serviceDef, action));
+		}
+
+		List<ValidationFailureDetails> failures = new ArrayList<ValidationFailureDetails>();
+		boolean valid = isValid(serviceDef, action, failures);
+		String message = "";
+		try {
+			if (!valid) {
+				message = serializeFailures(failures);
+				throw new Exception(message);
+			}
+		} finally {
+			if(LOG.isDebugEnabled()) {
+				LOG.debug(String.format("<== RangerServiceDefValidator.validate(%s, %s): %s, reason[%s]", serviceDef, action, valid, message));
+			}
+		}
+	}
+
+	boolean isValid(final Long id, final Action action, final List<ValidationFailureDetails> failures) {
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("==> RangerServiceDefValidator.isValid(" + id + ")");
+		}
+
+		boolean valid = true;
+		if (action != Action.DELETE) {
+			failures.add(new ValidationFailureDetailsBuilder()
+				.isAnInternalError()
+				.becauseOf("unsupported action[" + action + "]; isValid(Long) is only supported for DELETE")
+				.build());
+			valid = false;
+		} else if (id == null) {
+			failures.add(new ValidationFailureDetailsBuilder()
+				.field("id")
+				.isMissing()
+				.build());
+			valid = false;
+		} else if (getServiceDef(id) == null) {
+			failures.add(new ValidationFailureDetailsBuilder()
+				.field("id")
+				.isSemanticallyIncorrect()
+				.becauseOf("no service def found for id[" + id + "]")
+				.build());
+			valid = false;
+		}
+
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("<== RangerServiceDefValidator.isValid(" + id + "): " + valid);
+		}
+		return valid;
+	}
+	
+	boolean isValid(final RangerServiceDef serviceDef, final Action action, final List<ValidationFailureDetails> failures) {
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("==> RangerServiceDefValidator.isValid(" + serviceDef + ")");
+		}
+		
+		if (!(action == Action.CREATE || action == Action.UPDATE)) {
+			throw new IllegalArgumentException("isValid(RangerServiceDef, ...) is only supported for CREATE/UPDATE");
+		}
+		boolean valid = true;
+		if (serviceDef == null) {
+			String message = "service def object passed in was null";
+			LOG.debug(message);
+			failures.add(new ValidationFailureDetailsBuilder()
+				.field("service def")
+				.isMissing()
+				.becauseOf(message)
+				.build());
+			valid = false;
+		} else {
+			Long id = serviceDef.getId();
+			if (action == Action.UPDATE) { // id is ignored for CREATE
+				if (id == null) {
+					String message = "service def id was null/empty/blank"; 
+					LOG.debug(message);
+					failures.add(new ValidationFailureDetailsBuilder()
+						.field("id")
+						.isMissing()
+						.becauseOf(message)
+						.build());
+					valid = false;
+				} else if (getServiceDef(id) == null) {
+					failures.add(new ValidationFailureDetailsBuilder()
+						.field("id")
+						.isSemanticallyIncorrect()
+						.becauseOf("no service def exists with id[" + id +"]")
+						.build());
+					valid = false;
+				}
+			}
+			// validate the service def name
+			String name = serviceDef.getName();
+			boolean nameSpecified = StringUtils.isNotBlank(name);
+			if (!nameSpecified) {
+				String message = "service def name[" + name + "] was null/empty/blank"; 
+				LOG.debug(message);
+				failures.add(new ValidationFailureDetailsBuilder()
+					.field("name")
+					.isMissing()
+					.becauseOf(message)
+					.build());
+				valid = false;
+			} else {
+				RangerServiceDef otherServiceDef = getServiceDef(name);
+				if (otherServiceDef != null && action == Action.CREATE) {
+					failures.add(new ValidationFailureDetailsBuilder()
+						.field("name")
+						.isSemanticallyIncorrect()
+						.becauseOf("service def with the name[" + name + "] already exists")
+						.build());
+					valid = false;
+				} else if (otherServiceDef != null && otherServiceDef.getId() !=null && otherServiceDef.getId() != id) {
+					failures.add(new ValidationFailureDetailsBuilder()
+						.field("id/name")
+						.isSemanticallyIncorrect()
+						.becauseOf("id/name conflict: another service def already exists with name[" + name + "], its id is [" + otherServiceDef.getId() + "]")
+						.build());
+					valid = false;
+				}
+			}
+			if (CollectionUtils.isEmpty(serviceDef.getAccessTypes())) {
+				failures.add(new ValidationFailureDetailsBuilder()
+					.field("access types")
+					.isMissing()
+					.becauseOf("access types collection was null/empty")
+					.build());
+				valid = false;
+			} else {
+				valid = isValidAccessTypes(serviceDef.getAccessTypes(), failures) && valid;
+			}
+			if (CollectionUtils.isEmpty(serviceDef.getEnums())) {
+				LOG.debug("No enums specified on the service def.  Ok!");
+			} else {
+				valid = isValidEnums(serviceDef.getEnums(), failures) && valid;
+			}
+		}
+		
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("<== RangerServiceDefValidator.isValid(" + serviceDef + "): " + valid);
+		}
+		return valid;
+	}
+
+	boolean isValidAccessTypes(final List<RangerAccessTypeDef> accessTypeDefs, final List<ValidationFailureDetails> failures) {
+		if(LOG.isDebugEnabled()) {
+			LOG.debug(String.format("==> RangerServiceDefValidator.isValidAccessTypes(%s, %s)", accessTypeDefs, failures));
+		}
+		
+		boolean valid = true;
+		if (CollectionUtils.isEmpty(accessTypeDefs)) {
+			LOG.debug("access type def collection is empty/null");
+		} else {
+			List<RangerAccessTypeDef> defsWithImpliedGrants = new ArrayList<RangerAccessTypeDef>();
+			Set<String> accessNames = new HashSet<String>();
+			for (RangerAccessTypeDef def : accessTypeDefs) {
+				String name = def.getName();
+				// name can't be null/empty/blank
+				if (StringUtils.isBlank(name)) {
+					failures.add(new ValidationFailureDetailsBuilder()
+						.field("access type name")
+						.isMissing()
+						.becauseOf("access type name[" + name + "] is null/empty")
+						.build());
+					valid = false;
+				} else if (accessNames.contains(name.toLowerCase())) {
+					failures.add(new ValidationFailureDetailsBuilder()
+						.field("access type name")
+						.subField(name)
+						.isSemanticallyIncorrect()
+						.becauseOf("duplicate access type names in access types collection: [" + name + "]")
+						.build());
+					valid = false;
+				} else {
+					accessNames.add(name.toLowerCase()); // we have a new unique access type
+				}
+				if (CollectionUtils.isNotEmpty(def.getImpliedGrants())) {
+					defsWithImpliedGrants.add(def);
+				}
+			}
+			// validate implied grants
+			for (RangerAccessTypeDef def : defsWithImpliedGrants) {
+				Collection<String> impliedGrants = getImpliedGrants(def);
+				Set<String> unknownAccessTypes = Sets.difference(Sets.newHashSet(impliedGrants), accessNames);
+				if (!unknownAccessTypes.isEmpty()) {
+					failures.add(new ValidationFailureDetailsBuilder()
+						.field("implied grants")
+						.subField(unknownAccessTypes.iterator().next())  // we return just on item here.  Message has all unknow items
+						.isSemanticallyIncorrect()
+						.becauseOf("implied grant[" + impliedGrants + "] contains an unknown access types[" + unknownAccessTypes + "]")
+						.build());
+					valid = false;
+				}
+				// implied grant should not imply itself! 
+				String name = def.getName(); // note: this name could be null/blank/empty!
+				if (impliedGrants.contains(name)) {
+					failures.add(new ValidationFailureDetailsBuilder()
+						.field("implied grants")
+						.subField(name)
+						.isSemanticallyIncorrect()
+						.becauseOf("implied grants list [" + impliedGrants + "] for access type[" + name + "] contains itself")
+						.build());
+					valid = false;
+				}
+			}
+		}
+		
+		if(LOG.isDebugEnabled()) {
+			LOG.debug(String.format("<== RangerServiceDefValidator.isValidAccessTypes(%s, %s): %s", accessTypeDefs, failures, valid));
+		}
+		return valid;
+	}
+
+	boolean isValidEnums(List<RangerEnumDef> enumDefs, List<ValidationFailureDetails> failures) {
+		if(LOG.isDebugEnabled()) {
+			LOG.debug(String.format("==> RangerServiceDefValidator.isValidEnums(%s, %s)", enumDefs, failures));
+		}
+		
+		boolean valid = true;
+		if (CollectionUtils.isEmpty(enumDefs)) {
+			LOG.debug("enum def collection passed in was null/empty");
+		} else {
+			Set<String> enumNames = new HashSet<String>();
+			for (RangerEnumDef enumDef : enumDefs) {
+				if (enumDef == null) {
+					failures.add(new ValidationFailureDetailsBuilder()
+						.field("enum def")
+						.isMissing()
+						.becauseOf("An enum def in enums collection is null")
+						.build());
+					valid = false;
+				} else {
+					// enum-names must non-blank and be unique to a service definition
+					String enumName = enumDef.getName();
+					if (StringUtils.isBlank(enumName)) {
+						failures.add(new ValidationFailureDetailsBuilder()
+							.field("enum def name")
+							.isMissing()
+							.becauseOf("enum name [" + enumName + "] is null/empty")
+							.build());
+						valid = false;
+					} else if (enumNames.contains(enumName.toLowerCase())) {
+						failures.add(new ValidationFailureDetailsBuilder()
+							.field("enum def name")
+							.subField(enumName)
+							.isSemanticallyIncorrect()
+							.becauseOf("dumplicate enum name [" + enumName + "] found")
+							.build());
+						valid = false;
+					} else {
+						enumNames.add(enumName.toLowerCase());
+					}
+					// enum must contain at least one valid value and those values should be non-blank and distinct
+					if (CollectionUtils.isEmpty(enumDef.getElements())) {
+						failures.add(new ValidationFailureDetailsBuilder()
+							.field("enum values")
+							.subField(enumName)
+							.isMissing()
+							.becauseOf("enum [" + enumName + "] does not have any elements")
+							.build());
+						valid = false;
+					} else {
+						valid = isValidEnumElements(enumDef.getElements(), failures, enumName) && valid;
+						// default index should be valid
+						int defaultIndex = getEnumDefaultIndex(enumDef);
+						if (defaultIndex < 0 || defaultIndex >= enumDef.getElements().size()) { // max index is one less than the size of the elements list
+							failures.add(new ValidationFailureDetailsBuilder()
+								.field("enum default index")
+								.subField(enumName)
+								.isSemanticallyIncorrect()
+								.becauseOf("default index[" + defaultIndex + "] for enum [" + enumName + "] is invalid")
+								.build());
+							valid = false;
+						}
+					}
+				}
+			}
+		}
+		
+		if(LOG.isDebugEnabled()) {
+			LOG.debug(String.format("<== RangerServiceDefValidator.isValidEnums(%s, %s): %s", enumDefs, failures, valid));
+		}
+		return valid;
+	}
+
+	boolean isValidEnumElements(List<RangerEnumElementDef> enumElementsDefs, List<ValidationFailureDetails> failures, String enumName) {
+		if(LOG.isDebugEnabled()) {
+			LOG.debug(String.format("==> RangerServiceDefValidator.isValidEnums(%s, %s)", enumElementsDefs, failures));
+		}
+		
+		boolean valid = true;
+		if (CollectionUtils.isEmpty(enumElementsDefs)) {
+			LOG.debug("Enum elements list passed in was null/empty!");
+		} else {
+			// enum element names should be valid and distinct
+			Set<String> elementNames = new HashSet<String>();
+			for (RangerEnumElementDef elementDef : enumElementsDefs) {
+				if (elementDef == null) {
+					failures.add(new ValidationFailureDetailsBuilder()
+						.field("enum element")
+						.subField(enumName)
+						.isMissing()
+						.becauseOf("An enum element in enum element collection of enum [" + enumName + "] is null")
+						.build());
+					valid = false;
+				} else {
+					String elementName = elementDef.getName();
+					if (StringUtils.isBlank(elementName)) {
+						failures.add(new ValidationFailureDetailsBuilder()
+							.field("enum element name")
+							.subField(enumName)
+							.isMissing()
+							.becauseOf("Name of an element of enum [" + enumName + "] is null/empty[" + elementName + "]")
+							.build());
+						valid = false;
+					} else if (elementNames.contains(elementName.toLowerCase())) {
+						failures.add(new ValidationFailureDetailsBuilder()
+							.field("enum element name")
+							.subField(enumName)
+							.isSemanticallyIncorrect()
+							.becauseOf("dumplicate enum element name [" + elementName + "] found for enum[" + enumName + "]")
+							.build());
+						valid = false;
+					} else {
+						elementNames.add(elementName.toLowerCase());
+					}
+				}
+			}
+		}
+		
+		if(LOG.isDebugEnabled()) {
+			LOG.debug(String.format("<== RangerServiceDefValidator.isValidEnums(%s, %s): %s", enumElementsDefs, failures, valid));
+		}
+		return valid;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/7bb68687/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceValidator.java
----------------------------------------------------------------------
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceValidator.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceValidator.java
new file mode 100644
index 0000000..2019284
--- /dev/null
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceValidator.java
@@ -0,0 +1,207 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.ranger.plugin.model.RangerService;
+import org.apache.ranger.plugin.model.RangerServiceDef;
+import org.apache.ranger.plugin.store.ServiceStore;
+
+import com.google.common.collect.Sets;
+
+public class RangerServiceValidator extends RangerValidator {
+
+	private static final Log LOG = LogFactory.getLog(RangerServiceValidator.class);
+
+	public RangerServiceValidator(ServiceStore store) {
+		super(store);
+	}
+
+	public void validate(RangerService service, Action action) throws Exception {
+		if(LOG.isDebugEnabled()) {
+			LOG.debug(String.format("==> RangerServiceValidator.validate(%s, %s)", service, action));
+		}
+
+		List<ValidationFailureDetails> failures = new ArrayList<ValidationFailureDetails>();
+		boolean valid = isValid(service, action, failures);
+		String message = "";
+		try {
+			if (!valid) {
+				message = serializeFailures(failures);
+				throw new Exception(message);
+			}
+		} finally {
+			if(LOG.isDebugEnabled()) {
+				LOG.debug(String.format("<== RangerServiceValidator.validate(%s, %s): %s, reason[%s]", service, action, valid, message));
+			}
+		}
+	}
+	
+	boolean isValid(Long id, Action action, List<ValidationFailureDetails> failures) {
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("==> RangerServiceValidator.isValid(" + id + ")");
+		}
+
+		boolean valid = true;
+		if (action != Action.DELETE) {
+			failures.add(new ValidationFailureDetailsBuilder()
+				.isAnInternalError()
+				.becauseOf("unsupported action[" + action + "]; isValid(Long) is only supported for DELETE")
+				.build());
+			valid = false;
+		} else if (id == null) {
+			failures.add(new ValidationFailureDetailsBuilder()
+				.field("id")
+				.isMissing()
+				.build());
+			valid = false;
+		} else if (getService(id) == null) {
+			failures.add(new ValidationFailureDetailsBuilder()
+				.field("id")
+				.isSemanticallyIncorrect()
+				.becauseOf("no service found for id[" + id + "]")
+				.build());
+			valid = false;
+		}
+
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("<== RangerServiceValidator.isValid(" + id + "): " + valid);
+		}
+		return valid;
+	}
+	
+	boolean isValid(RangerService service, Action action, List<ValidationFailureDetails> failures) {
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("==> RangerServiceValidator.isValid(" + service + ")");
+		}
+		if (!(action == Action.CREATE || action == Action.UPDATE)) {
+			throw new IllegalArgumentException("isValid(RangerService, ...) is only supported for CREATE/UPDATE");
+		}
+		
+		boolean valid = true;
+		if (service == null) {
+			String message = "service object passed in was null";
+			LOG.debug(message);
+			failures.add(new ValidationFailureDetailsBuilder()
+				.field("service")
+				.isMissing()
+				.becauseOf(message)
+				.build());
+			valid = false;
+		} else {
+			Long id = service.getId();
+			if (action == Action.UPDATE) { // id is ignored for CREATE
+				if (id == null) {
+					String message = "service id was null/empty/blank"; 
+					LOG.debug(message);
+					failures.add(new ValidationFailureDetailsBuilder()
+						.field("id")
+						.isMissing()
+						.becauseOf(message)
+						.build());
+					valid = false;
+				} else if (getService(id) == null) {
+					failures.add(new ValidationFailureDetailsBuilder()
+						.field("id")
+						.isSemanticallyIncorrect()
+						.becauseOf("no service exists with id[" + id +"]")
+						.build());
+					valid = false;
+				}
+			}
+			String name = service.getName();
+			boolean nameSpecified = StringUtils.isNotBlank(name);
+			RangerServiceDef serviceDef = null;
+			if (!nameSpecified) {
+				String message = "service name[" + name + "] was null/empty/blank"; 
+				LOG.debug(message);
+				failures.add(new ValidationFailureDetailsBuilder()
+					.field("name")
+					.isMissing()
+					.becauseOf(message)
+					.build());
+				valid = false;
+			} else {
+				RangerService otherService = getService(name);
+				if (otherService != null && action == Action.CREATE) {
+					failures.add(new ValidationFailureDetailsBuilder()
+						.field("name")
+						.isSemanticallyIncorrect()
+						.becauseOf("service with the name[" + name + "] already exists")
+						.build());
+					valid = false;
+				} else if (otherService != null && otherService.getId() !=null && otherService.getId() != id) {
+					failures.add(new ValidationFailureDetailsBuilder()
+						.field("id/name")
+						.isSemanticallyIncorrect()
+						.becauseOf("id/name conflict: another service already exists with name[" + name + "], its id is [" + otherService.getId() + "]")
+						.build());
+					valid = false;
+				}
+			}
+			String type = service.getType();
+			boolean typeSpecified = StringUtils.isNotBlank(type);
+			if (!typeSpecified) {
+				failures.add(new ValidationFailureDetailsBuilder()
+					.field("type")
+					.isMissing()
+					.becauseOf("service def [" + type + "] was null/empty/blank")
+					.build());
+				valid = false;
+			} else {
+				serviceDef = getServiceDef(type);
+				if (serviceDef == null) {
+					failures.add(new ValidationFailureDetailsBuilder()
+						.field("type")
+						.isSemanticallyIncorrect()
+						.becauseOf("service def named[" + type + "] not found")
+						.build());
+					valid = false;
+				}
+			}
+			if (nameSpecified && serviceDef != null) {
+				// check if required parameters were specified
+				Set<String> reqiredParameters = getRequiredParameters(serviceDef);
+				Set<String> inputParameters = getServiceConfigParameters(service);
+				Set<String> missingParameters = Sets.difference(reqiredParameters, inputParameters);
+				if (!missingParameters.isEmpty()) {
+					failures.add(new ValidationFailureDetailsBuilder()
+						.field("configuration")
+						.subField(missingParameters.iterator().next()) // we return any one parameter!
+						.isMissing()
+						.becauseOf("required configuration parameter is missing; missing parameters: " + missingParameters)
+						.build());
+					valid = false;
+				}
+			}
+		}
+		
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("<== RangerServiceValidator.isValid(" + service + "): " + valid);
+		}
+		return valid;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/7bb68687/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
new file mode 100644
index 0000000..7bf744e
--- /dev/null
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerValidator.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 java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+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.RangerPolicy;
+import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource;
+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.RangerEnumDef;
+import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef;
+import org.apache.ranger.plugin.model.RangerServiceDef.RangerServiceConfigDef;
+import org.apache.ranger.plugin.store.ServiceStore;
+import org.apache.ranger.plugin.util.SearchFilter;
+
+public abstract class RangerValidator {
+	
+	private static final Log LOG = LogFactory.getLog(RangerValidator.class);
+
+	ServiceStore _store;
+
+	public enum Action {
+		CREATE, UPDATE, DELETE;
+	};
+	
+	protected RangerValidator(ServiceStore store) {
+		if (store == null) {
+			throw new IllegalArgumentException("ServiceValidator(): store is null!");
+		}
+		_store = store;
+	}
+
+	public void validate(Long id, Action action) throws Exception {
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("==> RangerValidator.validate(" + id + ")");
+		}
+
+		List<ValidationFailureDetails> failures = new ArrayList<ValidationFailureDetails>();
+		if (isValid(id, action, failures)) {
+			if(LOG.isDebugEnabled()) {
+				LOG.debug("<== RangerValidator.validate(" + id + "): valid");
+			}
+		} else {
+			String message = serializeFailures(failures);
+			LOG.debug("<== RangerValidator.validate(" + id + "): invalid, reason[" + message + "]");
+			throw new Exception(message);
+		}
+	}
+	
+	/**
+	 * This method is expected to be overridden by sub-classes.  Default implementation provided to not burden implementers from having to implement methods that they know would never be called. 
+	 * @param id
+	 * @param action
+	 * @param failures
+	 * @return
+	 */
+	boolean isValid(Long id, Action action, List<ValidationFailureDetails> failures) {
+		failures.add(new ValidationFailureDetailsBuilder()
+				.isAnInternalError()
+				.becauseOf("unimplemented method called")
+				.build());
+		return false;
+	}
+
+	String serializeFailures(List<ValidationFailureDetails> failures) {
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("==> RangerValidator.getFailureMessage()");
+		}
+
+		String message = null;
+		if (CollectionUtils.isEmpty(failures)) {
+			LOG.warn("serializeFailures: called while list of failures is null/empty!");
+		} else {
+			StringBuilder builder = new StringBuilder();
+			for (ValidationFailureDetails aFailure : failures) {
+				builder.append(aFailure.toString());
+				builder.append(";");
+			}
+			message = builder.toString();
+		}
+
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("<== RangerValidator.serializeFailures(): " + message);
+		}
+		return message;
+	}
+
+	Set<String> getServiceConfigParameters(RangerService service) {
+		if (service == null || service.getConfigs() == null) {
+			return new HashSet<String>();
+		} else {
+			return service.getConfigs().keySet();
+		}
+	}
+
+	Set<String> getRequiredParameters(RangerServiceDef serviceDef) {
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("==> RangerValidator.getRequiredParameters(" + serviceDef + ")");
+		}
+
+		Set<String> result;
+		if (serviceDef == null) {
+			result = Collections.emptySet();
+		} else {
+			List<RangerServiceConfigDef> configs = serviceDef.getConfigs();
+			if (CollectionUtils.isEmpty(configs)) {
+				result = Collections.emptySet();
+			} else {
+				result = new HashSet<String>(configs.size()); // at worse all of the config items are required!
+				for (RangerServiceConfigDef configDef : configs) {
+					if (configDef.getMandatory()) {
+						result.add(configDef.getName());
+					}
+				}
+			}
+		}
+
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("<== RangerValidator.getRequiredParameters(" + serviceDef + "): " + result);
+		}
+		return result;
+	}
+
+	RangerServiceDef getServiceDef(Long id) {
+
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("==> RangerValidator.getServiceDef(" + id + ")");
+		}
+		RangerServiceDef result = null;
+		try {
+			result = _store.getServiceDef(id);
+		} catch (Exception e) {
+			LOG.debug("Encountred exception while retrieving service def from service store!", e);
+		}
+		
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("<== RangerValidator.getServiceDef(" + id + "): " + result);
+		}
+		return result;
+	}
+
+	RangerServiceDef getServiceDef(String type) {
+		
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("==> RangerValidator.getServiceDef(" + type + ")");
+		}
+		RangerServiceDef result = null;
+		try {
+			result = _store.getServiceDefByName(type);
+		} catch (Exception e) {
+			LOG.debug("Encountred exception while retrieving service definition from service store!", e);
+		}
+		
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("<== RangerValidator.getServiceDef(" + type + "): " + result);
+		}
+		return result;
+	}
+
+	RangerService getService(Long id) {
+		
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("==> RangerValidator.getService(" + id + ")");
+		}
+		RangerService result = null;
+		try {
+			result = _store.getService(id);
+		} catch (Exception e) {
+			LOG.debug("Encountred exception while retrieving service from service store!", e);
+		}
+		
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("<== RangerValidator.getService(" + id + "): " + result);
+		}
+		return result;
+	}
+
+	RangerService getService(String name) {
+		
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("==> RangerValidator.getService(" + name + ")");
+		}
+		RangerService result = null;
+		try {
+			result = _store.getServiceByName(name);
+		} catch (Exception e) {
+			LOG.debug("Encountred exception while retrieving service from service store!", e);
+		}
+		
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("<== RangerValidator.getService(" + name + "): " + result);
+		}
+		return result;
+	}
+
+	RangerPolicy getPolicy(Long id) {
+		
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("==> RangerValidator.getPolicy(" + id + ")");
+		}
+		RangerPolicy result = null;
+		try {
+			result = _store.getPolicy(id);
+		} catch (Exception e) {
+			LOG.debug("Encountred exception while retrieving policy from service store!", e);
+		}
+		
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("<== RangerValidator.getPolicy(" + id + "): " + result);
+		}
+		return result;
+	}
+
+	List<RangerPolicy> getPolicies(final String policyName, final String serviceName) {
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("==> RangerValidator.getPolicies(" + policyName + ", " + serviceName + ")");
+		}
+
+		List<RangerPolicy> policies = null;
+		try {
+			SearchFilter filter = new SearchFilter();
+			filter.setParam(SearchFilter.POLICY_NAME, policyName);
+			filter.setParam(SearchFilter.SERVICE_NAME, serviceName);
+			
+			policies = _store.getPolicies(filter);
+		} catch (Exception e) {
+			LOG.debug("Encountred exception while retrieving service from service store!", e);
+		}
+		
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("<== RangerValidator.getPolicies(" + policyName + ", " + serviceName + "): " + policies);
+		}
+		return policies;
+	}
+
+	Set<String> getAccessTypes(RangerServiceDef serviceDef) {
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("==> RangerValidator.getAccessTypes(" + serviceDef + ")");
+		}
+
+		Set<String> accessTypes = new HashSet<String>();
+		if (serviceDef == null) {
+			LOG.warn("serviceDef passed in was null!");
+		} else if (CollectionUtils.isEmpty(serviceDef.getAccessTypes())) {
+			LOG.warn("AccessTypeDef collection on serviceDef was null!");
+		} else {
+			for (RangerAccessTypeDef accessTypeDef : serviceDef.getAccessTypes()) {
+				if (accessTypeDef == null) {
+					LOG.warn("Access type def was null!");
+				} else {
+					String accessType = accessTypeDef.getName();
+					if (StringUtils.isBlank(accessType)) {
+						LOG.warn("Access type def name was null/empty/blank!");
+					} else {
+						accessTypes.add(accessType.toLowerCase());
+					}
+				}
+			}
+		}
+
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("<== RangerValidator.getAccessTypes(" + serviceDef + "): " + accessTypes);
+		}
+		return accessTypes;
+	}
+	
+	/**
+	 * This function exists to encapsulates the current behavior of code which treats and unspecified audit preference to mean audit is enabled.
+	 * @param policy
+	 * @return
+	 */
+	boolean getIsAuditEnabled(RangerPolicy policy) {
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("<== RangerValidator.getIsAuditEnabled(" + policy + ")");
+		}
+
+		boolean isEnabled = false;
+		if (policy == null) {
+			LOG.warn("policy was null!");
+		} else if (policy.getIsAuditEnabled() == null) {
+			isEnabled = true;
+		} else {
+			isEnabled = policy.getIsAuditEnabled();
+		}
+
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("<== RangerValidator.getIsAuditEnabled(" + policy + "): " + isEnabled);
+		}
+		return isEnabled;
+	}
+	
+	/**
+	 * Returns names of resource types set to lower-case to allow for case-insensitive comparison. 
+	 * @param serviceDef
+	 * @return
+	 */
+	Set<String> getMandatoryResourceNames(RangerServiceDef serviceDef) {
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("==> RangerValidator.getMandatoryResourceNames(" + serviceDef + ")");
+		}
+
+		Set<String> resourceNames = new HashSet<String>();
+		if (serviceDef == null) {
+			LOG.warn("serviceDef passed in was null!");
+		} else if (CollectionUtils.isEmpty(serviceDef.getResources())) {
+			LOG.warn("ResourceDef collection on serviceDef was null!");
+		} else {
+			for (RangerResourceDef resourceTypeDef : serviceDef.getResources()) {
+				if (resourceTypeDef == null) {
+					LOG.warn("resource type def was null!");
+				} else {
+					Boolean mandatory = resourceTypeDef.getMandatory();
+					if (mandatory != null && mandatory == true) {
+						String resourceName = resourceTypeDef.getName();
+						if (StringUtils.isBlank(resourceName)) {
+							LOG.warn("Resource def name was null/empty/blank!");
+						} else {
+							resourceNames.add(resourceName.toLowerCase());
+						}
+					}
+				}
+			}
+		}
+
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("<== RangerValidator.getMandatoryResourceNames(" + serviceDef + "): " + resourceNames);
+		}
+		return resourceNames;
+	}
+
+	Set<String> getAllResourceNames(RangerServiceDef serviceDef) {
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("==> RangerValidator.getAllResourceNames(" + serviceDef + ")");
+		}
+
+		Set<String> resourceNames = new HashSet<String>();
+		if (serviceDef == null) {
+			LOG.warn("serviceDef passed in was null!");
+		} else if (CollectionUtils.isEmpty(serviceDef.getResources())) {
+			LOG.warn("ResourceDef collection on serviceDef was null!");
+		} else {
+			for (RangerResourceDef resourceTypeDef : serviceDef.getResources()) {
+				if (resourceTypeDef == null) {
+					LOG.warn("resource type def was null!");
+				} else {
+					String resourceName = resourceTypeDef.getName();
+					if (StringUtils.isBlank(resourceName)) {
+						LOG.warn("Resource def name was null/empty/blank!");
+					} else {
+						resourceNames.add(resourceName.toLowerCase());
+					}
+				}
+			}
+		}
+
+		if(LOG.isDebugEnabled()) {
+			LOG.debug("<== RangerValidator.getAllResourceNames(" + serviceDef + "): " + resourceNames);
+		}
+		return resourceNames;
+	}
+	
+	/**
+	 * Returns the resource-types defined on the policy converted to lowe-case
+	 * @param policy
+	 * @return
+	 */
+	Set<String> getPolicyResources(RangerPolicy policy) {
+		if (policy == null || policy.getResources() == null || policy.getResources().isEmpty()) {
+			return new HashSet<String>();
+		} else {
+			Set<String> result = new HashSet<String>();
+			for (String name : policy.getResources().keySet()) {
+				result.add(name.toLowerCase());
+			}
+			return result;
+		}
+	}
+
+	Map<String, String> getValidationRegExes(RangerServiceDef serviceDef) {
+		if (serviceDef == null || CollectionUtils.isEmpty(serviceDef.getResources())) {
+			return new HashMap<String, String>();
+		} else {
+			Map<String, String> result = new HashMap<String, String>();
+			for (RangerResourceDef resourceDef : serviceDef.getResources()) {
+				if (resourceDef == null) {
+					LOG.warn("A resource def in resource def collection is null");
+				} else {
+					String name = resourceDef.getName();
+					String regEx = resourceDef.getValidationRegEx();
+					if (StringUtils.isBlank(name)) {
+						LOG.warn("resource name is null/empty/blank");
+					} else if (StringUtils.isBlank(regEx)) {
+						LOG.debug("validation regex is null/empty/blank");
+					} else {
+						result.put(name, regEx);
+					}
+				}
+			}
+			return result;
+		}
+	}
+	
+	int getEnumDefaultIndex(RangerEnumDef enumDef) {
+		int index;
+		if (enumDef == null) {
+			index = -1;
+		} else if (enumDef.getDefaultIndex() == null) {
+			index = 0;
+		} else {
+			index = enumDef.getDefaultIndex();
+		}
+		return index;
+	}
+
+	Collection<String> getImpliedGrants(RangerAccessTypeDef def) {
+		if (def == null) {
+			return null;
+		} else if (CollectionUtils.isEmpty(def.getImpliedGrants())) {
+			return new ArrayList<String>();
+		} else {
+			List<String> result = new ArrayList<String>(def.getImpliedGrants().size());
+			for (String name : def.getImpliedGrants()) {
+				if (StringUtils.isBlank(name)) {
+					result.add(name); // could be null!
+				} else {
+					result.add(name.toLowerCase());
+				}
+			}
+			return result;
+		}
+	}
+
+	/**
+	 * Returns a copy of the policy resource map where all keys (resource-names) are lowercase
+	 * @param input
+	 * @return
+	 */
+	Map<String, RangerPolicyResource> getPolicyResourceWithLowerCaseKeys(Map<String, RangerPolicyResource> input) {
+		if (input == null) {
+			return null;
+		}
+		Map<String, RangerPolicyResource> output = new HashMap<String, RangerPolicyResource>(input.size());
+		for (Map.Entry<String, RangerPolicyResource> entry : input.entrySet()) {
+			output.put(entry.getKey().toLowerCase(), entry.getValue());
+		}
+		return output;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/7bb68687/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerValidatorFactory.java
----------------------------------------------------------------------
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerValidatorFactory.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerValidatorFactory.java
new file mode 100644
index 0000000..f72e8df
--- /dev/null
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerValidatorFactory.java
@@ -0,0 +1,36 @@
+/*
+ * 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.ranger.plugin.store.ServiceStore;
+
+public class RangerValidatorFactory {
+	public RangerServiceValidator getServiceValidator(ServiceStore store) {
+		return new RangerServiceValidator(store);
+	}
+
+	public RangerPolicyValidator getPolicyValidator(ServiceStore store) {
+		return new RangerPolicyValidator(store);
+	}
+
+	public RangerServiceDefValidator getServiceDefValidator(ServiceStore store) {
+		return new RangerServiceDefValidator(store);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/7bb68687/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/ValidationFailureDetails.java
----------------------------------------------------------------------
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/ValidationFailureDetails.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/ValidationFailureDetails.java
new file mode 100644
index 0000000..015203a
--- /dev/null
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/ValidationFailureDetails.java
@@ -0,0 +1,91 @@
+/*
+ * 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 java.util.Objects;
+
+public class ValidationFailureDetails {
+
+	final String _fieldName;
+	final String _subFieldName;
+	final boolean _missing;
+	final boolean _semanticError;
+	final boolean _internalError;
+	final String _reason;
+	
+	public ValidationFailureDetails(String fieldName, String subFieldName, boolean missing, boolean semanticError, boolean internalError, String reason) {
+		_missing = missing;
+		_semanticError = semanticError;
+		_internalError = internalError;
+		_fieldName = fieldName;
+		_subFieldName = subFieldName;
+		_reason = reason;
+	}
+
+	public String getFieldName() {
+		return _fieldName;
+	}
+
+	public boolean isMissingRequiredValue() {
+		return _missing;
+	}
+
+	public boolean isSemanticallyIncorrect() {
+		return _semanticError;
+	}
+	
+	String getType() {
+		if (_missing) return "missing";
+		if (_semanticError) return "semantically incorrect";
+		if (_internalError) return "internal error";
+		return "";
+	}
+
+	public String getSubFieldName() {
+		return _subFieldName;
+	}
+	
+	@Override
+	public String toString() {
+		return String.format("Field[%s]%s is %s: reason[%s]", 
+				_fieldName, 
+				_subFieldName == null ? "" : ", subField[" + _subFieldName + "]",
+				getType(), _reason);
+	}
+	
+	@Override
+	public int hashCode() {
+		return Objects.hash(_fieldName, _subFieldName, _missing, _semanticError, _internalError, _reason);
+	}
+	
+	@Override
+	public boolean equals(Object obj) {
+		if (obj == null || !(obj instanceof ValidationFailureDetails)) {
+			return false;
+		}
+		ValidationFailureDetails that = (ValidationFailureDetails)obj;
+		return Objects.equals(_fieldName, that._fieldName) && 
+				Objects.equals(_subFieldName, that._subFieldName) && 
+				Objects.equals(_reason, that._reason) && 
+				_internalError == that._internalError &&
+				_missing == that._missing &&
+				_semanticError == that._semanticError;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/7bb68687/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/ValidationFailureDetailsBuilder.java
----------------------------------------------------------------------
diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/ValidationFailureDetailsBuilder.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/ValidationFailureDetailsBuilder.java
new file mode 100644
index 0000000..3a57341
--- /dev/null
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/ValidationFailureDetailsBuilder.java
@@ -0,0 +1,64 @@
+/*
+ * 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;
+
+public class ValidationFailureDetailsBuilder {
+	private String _fieldName;
+	private boolean _missing;
+	private boolean _semanticError;
+	private String _reason;
+	private String _subFieldName;
+	private boolean _internalError;
+	
+	ValidationFailureDetailsBuilder becauseOf(String aReason) {
+		_reason = aReason;
+		return this;
+	}
+	
+	ValidationFailureDetailsBuilder isMissing() {
+		_missing = true;
+		return this;
+	}
+	
+	ValidationFailureDetailsBuilder isSemanticallyIncorrect() {
+		_semanticError = true;
+		return this;
+	}
+	
+	ValidationFailureDetailsBuilder field(String fieldName) {
+		_fieldName = fieldName;
+		return this;
+	}
+	
+	ValidationFailureDetails build() {
+		return new ValidationFailureDetails(_fieldName, _subFieldName, _missing, _semanticError, _internalError, _reason);
+	}
+
+	ValidationFailureDetailsBuilder subField(String missingParameter) {
+		_subFieldName = missingParameter;
+		return this;
+	}
+
+	ValidationFailureDetailsBuilder isAnInternalError() {
+		_internalError = true;
+		return this;
+	}
+
+}


Mime
View raw message