RANGER-278 move validation classes under org.apache.ranger.plugin.model.validation
Signed-off-by: Madhan Neethiraj <[email protected]> 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 <[email protected]> Authored: Sat Mar 14 08:09:54 2015 -0700 Committer: Madhan Neethiraj <[email protected]> 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; + } + +}
