RANGER-560 Policy validation: user friendly error messages about validation 
failures

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/2c9be935
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ranger/tree/2c9be935
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ranger/diff/2c9be935

Branch: refs/heads/HDP-2.3.2-groupid
Commit: 2c9be935e1a53372f8e276b76e6305421a998a9d
Parents: 59810b2
Author: Alok Lal <[email protected]>
Authored: Thu Jun 18 15:30:51 2015 -0700
Committer: Alok Lal <[email protected]>
Committed: Wed Sep 2 13:09:42 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/2c9be935/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<RangerPolicy> 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<List<RangerResourceDef>> 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<List<RangerResourceDef>> 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<String, RangerPolicyResource> 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<String> 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<Integer, String> _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<Integer, Integer> _ErrorCode2MessageTemplate = 
createMap(ErrorCode2MessageTemplateData);
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/2c9be935/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<Integer, Integer> createMap(int[][] data) {
+               Map<Integer, Integer> 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<Integer, String> createMap(Object[][] data) {
+               Map<Integer, String> 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/2c9be935/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/2c9be935/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/2c9be935/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<String, RangerPolicyResource> 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/2c9be935/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


Reply via email to