ranger-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mad...@apache.org
Subject [3/7] incubator-ranger git commit: RANGER-560 Policy validation: user friendly error messages about validation failures
Date Thu, 30 Jul 2015 21:39:32 GMT
RANGER-560 Policy validation: user friendly error messages about validation failures

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


Project: http://git-wip-us.apache.org/repos/asf/incubator-ranger/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ranger/commit/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 <alal@hortonworks.com>
Authored: Thu Jun 18 15:30:51 2015 -0700
Committer: Madhan Neethiraj <madhan@apache.org>
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<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/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<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/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<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/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



Mime
View raw message