Return-Path: X-Original-To: apmail-ranger-commits-archive@www.apache.org Delivered-To: apmail-ranger-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 9721118B36 for ; Thu, 30 Jul 2015 21:39:38 +0000 (UTC) Received: (qmail 36836 invoked by uid 500); 30 Jul 2015 21:39:38 -0000 Delivered-To: apmail-ranger-commits-archive@ranger.apache.org Received: (qmail 36814 invoked by uid 500); 30 Jul 2015 21:39:38 -0000 Mailing-List: contact commits-help@ranger.incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@ranger.incubator.apache.org Delivered-To: mailing list commits@ranger.incubator.apache.org Received: (qmail 36805 invoked by uid 99); 30 Jul 2015 21:39:38 -0000 Received: from Unknown (HELO spamd4-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 30 Jul 2015 21:39:38 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd4-us-west.apache.org (ASF Mail Server at spamd4-us-west.apache.org) with ESMTP id 02DD4C071B for ; Thu, 30 Jul 2015 21:39:38 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd4-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: 1.771 X-Spam-Level: * X-Spam-Status: No, score=1.771 tagged_above=-999 required=6.31 tests=[KAM_ASCII_DIVIDERS=0.8, KAM_LAZY_DOMAIN_SECURITY=1, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, T_RP_MATCHES_RCVD=-0.01, URIBL_BLOCKED=0.001] autolearn=disabled Received: from mx1-us-west.apache.org ([10.40.0.8]) by localhost (spamd4-us-west.apache.org [10.40.0.11]) (amavisd-new, port 10024) with ESMTP id XB3KV9rqr8FX for ; Thu, 30 Jul 2015 21:39:30 +0000 (UTC) Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx1-us-west.apache.org (ASF Mail Server at mx1-us-west.apache.org) with SMTP id 80D5F2142D for ; Thu, 30 Jul 2015 21:39:30 +0000 (UTC) Received: (qmail 32833 invoked by uid 99); 30 Jul 2015 21:39:30 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 30 Jul 2015 21:39:30 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 44117E7155; Thu, 30 Jul 2015 21:39:30 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: madhan@apache.org To: commits@ranger.incubator.apache.org Date: Thu, 30 Jul 2015 21:39:32 -0000 Message-Id: In-Reply-To: References: X-Mailer: ASF-Git Admin Mailer Subject: [3/7] incubator-ranger git commit: RANGER-560 Policy validation: user friendly error messages about validation failures RANGER-560 Policy validation: user friendly error messages about validation failures Signed-off-by: Madhan Neethiraj Project: http://git-wip-us.apache.org/repos/asf/incubator-ranger/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-ranger/commit/e35e1c19 Tree: http://git-wip-us.apache.org/repos/asf/incubator-ranger/tree/e35e1c19 Diff: http://git-wip-us.apache.org/repos/asf/incubator-ranger/diff/e35e1c19 Branch: refs/heads/tag-policy Commit: e35e1c1916bc5ed9118d76469e643a0c9c41f1fb Parents: 21fe732 Author: Alok Lal Authored: Thu Jun 18 15:30:51 2015 -0700 Committer: Madhan Neethiraj Committed: Tue Jul 28 22:27:45 2015 -0700 ---------------------------------------------------------------------- .../model/validation/RangerPolicyValidator.java | 265 +++++++++++++++---- .../model/validation/RangerValidator.java | 42 ++- .../validation/ValidationFailureDetails.java | 38 ++- .../ValidationFailureDetailsBuilder.java | 19 +- .../validation/TestRangerPolicyValidator.java | 22 +- .../TestValidationFailureDetails.java | 39 +++ 6 files changed, 345 insertions(+), 80 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/e35e1c19/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerPolicyValidator.java ---------------------------------------------------------------------- diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerPolicyValidator.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerPolicyValidator.java index 84f750d..8817049 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerPolicyValidator.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerPolicyValidator.java @@ -19,11 +19,7 @@ package org.apache.ranger.plugin.model.validation; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; @@ -75,15 +71,18 @@ public class RangerPolicyValidator extends RangerValidator { boolean valid = true; if (action != Action.DELETE) { - failures.add(new ValidationFailureDetailsBuilder() + failures.add(new RangerPolicyValidationErrorBuilder() .isAnInternalError() - .becauseOf("isValid(Long) is only supported for DELETE") + .becauseOf("method signature isValid(Long) is only supported for DELETE") + .errorCode(ErrorCode.InternalError_InvalidMethodInvocation) .build()); valid = false; } else if (id == null) { - failures.add(new ValidationFailureDetailsBuilder() + failures.add(new RangerPolicyValidationErrorBuilder() + .becauseOf("policy id was null/missing") .field("id") .isMissing() + .errorCode(ErrorCode.Missing_PolicyId_Delete) .build()); valid = false; } else if (getPolicy(id) == null) { @@ -110,10 +109,11 @@ public class RangerPolicyValidator extends RangerValidator { if (policy == null) { String message = "policy object passed in was null"; LOG.debug(message); - failures.add(new ValidationFailureDetailsBuilder() + failures.add(new RangerPolicyValidationErrorBuilder() .field("policy") .isMissing() .becauseOf(message) + .errorCode(ErrorCode.Missing_PolicyObject) .build()); valid = false; } else { @@ -122,17 +122,19 @@ public class RangerPolicyValidator extends RangerValidator { if (id == null) { String message = "policy id was null/empty/blank"; LOG.debug(message); - failures.add(new ValidationFailureDetailsBuilder() + failures.add(new RangerPolicyValidationErrorBuilder() .field("id") .isMissing() .becauseOf(message) + .errorCode(ErrorCode.Missing_PolicyId_Update) .build()); valid = false; } else if (getPolicy(id) == null) { - failures.add(new ValidationFailureDetailsBuilder() + failures.add(new RangerPolicyValidationErrorBuilder() .field("id") .isSemanticallyIncorrect() - .becauseOf("no policy exists with id[" + id +"]") + .becauseOf("Invalid policy id provided for update: no policy found for id[" + id + "]") + .errorCode(ErrorCode.Invalid_PolicyId) .build()); valid = false; } @@ -140,34 +142,39 @@ public class RangerPolicyValidator extends RangerValidator { String policyName = policy.getName(); String serviceName = policy.getService(); if (StringUtils.isBlank(policyName)) { - String message = "policy name was null/empty/blank[" + policyName + "]"; + String message = "policy name was null/empty/blank[" + policyName + "]"; LOG.debug(message); - failures.add(new ValidationFailureDetailsBuilder() + failures.add(new RangerPolicyValidationErrorBuilder() .field("name") .isMissing() .becauseOf(message) + .errorCode(ErrorCode.Missing_PolicyName) .build()); valid = false; } else { List policies = getPolicies(serviceName, policyName); if (CollectionUtils.isNotEmpty(policies)) { if (policies.size() > 1) { - failures.add(new ValidationFailureDetailsBuilder() + failures.add(new RangerPolicyValidationErrorBuilder() + .field("name") .isAnInternalError() .becauseOf("multiple policies found with the name[" + policyName + "]") + .errorCode(ErrorCode.InternalError_Data_MultiplePoliciesSameName) .build()); valid = false; } else if (action == Action.CREATE) { // size == 1 - failures.add(new ValidationFailureDetailsBuilder() - .field("name") + failures.add(new RangerPolicyValidationErrorBuilder() + .field("policy name") .isSemanticallyIncorrect() - .becauseOf("policy already exists with name[" + policyName + "]; its id is[" + policies.iterator().next().getId() + "]") + .becauseOf("A policy already exists with name[" + policyName + "] for service[" + serviceName + "]; its id is[" + policies.iterator().next().getId() + "]") + .errorCode(ErrorCode.Duplicate_PolicyName_Create) .build()); valid = false; } else if (!policies.iterator().next().getId().equals(id)) { // size == 1 && action == UPDATE - failures.add(new ValidationFailureDetailsBuilder() + failures.add(new RangerPolicyValidationErrorBuilder() .field("id/name") .isSemanticallyIncorrect() + .errorCode(ErrorCode.Duplicate_PolicyName_Update) .becauseOf("id/name conflict: another policy already exists with name[" + policyName + "], its id is[" + policies.iterator().next().getId() + "]") .build()); valid = false; @@ -177,19 +184,21 @@ public class RangerPolicyValidator extends RangerValidator { RangerService service = null; boolean serviceNameValid = false; if (StringUtils.isBlank(serviceName)) { - failures.add(new ValidationFailureDetailsBuilder() - .field("service") - .isMissing() - .becauseOf("service name was null/empty/blank") - .build()); + failures.add(new RangerPolicyValidationErrorBuilder() + .field("service name") + .isMissing() + .errorCode(ErrorCode.Missing_ServiceName) + .becauseOf("service name was null/empty/blank") + .build()); valid = false; } else { service = getService(serviceName); if (service == null) { - failures.add(new ValidationFailureDetailsBuilder() - .field("service") + failures.add(new RangerPolicyValidationErrorBuilder() + .field("service name") .isSemanticallyIncorrect() - .becauseOf("service does not exist") + .becauseOf("no service found with name[" + serviceName + "]") + .errorCode(ErrorCode.Invalid_ServiceName) .build()); valid = false; } else { @@ -201,20 +210,24 @@ public class RangerPolicyValidator extends RangerValidator { RangerServiceDef serviceDef = null; String serviceDefName = null; if (CollectionUtils.isEmpty(policyItems) && !isAuditEnabled) { - failures.add(new ValidationFailureDetailsBuilder() + failures.add(new RangerPolicyValidationErrorBuilder() .field("policy items") .isMissing() .becauseOf("at least one policy item must be specified if audit isn't enabled") + .errorCode(ErrorCode.Missing_PolicyItems) .build()); valid = false; } else if (service != null) { serviceDefName = service.getType(); serviceDef = getServiceDef(serviceDefName); if (serviceDef == null) { - failures.add(new ValidationFailureDetailsBuilder() + String message = String.format("Service def[%s] of policy's service[%s] does not exist!", serviceDefName, serviceName); + LOG.debug(message); + failures.add(new RangerPolicyValidationErrorBuilder() .field("policy service def") .isAnInternalError() - .becauseOf("Service def of policies service does not exist") + .becauseOf(message) + .errorCode(ErrorCode.InternalError_Data_MissingServiceDef) .build()); valid = false; } else { @@ -273,10 +286,13 @@ public class RangerPolicyValidator extends RangerValidator { RangerPolicy matchedPolicy = policies.iterator().next(); // there shouldn't be a matching policy for create. During update only match should be to itself if (action == Action.CREATE || (action == Action.UPDATE && (policies.size() > 1 || !matchedPolicy.getId().equals(policy.getId())))) { - failures.add(new ValidationFailureDetailsBuilder() + String message = String.format("another policy[%s] with matching resources[%s] exists for service[%s]!", + matchedPolicy.getName(), matchedPolicy.getResources(), policy.getService()); + failures.add(new RangerPolicyValidationErrorBuilder() .field("resources") .isSemanticallyIncorrect() - .becauseOf("found another policy[" + matchedPolicy.getName() + "] with matching resources[" + matchedPolicy.getResources() + "]!") + .becauseOf(message) + .errorCode(ErrorCode.Duplicate_PolicyResource) .build()); valid = false; } @@ -312,12 +328,14 @@ public class RangerPolicyValidator extends RangerValidator { Set> candidateHierarchies = filterHierarchies_hierarchyHasAllPolicyResources(policyResources, hierarchies, defHelper); if (candidateHierarchies.isEmpty()) { // let's build a helpful message for user - failures.add(new ValidationFailureDetailsBuilder() - .field("resources") + String message = String.format("policy resources %s are not compatible with any resource hierarchy for service def[%s]! Valid hierarchies are: %s", + policyResources.toString(), serviceDef.getName(), toStringHierarchies_all(hierarchies, defHelper)); + failures.add(new RangerPolicyValidationErrorBuilder() + .field("policy resources") .subField("incompatible") .isSemanticallyIncorrect() - .becauseOf(String.format("policy resources [%s] were incompatible with all the hierarchies for this service defs! Valid hierarchies are: %s", - policyResources.toString(), toStringHierarchies_all(hierarchies, defHelper))) + .becauseOf(message) + .errorCode(ErrorCode.Invalid_PolicyResource_NoCompatibleHierarchy) .build()); valid = false; } else { @@ -331,11 +349,12 @@ public class RangerPolicyValidator extends RangerValidator { */ Set> validHierarchies = filterHierarchies_mandatoryResourcesSpecifiedInPolicy(policyResources, candidateHierarchies, defHelper); if (validHierarchies.isEmpty()) { - failures.add(new ValidationFailureDetailsBuilder() - .field("resources") + failures.add(new RangerPolicyValidationErrorBuilder() + .field("policy resources") .subField("missing mandatory") .isSemanticallyIncorrect() - .becauseOf("policy is missing required resources. Mandatory fields of potential hierarchies are: " + toStringHierarchies_mandatory(candidateHierarchies, defHelper)) + .errorCode(ErrorCode.Invalid_PolicyResource_MissingMandatory) + .becauseOf("policy is missing required resources. Mandatory resources of potential hierarchies are: " + toStringHierarchies_mandatory(candidateHierarchies, defHelper)) .build()); valid = false; } else { @@ -438,16 +457,18 @@ public class RangerPolicyValidator extends RangerValidator { Map policyResources = getPolicyResourceWithLowerCaseKeys(inputPolicyResources); for (RangerResourceDef resourceDef : resourceDefs) { if (resourceDef == null) { - failures.add(new ValidationFailureDetailsBuilder() + failures.add(new RangerPolicyValidationErrorBuilder() .field("resource-def") .isAnInternalError() + .errorCode(ErrorCode.InternalError_Data_NullResourceDef) .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() + failures.add(new RangerPolicyValidationErrorBuilder() .field("resource-def-name") .isAnInternalError() + .errorCode(ErrorCode.InternalError_Data_NullResourceDefName) .becauseOf("name of a resource-def on resource def collection of service-def[" + serviceDefName + "] was null") .build()); valid = false; @@ -462,31 +483,34 @@ public class RangerPolicyValidator extends RangerValidator { boolean excludesSupported = Boolean.TRUE.equals(resourceDef.getExcludesSupported()); // could be null boolean policyResourceIsExcludes = Boolean.TRUE.equals(policyResource.getIsExcludes()); // could be null if (policyResourceIsExcludes && !excludesSupported) { - failures.add(new ValidationFailureDetailsBuilder() + failures.add(new RangerPolicyValidationErrorBuilder() .field("isExcludes") .subField(resourceName) .isSemanticallyIncorrect() + .errorCode(ErrorCode.Invalid_Excludes_NotSupported) .becauseOf("isExcludes specified as [" + policyResourceIsExcludes + "] for resource [" + resourceName + "] which doesn't support isExcludes") .build()); valid = false; } if (policyResourceIsExcludes && !isAdmin) { - failures.add(new ValidationFailureDetailsBuilder() + failures.add(new RangerPolicyValidationErrorBuilder() .field("isExcludes") .subField("isAdmin") .isSemanticallyIncorrect() .becauseOf("isExcludes specified as [" + policyResourceIsExcludes + "] for resource [" + resourceName + "]. Insufficient permissions to create excludes policy.") + .errorCode(ErrorCode.Invalid_Excludes_RequiresAdmin) .build()); valid = false; } boolean recursiveSupported = Boolean.TRUE.equals(resourceDef.getRecursiveSupported()); boolean policyIsRecursive = Boolean.TRUE.equals(policyResource.getIsRecursive()); if (policyIsRecursive && !recursiveSupported) { - failures.add(new ValidationFailureDetailsBuilder() + failures.add(new RangerPolicyValidationErrorBuilder() .field("isRecursive") .subField(resourceName) .isSemanticallyIncorrect() .becauseOf("isRecursive specified as [" + policyIsRecursive + "] for resource [" + resourceName + "] which doesn't support isRecursive") + .errorCode(ErrorCode.Invalid_Recursive_NotSupported) .build()); valid = false; } @@ -517,11 +541,14 @@ public class RangerPolicyValidator extends RangerValidator { if (StringUtils.isBlank(aValue)) { LOG.debug("resource value was blank"); } else if (!aValue.matches(regEx)) { - failures.add(new ValidationFailureDetailsBuilder() + String message = String.format("Value[%s] of resource[%s] does not conform to the validation regex[%s] defined on the service-def[%s]", aValue, name, regEx, serviceDef.getName()); + LOG.debug(message); + failures.add(new RangerPolicyValidationErrorBuilder() .field("resource-values") .subField(name) .isSemanticallyIncorrect() - .becauseOf("resources value[" + aValue + "] does not match validation regex[" + regEx + "] defined on service-def[" + serviceDef.getName() + "]") + .becauseOf(message) + .errorCode(ErrorCode.Invalid_ResourceValue_RegEx) .build()); valid = false; } @@ -546,10 +573,11 @@ public class RangerPolicyValidator extends RangerValidator { } else { for (RangerPolicyItem policyItem : policyItems) { if (policyItem == null) { - failures.add(new ValidationFailureDetailsBuilder() + failures.add(new RangerPolicyValidationErrorBuilder() .field("policy item") .isMissing() .becauseOf("policy items object was null") + .errorCode(ErrorCode.InternalError_Data_NullPolicyItem) .build()); valid = false; } else { @@ -577,10 +605,11 @@ public class RangerPolicyValidator extends RangerValidator { // access items collection can't be empty (unless delegated admin is true) and should be otherwise valid if (CollectionUtils.isEmpty(policyItem.getAccesses())) { if (!Boolean.TRUE.equals(policyItem.getDelegateAdmin())) { - failures.add(new ValidationFailureDetailsBuilder() + failures.add(new RangerPolicyValidationErrorBuilder() .field("policy item accesses") .isMissing() .becauseOf("policy items accesses collection was null") + .errorCode(ErrorCode.Missing_PolicyItemAccesses) .build()); valid = false; } else { @@ -591,10 +620,11 @@ public class RangerPolicyValidator extends RangerValidator { } // both users and user-groups collections can't be empty if (CollectionUtils.isEmpty(policyItem.getUsers()) && CollectionUtils.isEmpty(policyItem.getGroups())) { - failures.add(new ValidationFailureDetailsBuilder() + failures.add(new RangerPolicyValidationErrorBuilder() .field("policy item users/user-groups") .isMissing() .becauseOf("both users and user-groups collections on the policy item were null/empty") + .errorCode(ErrorCode.Missing_PolicyItemUserGroup) .build()); valid = false; } @@ -618,10 +648,11 @@ public class RangerPolicyValidator extends RangerValidator { Set accessTypes = getAccessTypes(serviceDef); for (RangerPolicyItemAccess access : accesses) { if (access == null) { - failures.add(new ValidationFailureDetailsBuilder() + failures.add(new RangerPolicyValidationErrorBuilder() .field("policy item access") .isMissing() .becauseOf("policy items access object was null") + .errorCode(ErrorCode.InternalError_Data_NullPolicyItemAccess) .build()); valid = false; } else { @@ -650,19 +681,21 @@ public class RangerPolicyValidator extends RangerValidator { } else { String accessType = access.getType(); if (StringUtils.isBlank(accessType)) { - failures.add(new ValidationFailureDetailsBuilder() + failures.add(new RangerPolicyValidationErrorBuilder() .field("policy item access type") .isMissing() .becauseOf("policy items access type's name was null/empty/blank") + .errorCode(ErrorCode.Missing_PolicyItemAccessType) .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() + failures.add(new RangerPolicyValidationErrorBuilder() .field("policy item access type") .isSemanticallyIncorrect() .becauseOf(message) + .errorCode(ErrorCode.Invalid_PolicyItemAccessType) .build()); valid = false; } @@ -671,10 +704,11 @@ public class RangerPolicyValidator extends RangerValidator { 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() + failures.add(new RangerPolicyValidationErrorBuilder() .field("policy item access type allowed") .isSemanticallyIncorrect() .becauseOf(message) + .errorCode(ErrorCode.Invalid_PolicyItemAccessType_Deny) .build()); valid = false; } @@ -685,4 +719,129 @@ public class RangerPolicyValidator extends RangerValidator { } return valid; } + + static class RangerPolicyValidationErrorBuilder extends ValidationFailureDetailsBuilder { + + @Override + ValidationFailureDetails build() { + return new RangerPolicyValidationFailure(_errorCode, _fieldName, _subFieldName, _missing, _semanticError, _internalError, _reason); + } + } + + static class RangerPolicyValidationFailure extends ValidationFailureDetails { + + public RangerPolicyValidationFailure(int errorCode, String fieldName, String subFieldName, boolean missing, boolean semanticError, boolean internalError, String reason) { + super(errorCode, fieldName, subFieldName, missing, semanticError, internalError, reason); + } + + // TODO remove and move to baseclass when all 3 move to new message framework + @Override + public String toString() { + LOG.debug("RangerPolicyValidationFailure.toString"); + String result = null; + if (_ErrorCode2MessageTemplate.containsKey(_errorCode)) { + Integer templateId = _ErrorCode2MessageTemplate.get(_errorCode); + if (templateId != null && _TemplateId2Template.containsKey(templateId)) { + String messageTemplate = _TemplateId2Template.get(templateId); + if (StringUtils.isNotBlank(messageTemplate)) { + // in the worst case result should be at least same as the messageTemplate which we know is not blank + result = substituteVariables(messageTemplate); + } else { + LOG.warn("Internal error: Message template string for template [" + templateId + "] was empty!"); + } + } else { + LOG.warn("Internal error: template id for error code [" + templateId + "] was null or template id to message map did not comtain the templateid"); + } + } else { + LOG.warn("Internal error: error code [" + _errorCode + "] not found in errorcode to message template map"); + } + if (result == null) { + result = super.toString(); + } + return "Policy validation failure: " + result; + } + } + + static class ErrorCode { + public static final int InternalError_InvalidMethodInvocation = 1001; + public static final int Missing_PolicyId_Delete = 1002; + public static final int Missing_PolicyObject = 1003; + public static final int Missing_PolicyId_Update = 1004; + public static final int Invalid_PolicyId = 1005; + public static final int Missing_PolicyName = 1006; + public static final int InternalError_Data_MultiplePoliciesSameName = 1007; + public static final int Duplicate_PolicyName_Create = 1008; + public static final int Duplicate_PolicyName_Update = 1009; + public static final int Missing_ServiceName = 1010; + public static final int Invalid_ServiceName = 1011; + public static final int Missing_PolicyItems = 1012; + public static final int InternalError_Data_MissingServiceDef = 1013; + public static final int Duplicate_PolicyResource = 1014; + public static final int Invalid_PolicyResource_NoCompatibleHierarchy = 1015; + public static final int Invalid_PolicyResource_MissingMandatory = 1016; + public static final int InternalError_Data_NullResourceDef = 1017; + public static final int InternalError_Data_NullResourceDefName = 1018; + public static final int Invalid_Excludes_NotSupported = 1019; + public static final int Invalid_Excludes_RequiresAdmin = 1020; + public static final int Invalid_Recursive_NotSupported = 1021; + public static final int Invalid_ResourceValue_RegEx = 1022; + public static final int InternalError_Data_NullPolicyItem = 1023; + public static final int Missing_PolicyItemAccesses = 1024; + public static final int Missing_PolicyItemUserGroup = 1025; + public static final int InternalError_Data_NullPolicyItemAccess = 1026; + public static final int Missing_PolicyItemAccessType = 1027; + public static final int Invalid_PolicyItemAccessType = 1028; + public static final int Invalid_PolicyItemAccessType_Deny = 1029; + } + static class MessageId { + public static final int InternalError = 1; + public static final int MissingField = 2; + public static final int InternalError_BadData = 3; + public static final int DuplicateValue = 4; + public static final int InvalidField = 5; + } + + static Object[][] MessageTemplateData = new Object[][] { + { MessageId.InternalError, "Internal error: {reason}."}, + { MessageId.InternalError_BadData, "Internal error: bad data encountered [{field}]: {reason}"}, + { MessageId.MissingField, "Missing Required field [{field}]: {reason}"}, + { MessageId.InvalidField, "Invalid value specified for field [{field}]: {reason}"}, + { MessageId.DuplicateValue, "Duplicate value for [{field}]: {reason}"}, + }; + static final Map _TemplateId2Template = createMap(MessageTemplateData); + + static int[][] ErrorCode2MessageTemplateData = new int[][] { + { ErrorCode.InternalError_InvalidMethodInvocation, MessageId.InternalError}, + { ErrorCode.Missing_PolicyId_Delete, MessageId.MissingField}, + { ErrorCode.Missing_PolicyObject, MessageId.InternalError}, + { ErrorCode.Missing_PolicyId_Update, MessageId.MissingField}, + { ErrorCode.Invalid_PolicyId, MessageId.InvalidField}, + { ErrorCode.Missing_PolicyName, MessageId.MissingField}, + { ErrorCode.InternalError_Data_MultiplePoliciesSameName, MessageId.InternalError_BadData}, + { ErrorCode.Duplicate_PolicyName_Create, MessageId.DuplicateValue}, + { ErrorCode.Duplicate_PolicyName_Update, MessageId.DuplicateValue}, + { ErrorCode.Missing_ServiceName, MessageId.MissingField}, + { ErrorCode.Invalid_ServiceName, MessageId.InvalidField}, + { ErrorCode.Missing_PolicyItems, MessageId.MissingField}, + { ErrorCode.InternalError_Data_MissingServiceDef, MessageId.InternalError_BadData}, + { ErrorCode.Duplicate_PolicyResource, MessageId.DuplicateValue}, + { ErrorCode.Invalid_PolicyResource_NoCompatibleHierarchy, MessageId.InvalidField}, + { ErrorCode.Invalid_PolicyResource_MissingMandatory, MessageId.MissingField}, + { ErrorCode.InternalError_Data_NullResourceDef, MessageId.InternalError_BadData}, + { ErrorCode.InternalError_Data_NullResourceDefName, MessageId.InternalError_BadData}, + { ErrorCode.Invalid_Excludes_NotSupported, MessageId.InvalidField}, + { ErrorCode.Invalid_Excludes_RequiresAdmin, MessageId.InvalidField}, + { ErrorCode.Invalid_Recursive_NotSupported, MessageId.InvalidField}, + { ErrorCode.Invalid_ResourceValue_RegEx, MessageId.InvalidField}, + { ErrorCode.InternalError_Data_NullPolicyItem, MessageId.InternalError_BadData}, + { ErrorCode.Missing_PolicyItemAccesses, MessageId.MissingField}, + { ErrorCode.Missing_PolicyItemUserGroup, MessageId.MissingField}, + { ErrorCode.InternalError_Data_NullPolicyItemAccess, MessageId.InternalError_BadData}, + { ErrorCode.Missing_PolicyItemAccessType, MessageId.MissingField}, + { ErrorCode.Invalid_PolicyItemAccessType, MessageId.InvalidField}, + { ErrorCode.Invalid_PolicyItemAccessType_Deny, MessageId.InvalidField}, + + }; + static final Map _ErrorCode2MessageTemplate = createMap(ErrorCode2MessageTemplateData); + } http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/e35e1c19/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerValidator.java ---------------------------------------------------------------------- diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerValidator.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerValidator.java index 3246138..381864d 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerValidator.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerValidator.java @@ -106,9 +106,10 @@ public abstract class RangerValidator { 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(";"); + for (int i = 0; i < failures.size(); i++) { + builder.append(String.format("(%d)", i)); + builder.append(failures.get(i).toString()); + builder.append(" "); } message = builder.toString(); } @@ -651,4 +652,39 @@ public abstract class RangerValidator { return result; } + static Map createMap(int[][] data) { + Map result = new HashMap<>(); + if (data != null) { + for (int[] row : data) { + Integer key = row[0]; + Integer value = row[1]; + if (result.containsKey(key)) { + LOG.warn("createMap: Internal error: duplicate key: multiple rows found for [" + key + "]. Skipped"); + } else { + result.put(key, value); + } + } + } + return result; + } + + static Map createMap(Object[][] data) { + Map result = new HashMap<>(); + if (data != null) { + for (Object[] row : data) { + Integer key = (Integer)row[0]; + String value = (String)row[1]; + if (key == null) { + LOG.warn("createMap: error converting key[" + row[0] + "] to Integer! Sipped!"); + } else if (StringUtils.isEmpty(value)) { + LOG.warn("createMap: empty/null value. Skipped!"); + } else if (result.containsKey(key)) { + LOG.warn("createMap: Internal error: duplicate key. Multiple rows found for [" + key + "]. Skipped"); + } else { + result.put(key, value); + } + } + } + return result; + } } http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/e35e1c19/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 index 015203a..64e7e50 100644 --- 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 @@ -19,18 +19,27 @@ package org.apache.ranger.plugin.model.validation; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class ValidationFailureDetails { + private static final Log LOG = LogFactory.getLog(ValidationFailureDetails.class); + 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) { + final int _errorCode; + + public ValidationFailureDetails(int errorCode, String fieldName, String subFieldName, boolean missing, boolean semanticError, boolean internalError, String reason) { + _errorCode = errorCode; _missing = missing; _semanticError = semanticError; _internalError = internalError; @@ -39,6 +48,11 @@ public class ValidationFailureDetails { _reason = reason; } + // TODO - legacy signature remove after all 3 are ported over to new message framework + public ValidationFailureDetails(String fieldName, String subFieldName, boolean missing, boolean semanticError, boolean internalError, String reason) { + this(-1, fieldName, subFieldName, missing, semanticError, internalError, reason); + } + public String getFieldName() { return _fieldName; } @@ -61,18 +75,29 @@ public class ValidationFailureDetails { public String getSubFieldName() { return _subFieldName; } - + + // matches "{blah}", "{{blah}", "{ }" and yields variables names like "blah", "{blah", " ", etc. for substitution + static final Pattern _Pattern = Pattern.compile("\\{([^\\}]+)\\}"); + + public String substituteVariables(String template) { + return template.replace("{field}", _fieldName == null ? "" : _fieldName) + .replace("{sub-field}", _subFieldName == null ? "" : _subFieldName) + .replace("{reason}", _reason == null ? "" : _reason); + } + + // TODO legacy implementation. Remove when all @Override public String toString() { + LOG.debug("ValidationFailureDetails.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); + return Objects.hash(_fieldName, _subFieldName, _missing, _semanticError, _internalError, _reason, _errorCode); } @Override @@ -86,6 +111,7 @@ public class ValidationFailureDetails { Objects.equals(_reason, that._reason) && _internalError == that._internalError && _missing == that._missing && - _semanticError == that._semanticError; + _semanticError == that._semanticError && + _errorCode == that._errorCode; } } http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/e35e1c19/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 index 3a57341..ab67f1f 100644 --- 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 @@ -20,13 +20,14 @@ 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; - + protected String _fieldName; + protected boolean _missing; + protected boolean _semanticError; + protected String _reason; + protected String _subFieldName; + protected boolean _internalError; + protected int _errorCode; + ValidationFailureDetailsBuilder becauseOf(String aReason) { _reason = aReason; return this; @@ -61,4 +62,8 @@ public class ValidationFailureDetailsBuilder { return this; } + ValidationFailureDetailsBuilder errorCode(int errorCode) { + _errorCode = errorCode; + return this; + } } http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/e35e1c19/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerPolicyValidator.java ---------------------------------------------------------------------- diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerPolicyValidator.java b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerPolicyValidator.java index 6236d71..e0f3b0e 100644 --- a/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerPolicyValidator.java +++ b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerPolicyValidator.java @@ -341,7 +341,7 @@ public class TestRangerPolicyValidator { filter.setParam(SearchFilter.SERVICE_NAME, "service-name"); filter.setParam(SearchFilter.POLICY_NAME, "policy-name"); when(_store.getPolicies(filter)).thenReturn(existingPolicies); - checkFailure_isValid(Action.CREATE, "semantic", "name"); + checkFailure_isValid(Action.CREATE, "semantic", "policy name"); // update : does not exist for id when(_policy.getId()).thenReturn(7L); @@ -374,11 +374,11 @@ public class TestRangerPolicyValidator { for (boolean isAdmin : new boolean[] { true, false }) { when(_policy.getService()).thenReturn(null); _failures.clear(); assertFalse(_validator.isValid(_policy, action, isAdmin, _failures)); - _utils.checkFailureForMissingValue(_failures, "service"); + _utils.checkFailureForMissingValue(_failures, "service name"); when(_policy.getService()).thenReturn(""); _failures.clear(); assertFalse(_validator.isValid(_policy, action, isAdmin, _failures)); - _utils.checkFailureForMissingValue(_failures, "service"); + _utils.checkFailureForMissingValue(_failures, "service name"); } } @@ -389,19 +389,19 @@ public class TestRangerPolicyValidator { for (boolean isAdmin : new boolean[] { true, false }) { when(_policy.getService()).thenReturn(null); _failures.clear(); assertFalse(_validator.isValid(_policy, action, isAdmin, _failures)); - _utils.checkFailureForMissingValue(_failures, "service"); + _utils.checkFailureForMissingValue(_failures, "service name"); when(_policy.getService()).thenReturn(null); _failures.clear(); assertFalse(_validator.isValid(_policy, action, isAdmin, _failures)); - _utils.checkFailureForMissingValue(_failures, "service"); + _utils.checkFailureForMissingValue(_failures, "service name"); when(_policy.getService()).thenReturn("service-name"); _failures.clear(); assertFalse(_validator.isValid(_policy, action, isAdmin, _failures)); - _utils.checkFailureForSemanticError(_failures, "service"); + _utils.checkFailureForSemanticError(_failures, "service name"); when(_policy.getService()).thenReturn("another-service-name"); _failures.clear(); assertFalse(_validator.isValid(_policy, action, isAdmin, _failures)); - _utils.checkFailureForSemanticError(_failures, "service"); + _utils.checkFailureForSemanticError(_failures, "service name"); } } @@ -475,7 +475,7 @@ public class TestRangerPolicyValidator { for (Action action : cu) { for (boolean isAdmin : new boolean[] { true, false }) { _failures.clear(); assertFalse(_validator.isValid(_policy, action, isAdmin, _failures)); - _utils.checkFailureForSemanticError(_failures, "resources"); + _utils.checkFailureForSemanticError(_failures, "policy resources"); } } } @@ -766,19 +766,19 @@ public class TestRangerPolicyValidator { Map policyResources = _utils.createPolicyResourceMap(policyResourceMap_bad); when(_policy.getResources()).thenReturn(policyResources); assertFalse("Missing required resource and unknown resource", _validator.isValidResourceNames(_policy, _failures, _serviceDef)); - _utils.checkFailureForSemanticError(_failures, "resources"); + _utils.checkFailureForSemanticError(_failures, "policy resources"); // another bad resource map that straddles multiple hierarchies policyResources = _utils.createPolicyResourceMap(policyResourceMap_bad_multiple_hierarchies); when(_policy.getResources()).thenReturn(policyResources); _failures.clear(); assertFalse("Policy with resources for multiple hierarchies", _validator.isValidResourceNames(_policy, _failures, _serviceDef)); - _utils.checkFailureForSemanticError(_failures, "resources", "incompatible"); + _utils.checkFailureForSemanticError(_failures, "policy resources", "incompatible"); // another bad policy resource map that could match multiple hierarchies but is short on mandatory resources for all of those matches policyResources = _utils.createPolicyResourceMap(policyResourceMap_bad_multiple_hierarchies_missing_mandatory); when(_policy.getResources()).thenReturn(policyResources); _failures.clear(); assertFalse("Policy with resources for multiple hierarchies missing mandatory resources for all pontential matches", _validator.isValidResourceNames(_policy, _failures, _serviceDef)); - _utils.checkFailureForSemanticError(_failures, "resources", "missing mandatory"); + _utils.checkFailureForSemanticError(_failures, "policy resources", "missing mandatory"); } http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/e35e1c19/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestValidationFailureDetails.java ---------------------------------------------------------------------- diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestValidationFailureDetails.java b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestValidationFailureDetails.java new file mode 100644 index 0000000..0a758d1 --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestValidationFailureDetails.java @@ -0,0 +1,39 @@ +package org.apache.ranger.plugin.model.validation; + +import junit.framework.TestCase; +import org.junit.Test; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static org.junit.Assert.assertEquals; + +/** + * Created by alal on 6/17/15. + */ +public class TestValidationFailureDetails { + + @Test + public void test1() { + String[] templates = new String[] { + "The {field}, was missing and sub-field {sub-field} was mssing, too. Validation failed due to {reason}", // pattern at end. + "{field}, was missing and sub-field {sub-field} was mssing, too. Validation failed due to {reason}.", // pattern at start but not end. + "The {field}, was missing and sub-field {sub-field} was mssing, too. Validation failed due to {missing}.", // unknown substitute + "Template does not have field, but had {sub-field} along with a {reason} and a sprious field named {missing}.", // unknown substitute + }; + + ValidationFailureDetails failureDetails = new ValidationFailureDetails("id", "subType", false, false, false, "foo-bar"); + + String[] results = new String[] { + "The id, was missing and sub-field subType was mssing, too. Validation failed due to foo-bar", // pattern at end. + "id, was missing and sub-field subType was mssing, too. Validation failed due to foo-bar.", // pattern at start but not end. + "The id, was missing and sub-field subType was mssing, too. Validation failed due to {missing}.", // unknown substitute + "Template does not have field, but had subType along with a foo-bar and a sprious field named {missing}.", // unknown substitute + }; + + for (int i = 0; i < templates.length; i++) { + String result = failureDetails.substituteVariables(templates[i]); + assertEquals(results[i], result); + } + } +} \ No newline at end of file