ranger-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mad...@apache.org
Subject [3/4] incubator-ranger git commit: RANGER-278 move validation classes under org.apache.ranger.plugin.model.validation
Date Sat, 14 Mar 2015 18:52:27 GMT
http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/7bb68687/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
new file mode 100644
index 0000000..e0f68ad
--- /dev/null
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerPolicyValidator.java
@@ -0,0 +1,524 @@
+package org.apache.ranger.plugin.model.validation;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.ranger.plugin.model.RangerPolicy;
+import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItem;
+import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemAccess;
+import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource;
+import org.apache.ranger.plugin.model.RangerService;
+import org.apache.ranger.plugin.model.RangerServiceDef;
+import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef;
+import org.apache.ranger.plugin.model.validation.RangerPolicyValidator;
+import org.apache.ranger.plugin.model.validation.ValidationFailureDetails;
+import org.apache.ranger.plugin.model.validation.RangerValidator.Action;
+import org.apache.ranger.plugin.store.ServiceStore;
+import org.apache.ranger.plugin.util.SearchFilter;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Sets;
+
+public class TestRangerPolicyValidator {
+
+	@Before
+	public void setUp() throws Exception {
+		_store = mock(ServiceStore.class);
+		_policy = mock(RangerPolicy.class);
+		_validator = new RangerPolicyValidator(_store);
+		_serviceDef = mock(RangerServiceDef.class);
+	}
+	
+	final Action[] cu = new Action[] { Action.CREATE, Action.UPDATE };
+	final Object[] policyItemsData = new Object[] {
+			ImmutableMap.of(  // all good
+				"users", new String[] {"user1" ," user2"},
+				"groups", new String[] {"group1", "group2"},
+				"accesses", new String[] { "r", "w" },
+				"isAllowed", new Boolean[] { true, true }),
+			ImmutableMap.of(   // no users, access type different case
+				"groups", new String[] {"group3", "group4"},
+				"accesses", new String[]{"W", "x"}, 
+				"isAllowed", new Boolean[] { true, true }),
+			ImmutableMap.of(   // no groups
+				"users", new String[] {"user3" ," user4"}, 
+				"accesses", new String[] { "r", "x" },
+				"isAllowed", new Boolean[] { true, true }),
+			ImmutableMap.of( // isallowed on access types is null, case is different from that in definition
+				"users", new String[] {"user7" ," user6"},
+				"accesses", new String[] { "a" },
+				"isAllowed", new Boolean[] { null, null })
+	};
+	String[] accessTypes = new String[] { "r", "w", "x", "A" };  // mix of lower and upper case
+	String[] accessTypes_bad = new String[] { "r", "w", "xx", }; // two missing (x, a), one new that isn't on bad (xx)
+	
+	private final Object[][] resourceDefData = new Object[][] {
+			// { name, mandatory, reg-exp, excludesSupported, recursiveSupported }
+			{ "db", true, "db\\d+", null, null }, // valid values: db1, db22, db983, etc.; invalid: db, db12x, ttx11, etc.; null => false for excludes and recursive
+			{ "tbl", true, null, true, true }, // regex == null => anything goes; excludes == true, recursive == true
+			{ "col", false, "col\\d{1,2}", false, true }  // valid: col1, col47, etc.; invalid: col, col238, col1, etc., excludes == false, recursive == true 
+	};
+	
+	private final Object[][] policyResourceMap_good = new Object[][] {
+			// resource-name, values, excludes, recursive
+			{ "db", new String[] { "db1", "db2" }, null, null },
+			{ "TBL", new String[] { "tbl1", "tbl2" }, true, false } // case should not matter
+	};
+	
+	private final Object[][] policyResourceMap_bad = new Object[][] {
+			// resource-name, values, excludes, recursive
+			{ "db", new String[] { "db1", "db2" }, null, true },        // mandatory "tbl" missing; recursive==true specified when resource-def does not support it (null) 
+			{"col", new String[] { "col12", "col 1" }, true, true },    // wrong format of value for "col"; excludes==true specified when resource-def does not allow it (false)
+			{"extra", new String[] { "extra1", "extra2" }, null, null } // spurious "extra" specified
+	};
+
+	@Test
+	public final void testIsValid_long() throws Exception {
+		// this validation should be removed if we start supporting other than delete action
+		assertFalse(_validator.isValid(3L, Action.CREATE, _failures));
+		_utils.checkFailureForInternalError(_failures);
+		
+		// should fail with appropriate error message if id is null
+		_failures.clear(); _failures.clear(); assertFalse(_validator.isValid((Long)null, Action.DELETE, _failures));
+		_utils.checkFailureForMissingValue(_failures, "id");
+		
+		// should fail with appropriate error message if policy can't be found for the specified id
+		when(_store.getPolicy(1L)).thenReturn(null);
+		when(_store.getPolicy(2L)).thenThrow(new Exception());
+		RangerPolicy existingPolicy = mock(RangerPolicy.class);
+		when(_store.getPolicy(3L)).thenReturn(existingPolicy);
+		_failures.clear(); assertFalse(_validator.isValid(1L, Action.DELETE, _failures));
+		_utils.checkFailureForSemanticError(_failures, "id");
+		_failures.clear(); assertFalse(_validator.isValid(2L, Action.DELETE, _failures));
+		_utils.checkFailureForSemanticError(_failures, "id");
+
+		// if policy exists then delete validation should pass 
+		assertTrue(_validator.isValid(3L, Action.DELETE, _failures));
+	}
+	
+	@Test
+	public final void testIsValid_happyPath() throws Exception {
+		// valid policy has valid non-empty name and service name 
+		when(_policy.getService()).thenReturn("service-name");
+		// service name exists
+		RangerService service = mock(RangerService.class);
+		when(service.getType()).thenReturn("service-type");
+		when(_store.getServiceByName("service-name")).thenReturn(service);
+		// service points to a valid service-def
+		_serviceDef = _utils.createServiceDefWithAccessTypes(accessTypes);
+		when(_store.getServiceDefByName("service-type")).thenReturn(_serviceDef);
+		// a matching policy should exist for create when checked by id and not exist when checked by name.
+		when(_store.getPolicy(7L)).thenReturn(null);
+		RangerPolicy existingPolicy = mock(RangerPolicy.class);
+		when(existingPolicy.getId()).thenReturn(8L);
+		when(_store.getPolicy(8L)).thenReturn(existingPolicy);
+		SearchFilter createFilter = new SearchFilter();
+		createFilter.setParam(SearchFilter.POLICY_NAME, "service-type");
+		createFilter.setParam(SearchFilter.POLICY_NAME, "policy-name-1"); // this name would be used for create
+		when(_store.getPolicies(createFilter)).thenReturn(new ArrayList<RangerPolicy>());
+		// a matching policy should not exist for update.
+		SearchFilter updateFilter = new SearchFilter();
+		updateFilter.setParam(SearchFilter.POLICY_NAME, "service-type");
+		updateFilter.setParam(SearchFilter.POLICY_NAME, "policy-name-2"); // this name would be used for update
+		List<RangerPolicy> existingPolicies = new ArrayList<RangerPolicy>();
+		existingPolicies.add(existingPolicy);
+		when(_store.getPolicies(updateFilter)).thenReturn(existingPolicies);
+		// valid policy can have empty set of policy items if audit is turned on
+		// null value for audit is treated as audit on.
+		for (Action action : cu) {
+			for (Boolean auditEnabled : new Boolean[] { null, true } ) {
+				when(_policy.getIsAuditEnabled()).thenReturn(auditEnabled);
+				if (action == Action.CREATE) {
+					when(_policy.getId()).thenReturn(7L);
+					when(_policy.getName()).thenReturn("policy-name-1");
+					assertTrue("" + action + ", " + auditEnabled, _validator.isValid(_policy, action, _failures));
+					assertTrue(_failures.isEmpty());
+				} else {
+					// update should work both when by-name is found or not, since nothing found by-name means name is being updated.
+					when(_policy.getId()).thenReturn(8L);
+					when(_policy.getName()).thenReturn("policy-name-1");
+					assertTrue("" + action + ", " + auditEnabled, _validator.isValid(_policy, action, _failures));
+					assertTrue(_failures.isEmpty());
+
+					when(_policy.getName()).thenReturn("policy-name-2");
+					assertTrue("" + action + ", " + auditEnabled, _validator.isValid(_policy, action, _failures));
+					assertTrue(_failures.isEmpty());
+				}
+			}
+		}
+		// if audit is disabled then policy should have policy items and all of them should be valid
+		List<RangerPolicyItem> policyItems = _utils.createPolicyItems(policyItemsData);
+		when(_policy.getPolicyItems()).thenReturn(policyItems);
+		when(_policy.getIsAuditEnabled()).thenReturn(false);
+		for (Action action : cu) {
+			if (action == Action.CREATE) {
+				when(_policy.getId()).thenReturn(7L);
+				when(_policy.getName()).thenReturn("policy-name-1");
+			} else {
+				when(_policy.getId()).thenReturn(8L);
+				when(_policy.getName()).thenReturn("policy-name-2");
+			}
+			assertTrue("" + action , _validator.isValid(_policy, action, _failures));
+			assertTrue(_failures.isEmpty());
+		}
+		
+		// above succeeded as service def did not have any resources on it, mandatory or otherwise.
+		// policy should have all mandatory resources specified, and they should conform to the validation pattern in resource definition
+		List<RangerResourceDef> resourceDefs = _utils.createResourceDefs(resourceDefData);
+		when(_serviceDef.getResources()).thenReturn(resourceDefs);
+		Map<String, RangerPolicyResource> resourceMap = _utils.createPolicyResourceMap(policyResourceMap_good);
+		when(_policy.getResources()).thenReturn(resourceMap);
+
+		for (Action action : cu) {
+			if (action == Action.CREATE) {
+				when(_policy.getId()).thenReturn(7L);
+				when(_policy.getName()).thenReturn("policy-name-1");
+			} else {
+				when(_policy.getId()).thenReturn(8L);
+				when(_policy.getName()).thenReturn("policy-name-2");
+			}
+			assertTrue("" + action , _validator.isValid(_policy, action, _failures));
+			assertTrue(_failures.isEmpty());
+		}
+	}
+	
+	void checkFailure_isValid(Action action, String errorType, String field) {
+		checkFailure_isValid(action, errorType, field, null);
+	}
+	
+	void checkFailure_isValid(Action action, String errorType, String field, String subField) {
+		_failures.clear();
+		assertFalse(_validator.isValid(_policy, action, _failures));
+		switch (errorType) {
+		case "missing":
+			_utils.checkFailureForMissingValue(_failures, field, subField);
+			break;
+		case "semantic":
+			_utils.checkFailureForSemanticError(_failures, field, subField);
+			break;
+		case "internal error":
+			_utils.checkFailureForInternalError(_failures);
+			break;
+		default:
+			fail("Unsupported errorType[" + errorType + "]");
+			break;
+		}
+	}
+	
+	@Test
+	public final void testIsValid_failures() throws Exception {
+		for (Action action : cu) {
+			// passing in a null policy should fail with appropriate failure reason
+			_policy = null;
+			checkFailure_isValid(action, "missing", "policy");
+			
+			// policy must have a name on it
+			_policy = mock(RangerPolicy.class);
+			for (String name : new String[] { null, "  " }) {
+				when(_policy.getName()).thenReturn(name);
+				checkFailure_isValid(action, "missing", "name");
+			}
+			
+			// for update id is required!
+			if (action == Action.UPDATE) {
+				when(_policy.getId()).thenReturn(null);
+				checkFailure_isValid(action, "missing", "id");
+			}
+		}
+		/*
+		 * Id is ignored for Create but name should not belong to an existing policy.  For update, policy should exist for its id and should match its name.
+		 */
+		when(_policy.getName()).thenReturn("policy-name");
+		when(_policy.getService()).thenReturn("service-name");
+
+		RangerPolicy existingPolicy = mock(RangerPolicy.class);
+		when(existingPolicy.getId()).thenReturn(7L);
+		List<RangerPolicy> existingPolicies = new ArrayList<RangerPolicy>();
+		existingPolicies.add(existingPolicy);
+		SearchFilter filter = new SearchFilter();
+		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");
+		
+		// update : does not exist for id
+		when(_policy.getId()).thenReturn(7L);
+		when(_store.getPolicy(7L)).thenReturn(null);
+		checkFailure_isValid(Action.UPDATE, "semantic", "id");
+
+		// Update: name should not point to an existing different policy, i.e. with a different id
+		when(_store.getPolicy(7L)).thenReturn(existingPolicy);
+		RangerPolicy anotherExistingPolicy = mock(RangerPolicy.class);
+		when(anotherExistingPolicy.getId()).thenReturn(8L);
+		existingPolicies.clear();
+		existingPolicies.add(anotherExistingPolicy);
+		when(_store.getPolicies(filter)).thenReturn(existingPolicies);
+		checkFailure_isValid(Action.UPDATE, "semantic", "id/name");
+
+		// more than one policies with same name is also an internal error
+		when(_policy.getName()).thenReturn("policy-name");
+		when(_store.getPolicies(filter)).thenReturn(existingPolicies);
+		existingPolicies.add(existingPolicy);
+		existingPolicy = mock(RangerPolicy.class);
+		existingPolicies.add(existingPolicy);
+		_failures.clear(); assertFalse(_validator.isValid(_policy, Action.UPDATE, _failures));
+		_utils.checkFailureForInternalError(_failures);
+		
+		// policy must have service name on it and it should be valid
+		when(_policy.getName()).thenReturn("policy-name");
+		when(_store.getServiceByName("service-name")).thenReturn(null);
+		when(_store.getServiceByName("another-service-name")).thenThrow(new Exception());
+
+		for (Action action : cu) {
+			when(_policy.getService()).thenReturn("service-name");
+			_failures.clear(); assertFalse(_validator.isValid(_policy, action, _failures));
+			_utils.checkFailureForMissingValue(_failures, "service");
+
+			when(_policy.getService()).thenReturn("another-service-name");
+			_failures.clear(); assertFalse(_validator.isValid(_policy, action, _failures));
+			_utils.checkFailureForMissingValue(_failures, "service");
+		}
+		
+		// policy must contain at least one policy item
+		List<RangerPolicyItem> policyItems = new ArrayList<RangerPolicy.RangerPolicyItem>();
+		when(_policy.getService()).thenReturn("service-name");
+		RangerService service = mock(RangerService.class);
+		when(_store.getServiceByName("service-name")).thenReturn(service);
+		for (Action action : cu) {
+			// when it is null
+			when(_policy.getPolicyItems()).thenReturn(null);
+			_failures.clear(); assertFalse(_validator.isValid(_policy, action, _failures));
+			_utils.checkFailureForMissingValue(_failures, "policy items");
+			// or when it is not null but empty.
+			when(_policy.getPolicyItems()).thenReturn(policyItems);
+			_failures.clear(); assertFalse(_validator.isValid(_policy, action, _failures));
+			_utils.checkFailureForMissingValue(_failures, "policy items");
+		}
+		
+		// these are known good policy items -- same as used above in happypath
+		policyItems = _utils.createPolicyItems(policyItemsData);
+		when(_policy.getPolicyItems()).thenReturn(policyItems);
+		// policy item check requires that service def should exist
+		when(service.getType()).thenReturn("service-type");
+		when(_store.getServiceDefByName("service-type")).thenReturn(null);
+		for (Action action : cu) {
+			_failures.clear(); assertFalse(_validator.isValid(_policy, action, _failures));
+			_utils.checkFailureForInternalError(_failures, "policy service def");
+		}
+		
+		// service-def should contain the right access types on it.
+		_serviceDef = _utils.createServiceDefWithAccessTypes(accessTypes_bad);
+		when(_store.getServiceDefByName("service-type")).thenReturn(_serviceDef);
+		for (Action action : cu) {
+			_failures.clear(); assertFalse(_validator.isValid(_policy, action, _failures));
+			_utils.checkFailureForSemanticError(_failures, "policy item access type");
+		}
+
+		// create the right service def with right resource defs - this is the same as in the happypath test above.
+		_serviceDef = _utils.createServiceDefWithAccessTypes(accessTypes);
+		when(_store.getPolicies(filter)).thenReturn(null);
+		List<RangerResourceDef> resourceDefs = _utils.createResourceDefs(resourceDefData);
+		when(_serviceDef.getResources()).thenReturn(resourceDefs);
+		when(_store.getServiceDefByName("service-type")).thenReturn(_serviceDef);
+		// one mandtory is missing (tbl) and one unknown resource is specified (extra), and values of option resource don't conform to validation pattern (col)
+		Map<String, RangerPolicyResource> policyResources = _utils.createPolicyResourceMap(policyResourceMap_bad);
+		when(_policy.getResources()).thenReturn(policyResources);
+		for (Action action : cu) {
+			_failures.clear(); assertFalse(_validator.isValid(_policy, action, _failures));
+			_utils.checkFailureForMissingValue(_failures, "resources", "tbl"); // for missing resource: tbl
+			_utils.checkFailureForSemanticError(_failures, "resources", "extra"); // for spurious resource: "extra"
+			_utils.checkFailureForSemanticError(_failures, "resource-values", "col"); // for spurious resource: "extra"
+			_utils.checkFailureForSemanticError(_failures, "isRecursive", "db"); // for specifying it as true when def did not allow it
+			_utils.checkFailureForSemanticError(_failures, "isExcludes", "col"); // for specifying it as true when def did not allow it
+		}
+	}
+	
+	@Test
+	public void test_isValidResourceValues() {
+		List<RangerResourceDef> resourceDefs = _utils.createResourceDefs(resourceDefData);
+		when(_serviceDef.getResources()).thenReturn(resourceDefs);
+		Map<String, RangerPolicyResource> policyResources = _utils.createPolicyResourceMap(policyResourceMap_bad);
+		assertFalse(_validator.isValidResourceValues(policyResources, _failures, _serviceDef));
+		_utils.checkFailureForSemanticError(_failures, "resource-values", "col");
+		
+		policyResources = _utils.createPolicyResourceMap(policyResourceMap_good);
+		assertTrue(_validator.isValidResourceValues(policyResources, _failures, _serviceDef));
+	}
+	
+	@Test
+	public void test_isValidPolicyItems_failures() {
+		// null/empty list is good because there is nothing
+		assertTrue(_validator.isValidPolicyItems(null, _failures, _serviceDef));
+		_failures.isEmpty();
+
+		List<RangerPolicyItem> policyItems = new ArrayList<RangerPolicy.RangerPolicyItem>();
+		assertTrue(_validator.isValidPolicyItems(policyItems, _failures, _serviceDef));
+		_failures.isEmpty();
+		
+		// null elements in the list are flagged
+		policyItems.add(null);
+		assertFalse(_validator.isValidPolicyItems(policyItems, _failures, _serviceDef));
+		_utils.checkFailureForMissingValue(_failures, "policy item");
+	}
+	
+	@Test
+	public void test_isValidPolicyItem_failures() {
+
+		// empty access collections are invalid
+		RangerPolicyItem policyItem = mock(RangerPolicyItem.class);
+		when(policyItem.getAccesses()).thenReturn(null);
+		_failures.clear(); assertFalse(_validator.isValidPolicyItem(policyItem, _failures, _serviceDef));
+		_utils.checkFailureForMissingValue(_failures, "policy item accesses");
+
+		List<RangerPolicyItemAccess> accesses = new ArrayList<RangerPolicy.RangerPolicyItemAccess>();
+		when(policyItem.getAccesses()).thenReturn(accesses);
+		_failures.clear(); assertFalse(_validator.isValidPolicyItem(policyItem, _failures, _serviceDef));
+		_utils.checkFailureForMissingValue(_failures, "policy item accesses");
+		
+		// both user and groups can't be null
+		RangerPolicyItemAccess access = mock(RangerPolicyItemAccess.class);
+		accesses.add(access);
+		when(policyItem.getUsers()).thenReturn(null);
+		when(policyItem.getGroups()).thenReturn(new ArrayList<String>());
+		_failures.clear(); assertFalse(_validator.isValidPolicyItem(policyItem, _failures, _serviceDef));
+		_utils.checkFailureForMissingValue(_failures, "policy item users/user-groups");
+	}
+	
+	@Test
+	public void test_isValidItemAccesses_happyPath() {
+		
+		// happy path
+		Object[][] data = new Object[][] {
+				{ "a", null }, // valid
+				{ "b", true }, // valid
+				{ "c", true }, // valid
+		};
+		List<RangerPolicyItemAccess> accesses = _utils.createItemAccess(data);
+		_serviceDef = _utils.createServiceDefWithAccessTypes(new String[] { "a", "b", "c", "d" });
+		assertTrue(_validator.isValidItemAccesses(accesses, _failures, _serviceDef));
+		assertTrue(_failures.isEmpty());
+	}
+	
+	@Test
+	public void test_isValidItemAccesses_failure() {
+
+		// null policy item access values are an error
+		List<RangerPolicyItemAccess> accesses = new ArrayList<RangerPolicyItemAccess>();
+		accesses.add(null);
+		_failures.clear(); assertFalse(_validator.isValidItemAccesses(accesses, _failures, _serviceDef));
+		_utils.checkFailureForMissingValue(_failures, "policy item access");
+
+		// all items must be valid for this call to be valid
+		Object[][] data = new Object[][] {
+				{ "a", null }, // valid
+				{ null, null }, // invalid - name can't be null
+				{ "c", true }, // valid
+		};
+		accesses = _utils.createItemAccess(data);
+		_serviceDef = _utils.createServiceDefWithAccessTypes(new String[] { "a", "b", "c", "d" });
+		_failures.clear(); assertFalse(_validator.isValidItemAccesses(accesses, _failures, _serviceDef));
+	}
+	
+	@Test
+	public void test_isValidPolicyItemAccess_happyPath() {
+		
+		RangerPolicyItemAccess access = mock(RangerPolicyItemAccess.class);
+		when(access.getType()).thenReturn("an-Access"); // valid
+
+		Set<String> validAccesses = Sets.newHashSet(new String[] { "an-access", "another-access" });  // valid accesses should be lower-cased
+		
+		// both null or true access types are the same and valid
+		for (Boolean allowed : new Boolean[] { null, true } ) {
+			when(access.getIsAllowed()).thenReturn(allowed);
+			assertTrue(_validator.isValidPolicyItemAccess(access, _failures, validAccesses));
+			assertTrue(_failures.isEmpty());
+		}
+	}
+	
+	@Test
+	public void test_isValidPolicyItemAccess_failures() {
+		
+		Set<String> validAccesses = Sets.newHashSet(new String[] { "anAccess", "anotherAccess" });
+		// null/empty names are invalid
+		RangerPolicyItemAccess access = mock(RangerPolicyItemAccess.class);
+		when(access.getIsAllowed()).thenReturn(null); // valid since null == true
+		for (String type : new String[] { null, " 	"}) {
+			when(access.getType()).thenReturn(type); // invalid
+			// null/empty validAccess set skips all checks
+			assertTrue(_validator.isValidPolicyItemAccess(access, _failures, null));
+			assertTrue(_validator.isValidPolicyItemAccess(access, _failures, new HashSet<String>()));
+			_failures.clear(); assertFalse(_validator.isValidPolicyItemAccess(access, _failures, validAccesses));
+			_utils.checkFailureForMissingValue(_failures, "policy item access type");
+		}
+		
+		when(access.getType()).thenReturn("anAccess"); // valid
+		when(access.getIsAllowed()).thenReturn(false); // invalid
+		_failures.clear();assertFalse(_validator.isValidPolicyItemAccess(access, _failures, validAccesses));
+		_utils.checkFailureForSemanticError(_failures, "policy item access type allowed");
+		
+		when(access.getType()).thenReturn("newAccessType"); // invalid
+		_failures.clear(); assertFalse(_validator.isValidPolicyItemAccess(access, _failures, validAccesses));
+		_utils.checkFailureForSemanticError(_failures, "policy item access type");
+	}
+	
+	final Object[][] resourceDef_happyPath = new Object[][] {
+			// { "resource-name", "isExcludes", "isRecursive" }
+			{ "db", true, true },
+			{ "tbl", null, true },
+			{ "col", true, false },
+	};
+	
+	private Object[][] policyResourceMap_happyPath = new Object[][] {
+			// { "resource-name", "isExcludes", "isRecursive" }
+			{ "db", null, true },    // null should be treated as false
+			{ "tbl", false, false }, // set to false where def is null and def is true  
+			{ "col", true, null}     // set to null where def is false
+	};
+	
+	@Test
+	public final void test_isValidResourceFlags_happyPath() {
+		// passing null values effectively bypasses the filter
+		assertTrue(_validator.isValidResourceFlags(null, _failures, null, "a-service-def", "a-policy"));
+		// so does passing in empty collections
+		Map<String, RangerPolicyResource> resourceMap = _utils.createPolicyResourceMap2(policyResourceMap_happyPath);
+		List<RangerResourceDef> resourceDefs = _utils.createResourceDefs2(resourceDef_happyPath);
+		when(_serviceDef.getResources()).thenReturn(resourceDefs);
+		assertTrue(_validator.isValidResourceFlags(resourceMap, _failures, resourceDefs, "a-service-def", "a-policy"));
+	}
+
+	private Object[][] policyResourceMap_failures = new Object[][] {
+			// { "resource-name", "isExcludes", "isRecursive" }
+			{ "db", true, true },    // ok: def has true for both  
+			{ "tbl", true, null },   // excludes: def==false, policy==true  
+			{ "col", false, true }    // recursive: def==null (i.e. false), policy==true
+	};
+	
+	@Test
+	public final void test_isValidResourceFlags_failures() {
+		// passing true when def says false/null
+		List<RangerResourceDef> resourceDefs = _utils.createResourceDefs2(resourceDef_happyPath);
+		Map<String, RangerPolicyResource> resourceMap = _utils.createPolicyResourceMap2(policyResourceMap_failures);
+		when(_serviceDef.getResources()).thenReturn(resourceDefs);
+		assertFalse(_validator.isValidResourceFlags(resourceMap, _failures, resourceDefs, "a-service-def", "a-policy"));
+		_utils.checkFailureForSemanticError(_failures, "isExcludes", "tbl");
+		_utils.checkFailureForSemanticError(_failures, "isRecursive", "col");
+	}
+
+	private ValidationTestUtils _utils = new ValidationTestUtils();
+	private List<ValidationFailureDetails> _failures = new ArrayList<ValidationFailureDetails>();
+	private ServiceStore _store;
+	private RangerPolicy _policy;
+	private RangerPolicyValidator _validator;
+	private RangerServiceDef _serviceDef;
+}

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/7bb68687/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceDefValidator.java
----------------------------------------------------------------------
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceDefValidator.java b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceDefValidator.java
new file mode 100644
index 0000000..1019aa1
--- /dev/null
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceDefValidator.java
@@ -0,0 +1,355 @@
+package org.apache.ranger.plugin.model.validation;
+
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.ranger.plugin.model.RangerServiceDef;
+import org.apache.ranger.plugin.model.RangerServiceDef.RangerAccessTypeDef;
+import org.apache.ranger.plugin.model.RangerServiceDef.RangerEnumDef;
+import org.apache.ranger.plugin.model.RangerServiceDef.RangerEnumElementDef;
+import org.apache.ranger.plugin.model.validation.RangerServiceDefValidator;
+import org.apache.ranger.plugin.model.validation.ValidationFailureDetails;
+import org.apache.ranger.plugin.model.validation.RangerValidator.Action;
+import org.apache.ranger.plugin.store.ServiceStore;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableMap;
+
+public class TestRangerServiceDefValidator {
+
+	@Before
+	public void setUp() throws Exception {
+		_store = mock(ServiceStore.class);
+		_validator = new RangerServiceDefValidator(_store);
+		_failures = new ArrayList<ValidationFailureDetails>();
+		_serviceDef = mock(RangerServiceDef.class);
+	}
+
+	final Action[] cu = new Action[] { Action.CREATE, Action.UPDATE };
+	
+	final Object[][] accessTypes_good = new Object[][] {
+			{ "read",  null },                                // ok, null implied grants
+			{ "write", new String[] {   } },                  // ok, empty implied grants
+			{ "admin", new String[] { "READ",  "write" } }    // ok, admin access implies read/write, access types are case-insensitive
+	};
+
+	final Map<String, String[]> enums_good = ImmutableMap.of(
+			"authentication-type", new String[] { "simple", "kerberos" },
+			"time-unit", new String[] { "day", "hour", "minute" }
+	);
+	
+	@Test
+	public final void test_isValid_happyPath_create() throws Exception {
+		
+		// setup access types with implied access and couple of enums
+		List<RangerAccessTypeDef> accessTypeDefs = _utils.createAccessTypeDefs(accessTypes_good);
+		when(_serviceDef.getAccessTypes()).thenReturn(accessTypeDefs);
+		List<RangerEnumDef> enumDefs = _utils.createEnumDefs(enums_good);
+		when(_serviceDef.getEnums()).thenReturn(enumDefs);
+
+		// create: id is not relevant, name should not conflict 
+		when(_serviceDef.getId()).thenReturn(null); // id is not relevant for create
+		when(_serviceDef.getName()).thenReturn("aServiceDef"); // service has a name
+		when(_store.getServiceDefByName("aServiceDef")).thenReturn(null); // no name collision
+		assertTrue(_validator.isValid(_serviceDef, Action.CREATE, _failures));
+		assertTrue(_failures.isEmpty());
+		
+		// update: id should match existing service, name should not point to different service def
+		when(_serviceDef.getId()).thenReturn(5L);
+		RangerServiceDef existingServiceDef = mock(RangerServiceDef.class);
+		when(_store.getServiceDef(5L)).thenReturn(existingServiceDef);
+		assertTrue(_validator.isValid(_serviceDef, Action.UPDATE, _failures));
+		assertTrue(_failures.isEmpty());
+		
+		// update: if name points to a service that it's id should be the same
+		RangerServiceDef anotherExistingServiceDef = mock(RangerServiceDef.class);
+		when(anotherExistingServiceDef.getId()).thenReturn(5L);
+		when(_store.getServiceDefByName("aServiceDef")).thenReturn(anotherExistingServiceDef);
+		assertTrue(_validator.isValid(_serviceDef, Action.UPDATE, _failures));
+		assertTrue(_failures.isEmpty());
+	}
+	
+	@Test
+	public final void testIsValid_Long_failures() throws Exception {
+		Long id = null;
+		// passing in wrong action type 
+		boolean result = _validator.isValid((Long)null, Action.CREATE, _failures);
+		assertFalse(result);
+		_utils.checkFailureForInternalError(_failures);
+		// passing in null id is an error
+		_failures.clear(); assertFalse(_validator.isValid((Long)null, Action.DELETE, _failures));
+		_utils.checkFailureForMissingValue(_failures, "id");
+		// a service def with that id should exist, else it is an error
+		id = 3L;
+		when(_store.getServiceDef(id)).thenReturn(null);
+		_failures.clear(); assertFalse(_validator.isValid(id, Action.DELETE, _failures));
+		_utils.checkFailureForSemanticError(_failures, "id");
+		// happypath
+		when(_store.getServiceDef(id)).thenReturn(_serviceDef);
+		_failures.clear(); assertTrue(_validator.isValid(id, Action.DELETE, _failures));
+		assertTrue(_failures.isEmpty());
+	}
+
+	@Test
+	public final void testIsValid_failures_name() throws Exception {
+		// null service def and bad service def name
+		for (Action action : cu) {
+			// passing in null service def is an error
+			assertFalse(_validator.isValid((RangerServiceDef)null, action, _failures));
+			_utils.checkFailureForMissingValue(_failures, "service def");
+			// name should be valid
+			for (String name : new String[] { null, "", "  " }) {
+				when(_serviceDef.getName()).thenReturn(name);
+				_failures.clear(); assertFalse(_validator.isValid(_serviceDef, action, _failures));
+				_utils.checkFailureForMissingValue(_failures, "name");
+			}
+		}
+	}
+	
+	@Test
+	public final void testIsValid_failures_id() throws Exception {
+		// id is required for update
+		when(_serviceDef.getId()).thenReturn(null);
+		assertFalse(_validator.isValid(_serviceDef, Action.UPDATE, _failures));
+		_utils.checkFailureForMissingValue(_failures, "id");
+		
+		// update: service should exist for the passed in id
+		Long id = 7L;
+		when(_serviceDef.getId()).thenReturn(id);
+		when(_store.getServiceDef(id)).thenReturn(null);
+		assertFalse(_validator.isValid(_serviceDef, Action.UPDATE, _failures));
+		_utils.checkFailureForSemanticError(_failures, "id");
+
+		when(_store.getServiceDef(id)).thenThrow(new Exception());
+		assertFalse(_validator.isValid(_serviceDef, Action.UPDATE, _failures));
+		_utils.checkFailureForSemanticError(_failures, "id");
+	}
+	
+	@Test
+	public final void testIsValid_failures_nameId_create() throws Exception {
+		// service shouldn't exist with the name
+		RangerServiceDef existingServiceDef = mock(RangerServiceDef.class);
+		when(_store.getServiceDefByName("existing-service")).thenReturn(existingServiceDef);
+		when(_serviceDef.getName()).thenReturn("existing-service");
+		_failures.clear(); assertFalse(_validator.isValid(_serviceDef, Action.CREATE, _failures));
+		_utils.checkFailureForSemanticError(_failures, "name");
+	}
+	
+	@Test
+	public final void testIsValid_failures_nameId_update() throws Exception {
+		
+		// update: if service exists with the same name then it can't point to a different service
+		Long id = 7L;
+		when(_serviceDef.getId()).thenReturn(id);
+		RangerServiceDef existingServiceDef = mock(RangerServiceDef.class);
+		when(existingServiceDef.getId()).thenReturn(id);
+		when(_store.getServiceDef(id)).thenReturn(existingServiceDef);
+		
+		String name = "aServiceDef";
+		when(_serviceDef.getName()).thenReturn(name);
+		RangerServiceDef anotherExistingServiceDef = mock(RangerServiceDef.class);
+		Long anotherId = 49L;
+		when(anotherExistingServiceDef.getId()).thenReturn(anotherId);
+		when(_store.getServiceDefByName(name)).thenReturn(anotherExistingServiceDef);
+		
+		assertFalse(_validator.isValid(_serviceDef, Action.UPDATE, _failures));
+		_utils.checkFailureForSemanticError(_failures, "id/name");
+	}
+
+	final Object[][] accessTypes_bad_unknownType = new Object[][] {
+			{ "read",  null },                                // ok, null implied grants
+			{ "write", new String[] {   } },                  // ok, empty implied grants
+			{ "admin", new String[] { "ReaD",  "execute" } }  // non-existent access type (execute), read is good (case should not matter)
+	};
+
+	final Object[][] accessTypes_bad_selfReference = new Object[][] {
+			{ "read",  null },                                // ok, null implied grants
+			{ "write", new String[] {   } },                  // ok, empty implied grants
+			{ "admin", new String[] { "write", "admin" } }  // non-existent access type (execute)
+	};
+
+	@Test
+	public final void test_isValidAccessTypes_happyPath() {
+		List<RangerAccessTypeDef> input = _utils.createAccessTypeDefs(accessTypes_good);
+		assertTrue(_validator.isValidAccessTypes(input, _failures));
+		assertTrue(_failures.isEmpty());
+	}
+	
+	@Test
+	public final void test_isValidAccessTypes_failures() {
+		// sending in empty null access type defs is ok
+		assertTrue(_validator.isValidAccessTypes(null, _failures));
+		assertTrue(_failures.isEmpty());
+		
+		List<RangerAccessTypeDef> input = new ArrayList<RangerAccessTypeDef>();
+		_failures.clear(); assertTrue(_validator.isValidAccessTypes(input, _failures));
+		assertTrue(_failures.isEmpty());
+
+		// null/empty access types
+		List<RangerAccessTypeDef> accessTypeDefs = _utils.createAccessTypeDefs(new String[] { null, "", "		" });
+		_failures.clear(); assertFalse(_validator.isValidAccessTypes(accessTypeDefs, _failures));
+		_utils.checkFailureForMissingValue(_failures, "access type name");
+		
+		// duplicate access types
+		accessTypeDefs = _utils.createAccessTypeDefs(new String[] { "read", "write", "execute", "read" } );
+		_failures.clear(); assertFalse(_validator.isValidAccessTypes(accessTypeDefs, _failures));
+		_utils.checkFailureForSemanticError(_failures, "access type name", "read");
+		
+		// duplicate access types - case-insensitive
+		accessTypeDefs = _utils.createAccessTypeDefs(new String[] { "read", "write", "execute", "READ" } );
+		_failures.clear(); assertFalse(_validator.isValidAccessTypes(accessTypeDefs, _failures));
+		_utils.checkFailureForSemanticError(_failures, "access type name", "READ");
+		
+		// unknown access type in implied grants list
+		accessTypeDefs = _utils.createAccessTypeDefs(accessTypes_bad_unknownType);
+		_failures.clear(); assertFalse(_validator.isValidAccessTypes(accessTypeDefs, _failures));
+		_utils.checkFailureForSemanticError(_failures, "implied grants", "execute");
+		
+		// access type with implied grant referring to itself
+		accessTypeDefs = _utils.createAccessTypeDefs(accessTypes_bad_selfReference);
+		_failures.clear(); assertFalse(_validator.isValidAccessTypes(accessTypeDefs, _failures));
+		_utils.checkFailureForSemanticError(_failures, "implied grants", "admin");
+	}
+	
+	final Map<String, String[]> enums_bad_enumName_null = ImmutableMap.of(
+			"authentication-type", new String[] { "simple", "kerberos" },
+			"time-unit", new String[] { "day", "hour", "minute" },
+			"null", new String[] { "foo", "bar", "tar" } // null enum-name -- "null" is a special value that leads to a null enum name
+	);
+	
+	final Map<String, String[]> enums_bad_enumName_blank = ImmutableMap.of(
+			"authentication-type", new String[] { "simple", "kerberos" },
+			"time-unit", new String[] { "day", "hour", "minute" },
+			"  ", new String[] { "foo", "bar", "tar" } // enum name is all spaces
+	);
+	
+	final Map<String, String[]> enums_bad_Elements_empty = ImmutableMap.of(
+			"authentication-type", new String[] { "simple", "kerberos" },
+			"time-unit", new String[] { "day", "hour", "minute" },
+			"anEnum", new String[] { } // enum elements collection is empty
+	);
+	
+	final Map<String, String[]> enums_bad_enumName_duplicate_exact = ImmutableMap.of(
+			"authentication-type", new String[] { "simple", "kerberos" },
+			"time-unit", new String[] { "day", "hour", "minute" }
+	);
+	
+	final Map<String, String[]> enums_bad_enumName_duplicate_differentCase = ImmutableMap.of(
+			"authentication-type", new String[] { "simple", "kerberos" },
+			"time-unit", new String[] { "day", "hour", "minute" },
+			"Authentication-Type", new String[] { } // duplicate enum-name different in case
+	);
+	
+	@Test
+	public final void test_isValidEnums_happyPath() {
+		List<RangerEnumDef> input = _utils.createEnumDefs(enums_good);
+		assertTrue(_validator.isValidEnums(input, _failures));
+		assertTrue(_failures.isEmpty());
+	}
+	
+	@Test
+	public final void test_isValidEnums_failures() {
+		// null elements in enum def list are a failure
+		List<RangerEnumDef> input = _utils.createEnumDefs(enums_good);
+		input.add(null);
+		assertFalse(_validator.isValidEnums(input, _failures));
+		_utils.checkFailureForMissingValue(_failures, "enum def");
+		
+		// enum names should be valid
+		input = _utils.createEnumDefs(enums_bad_enumName_null);
+		_failures.clear(); assertFalse(_validator.isValidEnums(input, _failures));
+		_utils.checkFailureForMissingValue(_failures, "enum def name");
+
+		input = _utils.createEnumDefs(enums_bad_enumName_blank);
+		_failures.clear(); assertFalse(_validator.isValidEnums(input, _failures));
+		_utils.checkFailureForMissingValue(_failures, "enum def name");
+		
+		// enum elements collection should not be null or empty
+		input = _utils.createEnumDefs(enums_good);
+		RangerEnumDef anEnumDef = mock(RangerEnumDef.class);
+		when(anEnumDef.getName()).thenReturn("anEnum");
+		when(anEnumDef.getElements()).thenReturn(null);
+		input.add(anEnumDef);
+		_failures.clear(); assertFalse(_validator.isValidEnums(input, _failures));
+		_utils.checkFailureForMissingValue(_failures, "enum values", "anEnum");
+
+		input = _utils.createEnumDefs(enums_bad_Elements_empty);
+		_failures.clear(); assertFalse(_validator.isValidEnums(input, _failures));
+		_utils.checkFailureForMissingValue(_failures, "enum values", "anEnum");
+	
+		// enum names should be distinct -- exact match
+		input = _utils.createEnumDefs(enums_good);
+		// add an element with same name as the first element
+		String name = input.iterator().next().getName();
+		when(anEnumDef.getName()).thenReturn(name);
+		List<RangerEnumElementDef> elementDefs = _utils.createEnumElementDefs(new String[] {"val1", "val2"}); 
+		when(anEnumDef.getElements()).thenReturn(elementDefs);
+		input.add(anEnumDef);
+		_failures.clear(); assertFalse(_validator.isValidEnums(input, _failures));
+		_utils.checkFailureForSemanticError(_failures, "enum def name", name);
+
+		// enum names should be distinct -- case insensitive
+		input = _utils.createEnumDefs(enums_bad_enumName_duplicate_differentCase);
+		_failures.clear(); assertFalse(_validator.isValidEnums(input, _failures));
+		_utils.checkFailureForSemanticError(_failures, "enum def name", "Authentication-Type");
+	
+		// enum default index should be right
+		input = _utils.createEnumDefs(enums_good);
+		// set the index of 1st on to be less than 0
+		when(input.iterator().next().getDefaultIndex()).thenReturn(-1);
+		_failures.clear(); assertFalse(_validator.isValidEnums(input, _failures));
+		_utils.checkFailureForSemanticError(_failures, "enum default index", "authentication-type");
+		// set the index to be more than number of elements
+		when(input.iterator().next().getDefaultIndex()).thenReturn(2);
+		_failures.clear(); assertFalse(_validator.isValidEnums(input, _failures));
+		_utils.checkFailureForSemanticError(_failures, "enum default index", "authentication-type");
+	}
+
+	@Test
+	public final void test_isValidEnumElements_happyPath() {
+		List<RangerEnumElementDef> input = _utils.createEnumElementDefs(new String[] { "simple", "kerberos" });
+		assertTrue(_validator.isValidEnumElements(input, _failures, "anEnum"));
+		assertTrue(_failures.isEmpty());
+	}
+
+	@Test
+	public final void test_isValidEnumElements_failures() {
+		// enum element collection should not have nulls in it
+		List<RangerEnumElementDef> input = _utils.createEnumElementDefs(new String[] { "simple", "kerberos" });
+		input.add(null);
+		assertFalse(_validator.isValidEnumElements(input, _failures, "anEnum"));
+		_utils.checkFailureForMissingValue(_failures, "enum element", "anEnum");
+
+		// element names can't be null/empty
+		input = _utils.createEnumElementDefs(new String[] { "simple", "kerberos", null });
+		_failures.clear(); assertFalse(_validator.isValidEnumElements(input, _failures, "anEnum"));
+		_utils.checkFailureForMissingValue(_failures, "enum element name", "anEnum");
+
+		input = _utils.createEnumElementDefs(new String[] { "simple", "kerberos", "		" }); // two tabs
+		_failures.clear(); assertFalse(_validator.isValidEnumElements(input, _failures, "anEnum"));
+		_utils.checkFailureForMissingValue(_failures, "enum element name", "anEnum");
+		
+		// element names should be distinct - case insensitive
+		input = _utils.createEnumElementDefs(new String[] { "simple", "kerberos", "kerberos" }); // duplicate name - exact match
+		_failures.clear(); assertFalse(_validator.isValidEnumElements(input, _failures, "anEnum"));
+		_utils.checkFailureForSemanticError(_failures, "enum element name", "anEnum");
+		
+		input = _utils.createEnumElementDefs(new String[] { "simple", "kerberos", "kErbErOs" }); // duplicate name - different case
+		_failures.clear(); assertFalse(_validator.isValidEnumElements(input, _failures, "anEnum"));
+		_utils.checkFailureForSemanticError(_failures, "enum element name", "anEnum");
+	}
+	
+	private ValidationTestUtils _utils = new ValidationTestUtils();
+	RangerServiceDef _serviceDef;
+	List<ValidationFailureDetails> _failures;
+	ServiceStore _store;
+	RangerServiceDefValidator _validator;
+}

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/7bb68687/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceValidator.java
----------------------------------------------------------------------
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceValidator.java b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceValidator.java
new file mode 100644
index 0000000..dd8485e
--- /dev/null
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceValidator.java
@@ -0,0 +1,240 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.ranger.plugin.model.validation;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.ranger.plugin.model.RangerService;
+import org.apache.ranger.plugin.model.RangerServiceDef;
+import org.apache.ranger.plugin.model.RangerServiceDef.RangerServiceConfigDef;
+import org.apache.ranger.plugin.model.validation.RangerServiceValidator;
+import org.apache.ranger.plugin.model.validation.ValidationFailureDetails;
+import org.apache.ranger.plugin.model.validation.RangerValidator.Action;
+import org.apache.ranger.plugin.store.ServiceStore;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TestRangerServiceValidator {
+	
+	final Action[] cud = new Action[] { Action.CREATE, Action.UPDATE, Action.DELETE };
+	final Action[] cu = new Action[] { Action.CREATE, Action.UPDATE };
+	final Action[] ud = new Action[] { Action.UPDATE, Action.DELETE };
+
+	@Before
+	public void before() {
+		_store = mock(ServiceStore.class);
+		_action = Action.CREATE; // by default we set action to create
+		_validator = new RangerServiceValidator(_store);
+	}
+
+	void checkFailure_isValid(RangerServiceValidator validator, RangerService service, Action action, List<ValidationFailureDetails> failures, String errorType, String field) {
+		checkFailure_isValid(validator, service, action, failures, errorType, field, null);
+	}
+	
+	void checkFailure_isValid(RangerServiceValidator validator, RangerService service, Action action, List<ValidationFailureDetails> failures, String errorType, String field, String subField) {
+		failures.clear();
+		assertFalse(validator.isValid(service, action, failures));
+		switch (errorType) {
+		case "missing":
+			_utils.checkFailureForMissingValue(failures, field, subField);
+			break;
+		case "semantic":
+			_utils.checkFailureForSemanticError(failures, field, subField);
+			break;
+		case "internal error":
+			_utils.checkFailureForInternalError(failures);
+			break;
+		default:
+			fail("Unsupported errorType[" + errorType + "]");
+			break;
+		}
+	}
+	
+	@Test
+	public void testIsValid_failures() throws Exception {
+		RangerService service = mock(RangerService.class);
+		// passing in a null service to the check itself is an error
+		assertFalse(_validator.isValid((RangerService)null, _action, _failures));
+		_utils.checkFailureForMissingValue(_failures, "service");
+
+		// id is required for update
+		when(service.getId()).thenReturn(null);
+		// let's verify the failure and the sort of error information that is returned (for one of these)
+		// assert that among the failure reason is one about id being missing.
+		checkFailure_isValid(_validator, service, Action.UPDATE, _failures, "missing", "id");
+		when(service.getId()).thenReturn(7L);
+
+		for (Action action : cu) {
+			// null, empty of blank name renders a service invalid
+			for (String name : new String[] { null, "", " 	" }) { // spaces and tabs
+				when(service.getName()).thenReturn(name);
+				checkFailure_isValid(_validator, service, action, _failures, "missing", "name");
+			}
+			// same is true for the type
+			for (String type : new String[] { null, "", "    " }) {
+				when(service.getType()).thenReturn(type);
+				checkFailure_isValid(_validator, service, action, _failures, "missing", "type");
+			}
+		}
+		when(service.getName()).thenReturn("aName");
+
+		// if non-empty, then the type should exist!
+		when(_store.getServiceDefByName("null-type")).thenReturn(null);
+		when(_store.getServiceDefByName("throwing-type")).thenThrow(new Exception());
+		for (Action action : cu) {
+			for (String type : new String[] { "null-type", "throwing-type" }) {
+				when(service.getType()).thenReturn(type);
+				checkFailure_isValid(_validator, service, action, _failures, "semantic", "type");
+			}
+		}
+		when(service.getType()).thenReturn("aType");
+		RangerServiceDef serviceDef = mock(RangerServiceDef.class);
+		when(_store.getServiceDefByName("aType")).thenReturn(serviceDef);
+		
+		// Create: No service should exist matching its id and/or name
+		RangerService anExistingService = mock(RangerService.class);
+		when(_store.getServiceByName("aName")).thenReturn(anExistingService);
+		checkFailure_isValid(_validator, service, Action.CREATE, _failures, "semantic", "name");
+
+		// Update: service should exist matching its id and name specified should not belong to a different service
+		when(_store.getService(7L)).thenReturn(null);
+		when(_store.getServiceByName("aName")).thenReturn(anExistingService);
+		checkFailure_isValid(_validator, service, Action.UPDATE, _failures, "semantic", "id");
+
+		when(_store.getService(7L)).thenReturn(anExistingService);
+		RangerService anotherExistingService = mock(RangerService.class);
+		when(anotherExistingService.getId()).thenReturn(49L);
+		when(_store.getServiceByName("aName")).thenReturn(anotherExistingService);
+		checkFailure_isValid(_validator, service, Action.UPDATE, _failures, "semantic", "id/name");
+	}
+	
+	@Test
+	public void test_isValid_missingRequiredParameter() throws Exception {
+		// Create/Update: simulate a condition where required parameters are missing
+		Object[][] input = new Object[][] {
+				{ "param1", true },
+				{ "param2", true },
+				{ "param3", false },
+				{ "param4", false },
+		};
+		List<RangerServiceConfigDef> configDefs = _utils.createServiceConditionDefs(input);
+		RangerServiceDef serviceDef = mock(RangerServiceDef.class);
+		when(serviceDef.getConfigs()).thenReturn(configDefs);
+		// wire this service def into store
+		when(_store.getServiceDefByName("aType")).thenReturn(serviceDef);
+		// create a service with some require parameters missing
+		RangerService service = mock(RangerService.class);
+		when(service.getType()).thenReturn("aType");
+		when(service.getName()).thenReturn("aName");
+		// required parameters param2 is missing
+		String[] params = new String[] { "param1", "param3", "param4", "param5" };
+		Map<String, String> paramMap = _utils.createMap(params);
+		when(service.getConfigs()).thenReturn(paramMap);
+		// service does not exist in the store
+		when(_store.getServiceByName("aService")).thenReturn(null);
+		for (Action action : cu) {
+			// it should be invalid
+			checkFailure_isValid(_validator, service, action, _failures, "missing", "configuration", "param2");
+		}
+	}
+
+	@Test
+	public void test_isValid_happyPath() throws Exception {
+		// create a service def with some required parameters 
+		Object[][] serviceDefInput = new Object[][] {
+				{ "param1", true },
+				{ "param2", true },
+				{ "param3", false },
+				{ "param4", false },
+				{ "param5", true },
+		};
+		List<RangerServiceConfigDef> configDefs = _utils.createServiceConditionDefs(serviceDefInput);
+		RangerServiceDef serviceDef = mock(RangerServiceDef.class);
+		when(serviceDef.getConfigs()).thenReturn(configDefs);
+		// create a service with some parameters on it
+		RangerService service = mock(RangerService.class);
+		when(service.getName()).thenReturn("aName");
+		when(service.getType()).thenReturn("aType");
+		// contains an extra parameter (param6) and one optional is missing(param4)
+		String[] configs = new String[] { "param1", "param2", "param3", "param5", "param6" };
+		Map<String, String> configMap = _utils.createMap(configs);  
+		when(service.getConfigs()).thenReturn(configMap);
+		// wire then into the store
+		// service does not exists
+		when(_store.getServiceByName("aName")).thenReturn(null);
+		// service def exists
+		when(_store.getServiceDefByName("aType")).thenReturn(serviceDef);
+
+		assertTrue(_validator.isValid(service, Action.CREATE, _failures));
+
+		// for update to work the only additional requirement is that id is required and service should exist
+		// if name is not null and it points to a service then it should match the id
+		when(service.getId()).thenReturn(7L);
+		RangerService existingService = mock(RangerService.class);
+		when(existingService.getId()).thenReturn(7L);
+		when(_store.getService(7L)).thenReturn(existingService);
+		when(_store.getServiceByName("aName")).thenReturn(existingService);
+		assertTrue(_validator.isValid(service, Action.UPDATE, _failures));
+		// name need not point to a service for update to work, of course.
+		when(_store.getServiceByName("aName")).thenReturn(null);
+		assertTrue(_validator.isValid(service, Action.UPDATE, _failures));
+	}
+
+	@Test
+	public void test_isValid_withId_errorConditions() throws Exception {
+		// api that takes in long is only supported for delete currently
+		assertFalse(_validator.isValid(1L, Action.CREATE, _failures));
+		_utils.checkFailureForInternalError(_failures);
+		// passing in a null id is a failure!
+		_validator = new RangerServiceValidator(_store);
+		_failures.clear(); assertFalse(_validator.isValid((Long)null, Action.DELETE, _failures));
+		_utils.checkFailureForMissingValue(_failures, "id");
+		// if service with that id does not exist then that, too, is a failure
+		when(_store.getService(1L)).thenReturn(null);
+		when(_store.getService(2L)).thenThrow(new Exception());
+		_failures.clear(); assertFalse(_validator.isValid(1L, Action.DELETE, _failures));
+		_utils.checkFailureForSemanticError(_failures, "id");
+
+		_failures.clear(); assertFalse(_validator.isValid(2L, Action.DELETE, _failures));
+		_utils.checkFailureForSemanticError(_failures, "id");
+	}
+	
+	@Test
+	public void test_isValid_withId_happyPath() throws Exception {
+		_validator = new RangerServiceValidator(_store);
+		RangerService service = mock(RangerService.class);
+		when(_store.getService(1L)).thenReturn(service);
+		assertTrue(_validator.isValid(1L, Action.DELETE, _failures));
+	}
+	
+	private ServiceStore _store;
+	private RangerServiceValidator _validator;
+	private Action _action;
+	private ValidationTestUtils _utils = new ValidationTestUtils();
+	private List<ValidationFailureDetails> _failures = new ArrayList<ValidationFailureDetails>();
+}

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/7bb68687/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerValidator.java
----------------------------------------------------------------------
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerValidator.java b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerValidator.java
new file mode 100644
index 0000000..f17b2c2
--- /dev/null
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerValidator.java
@@ -0,0 +1,476 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.ranger.plugin.model.validation;
+
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.ranger.plugin.model.RangerPolicy;
+import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource;
+import org.apache.ranger.plugin.model.RangerService;
+import org.apache.ranger.plugin.model.RangerServiceDef;
+import org.apache.ranger.plugin.model.RangerServiceDef.RangerAccessTypeDef;
+import org.apache.ranger.plugin.model.RangerServiceDef.RangerEnumDef;
+import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef;
+import org.apache.ranger.plugin.model.RangerServiceDef.RangerServiceConfigDef;
+import org.apache.ranger.plugin.model.validation.RangerValidator;
+import org.apache.ranger.plugin.model.validation.RangerValidator.Action;
+import org.apache.ranger.plugin.store.ServiceStore;
+import org.apache.ranger.plugin.util.SearchFilter;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.collect.Maps;
+
+public class TestRangerValidator {
+
+	static class RangerValidatorForTest extends RangerValidator {
+
+		public RangerValidatorForTest(ServiceStore store) {
+			super(store);
+		}
+		
+		boolean isValid(String behavior) {
+			boolean valid;
+			if (behavior.equals("valid")) {
+				valid = true;
+			} else {
+				valid = false;
+			}
+			return valid;
+		}
+	}
+	
+	@Before
+	public void before() {
+		_store = mock(ServiceStore.class);
+		_validator = new RangerValidatorForTest(_store);
+	}
+
+	@Test
+	public void test_ctor_firewalling() {
+		try {
+			// service store can't be null during construction  
+			new RangerValidatorForTest(null);
+			fail("Should have thrown exception!");
+		} catch (IllegalArgumentException e) {
+			// expected exception
+		}
+	}
+	
+	@Test
+	public void test_validate() {
+		// default implementation should fail.  This is abstract class.  Sub-class must do something sensible with isValid
+		try {
+			_validator.validate(1L, Action.CREATE);
+			fail("Should have thrown exception!");
+		} catch (Exception e) {
+			// ok expected exception
+			String message = e.getMessage();
+			assertTrue(message.contains("internal error"));
+		}
+	}
+
+	@Test
+	public void test_getServiceConfigParameters() {
+		// reasonable protection against null values
+		Set<String> parameters = _validator.getServiceConfigParameters(null);
+		assertNotNull(parameters);
+		assertTrue(parameters.isEmpty());
+		
+		RangerService service = mock(RangerService.class);
+		when(service.getConfigs()).thenReturn(null);
+		parameters = _validator.getServiceConfigParameters(service);
+		assertNotNull(parameters);
+		assertTrue(parameters.isEmpty());
+		
+		when(service.getConfigs()).thenReturn(new HashMap<String, String>());
+		parameters = _validator.getServiceConfigParameters(service);
+		assertNotNull(parameters);
+		assertTrue(parameters.isEmpty());
+
+		String[] keys = new String[] { "a", "b", "c" };
+		Map<String, String> map = _utils.createMap(keys);
+		when(service.getConfigs()).thenReturn(map);
+		parameters = _validator.getServiceConfigParameters(service);
+		for (String key: keys) {
+			assertTrue("key", parameters.contains(key));
+		}
+	}
+	
+	@Test
+	public void test_getRequiredParameters() {
+		// reasonable protection against null things
+		Set<String> parameters = _validator.getRequiredParameters(null);
+		assertNotNull(parameters);
+		assertTrue(parameters.isEmpty());
+
+		RangerServiceDef serviceDef = mock(RangerServiceDef.class);
+		when(serviceDef.getConfigs()).thenReturn(null);
+		parameters = _validator.getRequiredParameters(null);
+		assertNotNull(parameters);
+		assertTrue(parameters.isEmpty());
+
+		List<RangerServiceConfigDef> configs = new ArrayList<RangerServiceDef.RangerServiceConfigDef>();
+		when(serviceDef.getConfigs()).thenReturn(configs);
+		parameters = _validator.getRequiredParameters(null);
+		assertNotNull(parameters);
+		assertTrue(parameters.isEmpty());
+		
+		Object[][] input = new Object[][] {
+				{ "param1", false },
+				{ "param2", true },
+				{ "param3", true },
+				{ "param4", false },
+		};
+		configs = _utils.createServiceConditionDefs(input);
+		when(serviceDef.getConfigs()).thenReturn(configs);
+		parameters = _validator.getRequiredParameters(serviceDef);
+		assertTrue("result does not contain: param2", parameters.contains("param2"));
+		assertTrue("result does not contain: param3", parameters.contains("param3"));
+	}
+	
+	@Test
+	public void test_getServiceDef() {
+		try {
+			// if service store returns null or throws an exception then service is deemed invalid
+			when(_store.getServiceDefByName("return null")).thenReturn(null);
+			when(_store.getServiceDefByName("throw")).thenThrow(new Exception());
+			RangerServiceDef serviceDef = mock(RangerServiceDef.class);
+			when(_store.getServiceDefByName("good-service")).thenReturn(serviceDef);
+		} catch (Exception e) {
+			e.printStackTrace();
+			fail("Unexpected exception during mocking!");
+		}
+		
+		assertNull(_validator.getServiceDef("return null"));
+		assertNull(_validator.getServiceDef("throw"));
+		assertFalse(_validator.getServiceDef("good-service") == null);
+	}
+
+	@Test
+	public void test_getPolicy() throws Exception {
+		// if service store returns null or throws an exception then return null policy
+		when(_store.getPolicy(1L)).thenReturn(null);
+		when(_store.getPolicy(2L)).thenThrow(new Exception());
+		RangerPolicy policy = mock(RangerPolicy.class);
+		when(_store.getPolicy(3L)).thenReturn(policy);
+		
+		assertNull(_validator.getPolicy(1L));
+		assertNull(_validator.getPolicy(2L));
+		assertTrue(_validator.getPolicy(3L) != null);
+	}
+
+	@Test
+	public void test_getService_byId() throws Exception {
+		// if service store returns null or throws an exception then service is deemed invalid
+		when(_store.getService(1L)).thenReturn(null);
+		when(_store.getService(2L)).thenThrow(new Exception());
+		RangerService service = mock(RangerService.class);
+		when(_store.getService(3L)).thenReturn(service);
+		
+		assertNull(_validator.getService(1L));
+		assertNull(_validator.getService(2L));
+		assertTrue(_validator.getService(3L) != null);
+	}
+
+	@Test
+	public void test_getService() {
+		try {
+			// if service store returns null or throws an exception then service is deemed invalid
+			when(_store.getServiceByName("return null")).thenReturn(null);
+			when(_store.getServiceByName("throw")).thenThrow(new Exception());
+			RangerService service = mock(RangerService.class);
+			when(_store.getServiceByName("good-service")).thenReturn(service);
+		} catch (Exception e) {
+			e.printStackTrace();
+			fail("Unexpected exception during mocking!");
+		}
+		
+		assertNull(_validator.getService("return null"));
+		assertNull(_validator.getService("throw"));
+		assertFalse(_validator.getService("good-service") == null);
+	}
+	
+	@Test
+	public void test_getAccessTypes() {
+		// passing in null service def
+		Set<String> accessTypes = _validator.getAccessTypes((RangerServiceDef)null);
+		assertTrue(accessTypes.isEmpty());
+		// that has null or empty access type def
+		RangerServiceDef serviceDef = mock(RangerServiceDef.class);
+		when(serviceDef.getAccessTypes()).thenReturn(null);
+		accessTypes = _validator.getAccessTypes(serviceDef);
+		assertTrue(accessTypes.isEmpty());
+
+		List<RangerAccessTypeDef> accessTypeDefs = new ArrayList<RangerServiceDef.RangerAccessTypeDef>();
+		when(serviceDef.getAccessTypes()).thenReturn(accessTypeDefs);
+		accessTypes = _validator.getAccessTypes(serviceDef);
+		assertTrue(accessTypes.isEmpty());
+		
+		// having null accesstypedefs
+		accessTypeDefs.add(null);
+		accessTypes = _validator.getAccessTypes(serviceDef);
+		assertTrue(accessTypes.isEmpty());
+		
+		// access type defs with null empty blank names are skipped, spaces within names are preserved
+		String[] names = new String[] { null, "", "a", "  ", "b ", "		", " C", "	D	" };
+		accessTypeDefs.addAll(_utils.createAccessTypeDefs(names));
+		accessTypes = _validator.getAccessTypes(serviceDef);
+		assertEquals(4, accessTypes.size());
+		assertTrue(accessTypes.contains("a"));
+		assertTrue(accessTypes.contains("b "));
+		assertTrue(accessTypes.contains(" c"));
+		assertTrue(accessTypes.contains("	d	"));
+	}
+	
+	@Test
+	public void test_getResourceNames() {
+		// passing in null service def
+		Set<String> accessTypes = _validator.getMandatoryResourceNames((RangerServiceDef)null);
+		assertTrue(accessTypes.isEmpty());
+		// that has null or empty access type def
+		RangerServiceDef serviceDef = mock(RangerServiceDef.class);
+		when(serviceDef.getResources()).thenReturn(null);
+		accessTypes = _validator.getMandatoryResourceNames(serviceDef);
+		assertTrue(accessTypes.isEmpty());
+
+		List<RangerResourceDef> resourceDefs = new ArrayList<RangerResourceDef>();
+		when(serviceDef.getResources()).thenReturn(resourceDefs);
+		accessTypes = _validator.getMandatoryResourceNames(serviceDef);
+		assertTrue(accessTypes.isEmpty());
+		
+		// having null accesstypedefs
+		resourceDefs.add(null);
+		accessTypes = _validator.getMandatoryResourceNames(serviceDef);
+		assertTrue(accessTypes.isEmpty());
+		
+		// access type defs with null empty blank names are skipped, spaces within names are preserved
+		Object[][] data = {
+				{ "a", true },  // all good
+				null,           // this should put a null element in the resource def!
+				{ "b", null },  // mandatory field is null, i.e. false
+				{ "c", false }, // non-mandatory field false - upper case
+				{ "D", true },  // resource specified in upper case
+				{ "E", false }, // all good
+		};
+		resourceDefs.addAll(_utils.createResourceDefs(data));
+		accessTypes = _validator.getMandatoryResourceNames(serviceDef);
+		assertEquals(2, accessTypes.size());
+		assertTrue(accessTypes.contains("a"));
+		assertTrue(accessTypes.contains("d")); // name should come back lower case
+		
+		accessTypes = _validator.getAllResourceNames(serviceDef);
+		assertEquals(5, accessTypes.size());
+		assertTrue(accessTypes.contains("b"));
+		assertTrue(accessTypes.contains("c"));
+		assertTrue(accessTypes.contains("e"));
+	}
+
+	@Test
+	public void test_getValidationRegExes() {
+		// passing in null service def
+		Map<String, String> regExMap = _validator.getValidationRegExes((RangerServiceDef)null);
+		assertTrue(regExMap.isEmpty());
+		// that has null or empty access type def
+		RangerServiceDef serviceDef = mock(RangerServiceDef.class);
+		when(serviceDef.getResources()).thenReturn(null);
+		regExMap = _validator.getValidationRegExes(serviceDef);
+		assertTrue(regExMap.isEmpty());
+
+		List<RangerResourceDef> resourceDefs = new ArrayList<RangerResourceDef>();
+		when(serviceDef.getResources()).thenReturn(resourceDefs);
+		regExMap = _validator.getValidationRegExes(serviceDef);
+		assertTrue(regExMap.isEmpty());
+		
+		// having null accesstypedefs
+		resourceDefs.add(null);
+		regExMap = _validator.getValidationRegExes(serviceDef);
+		assertTrue(regExMap.isEmpty());
+		
+		// access type defs with null empty blank names are skipped, spaces within names are preserved
+		String[][] data = {
+				{ "a", null },     // null-regex
+				null,              // this should put a null element in the resource def!
+				{ "b", "regex1" }, // valid
+				{ "c", "" },       // empty regex
+				{ "d", "regex2" }, // valid
+				{ "e", "   " },    // blank regex
+				{ "f", "regex3" }, // all good
+		};
+		resourceDefs.addAll(_utils.createResourceDefsWithRegEx(data));
+		regExMap = _validator.getValidationRegExes(serviceDef);
+		assertEquals(3, regExMap.size());
+		assertEquals("regex1", regExMap.get("b"));
+		assertEquals("regex2", regExMap.get("d"));
+		assertEquals("regex3", regExMap.get("f"));
+	}
+
+	@Test
+	public void test_getPolicyResources() {
+		
+		Set<String> result;
+		RangerPolicy policy = null;
+		// null policy
+		result = _validator.getPolicyResources(null);
+		assertTrue(result != null);
+		assertTrue(result.isEmpty());
+		// null resource map
+		policy = mock(RangerPolicy.class);
+		when(policy.getResources()).thenReturn(null);
+		result = _validator.getPolicyResources(null);
+		assertTrue(result != null);
+		assertTrue(result.isEmpty());
+		// empty resource map
+		Map<String, RangerPolicyResource> input = Maps.newHashMap();
+		when(policy.getResources()).thenReturn(input);
+		result = _validator.getPolicyResources(policy);
+		assertTrue(result != null);
+		assertTrue(result.isEmpty());
+		// known resource map
+		input.put("r1", mock(RangerPolicyResource.class));
+		input.put("R2", mock(RangerPolicyResource.class));
+		result = _validator.getPolicyResources(policy);
+		assertEquals(2, result.size());
+		assertTrue("r1", result.contains("r1"));
+		assertTrue("R2", result.contains("r2")); // result should lowercase the resource-names
+	}
+
+	@Test
+	public void test_getIsAuditEnabled() {
+		// null policy
+		RangerPolicy policy = null;
+		boolean result = _validator.getIsAuditEnabled(policy);
+		assertFalse(result);
+		// null isAuditEnabled Boolean is supposed to be TRUE!!
+		policy = mock(RangerPolicy.class);
+		when(policy.getIsAuditEnabled()).thenReturn(null);
+		result = _validator.getIsAuditEnabled(policy);
+		assertTrue(result);
+		// non-null value
+		when(policy.getIsAuditEnabled()).thenReturn(Boolean.FALSE);
+		result = _validator.getIsAuditEnabled(policy);
+		assertFalse(result);
+
+		when(policy.getIsAuditEnabled()).thenReturn(Boolean.TRUE);
+		result = _validator.getIsAuditEnabled(policy);
+		assertTrue(result);
+	}
+
+	@Test
+	public void test_getPolicies() throws Exception {
+
+		// returns null when store returns null
+		String policyName = "aPolicy";
+		String serviceName = "aService";
+		SearchFilter filter = new SearchFilter();
+		filter.setParam(SearchFilter.POLICY_NAME, policyName);
+		filter.setParam(SearchFilter.SERVICE_NAME, serviceName);
+		
+		when(_store.getPolicies(filter)).thenReturn(null);
+		List<RangerPolicy> result = _validator.getPolicies(policyName, serviceName);
+		// validate store is queried with both parameters
+		verify(_store).getPolicies(filter);
+		assertNull(result);
+
+		// returns null if store throws an exception
+		when(_store.getPolicies(filter)).thenThrow(new Exception());
+		result = _validator.getPolicies(policyName, serviceName);
+		assertNull(result);
+	}
+	
+	@Test
+	public void test_getServiceDef_byId() throws Exception {
+		// if service store returns null or throws an exception then service is deemed invalid
+		when(_store.getServiceDef(1L)).thenReturn(null);
+		when(_store.getServiceDef(2L)).thenThrow(new Exception());
+		RangerServiceDef serviceDef = mock(RangerServiceDef.class);
+		when(_store.getServiceDef(3L)).thenReturn(serviceDef);
+		
+		assertNull(_validator.getServiceDef(1L));
+		assertNull(_validator.getServiceDef(2L));
+		assertTrue(_validator.getServiceDef(3L) != null);
+	}
+
+	@Test
+	public void test_getEnumDefaultIndex() {
+		RangerEnumDef enumDef = mock(RangerEnumDef.class);
+		assertEquals(-1, _validator.getEnumDefaultIndex(null));
+		when(enumDef.getDefaultIndex()).thenReturn(null);
+		assertEquals(0, _validator.getEnumDefaultIndex(enumDef));
+		when(enumDef.getDefaultIndex()).thenReturn(-5);
+		assertEquals(-5, _validator.getEnumDefaultIndex(enumDef));
+	}
+	
+	@Test
+	public void test_getImpliedGrants() {
+		
+		// passing in null gets back a null
+		Collection<String> result = _validator.getImpliedGrants(null);
+		assertNull(result);
+		
+		// null or empty implied grant collection gets back an empty collection
+		RangerAccessTypeDef accessTypeDef = mock(RangerAccessTypeDef.class);
+		when(accessTypeDef.getImpliedGrants()).thenReturn(null);
+		result = _validator.getImpliedGrants(accessTypeDef);
+		assertTrue(result.isEmpty());
+		
+		List<String> impliedGrants = new ArrayList<String>();
+		when(accessTypeDef.getImpliedGrants()).thenReturn(impliedGrants);
+		result = _validator.getImpliedGrants(accessTypeDef);
+		assertTrue(result.isEmpty());
+
+		// null/empty values come back as is
+		impliedGrants = Arrays.asList(new String[] { null, "", " ", "		" });
+		when(accessTypeDef.getImpliedGrants()).thenReturn(impliedGrants);
+		result = _validator.getImpliedGrants(accessTypeDef);
+		assertEquals(4, result.size());
+		
+		// non-empty values get lower cased
+		impliedGrants = Arrays.asList(new String[] { "a", "B", "C	", " d " });
+		when(accessTypeDef.getImpliedGrants()).thenReturn(impliedGrants);
+		result = _validator.getImpliedGrants(accessTypeDef);
+		assertEquals(4, result.size());
+		assertTrue(result.contains("a"));
+		assertTrue(result.contains("b"));
+		assertTrue(result.contains("c	"));
+		assertTrue(result.contains(" d "));
+	}
+	
+	private RangerValidatorForTest _validator;
+	private ServiceStore _store;
+	private ValidationTestUtils _utils = new ValidationTestUtils();
+}

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/7bb68687/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/ValidationTestUtils.java
----------------------------------------------------------------------
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/ValidationTestUtils.java b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/ValidationTestUtils.java
new file mode 100644
index 0000000..5ed2691
--- /dev/null
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/ValidationTestUtils.java
@@ -0,0 +1,371 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.ranger.plugin.model.validation;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItem;
+import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemAccess;
+import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource;
+import org.apache.ranger.plugin.model.RangerServiceDef;
+import org.apache.ranger.plugin.model.RangerServiceDef.RangerAccessTypeDef;
+import org.apache.ranger.plugin.model.RangerServiceDef.RangerEnumDef;
+import org.apache.ranger.plugin.model.RangerServiceDef.RangerEnumElementDef;
+import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef;
+import org.apache.ranger.plugin.model.RangerServiceDef.RangerServiceConfigDef;
+import org.apache.ranger.plugin.model.validation.ValidationFailureDetails;
+
+public class ValidationTestUtils {
+	
+	Map<String, String> createMap(String[] keys) {
+		Map<String, String> result = new HashMap<String, String>();
+		for (String key : keys) {
+			result.put(key, "valueof-" + key);
+		}
+		return result;
+	}
+
+	// helper methods for tests
+	List<RangerServiceConfigDef> createServiceConditionDefs(Object[][] input) {
+		List<RangerServiceConfigDef> result = new ArrayList<RangerServiceDef.RangerServiceConfigDef>();
+		
+		for (Object data[] : input) {
+			RangerServiceConfigDef aConfigDef = mock(RangerServiceConfigDef.class);
+			when(aConfigDef.getName()).thenReturn((String)data[0]);
+			when(aConfigDef.getMandatory()).thenReturn((boolean)data[1]);
+			result.add(aConfigDef);
+		}
+		
+		return result;
+	}
+	
+	void checkFailureForSemanticError(List<ValidationFailureDetails> failures, String fieldName) {
+		checkFailure(failures, null, null, true, fieldName, null);
+	}
+
+	void checkFailureForSemanticError(List<ValidationFailureDetails> failures, String fieldName, String subField) {
+		checkFailure(failures, null, null, true, fieldName, subField);
+	}
+
+	void checkFailureForMissingValue(List<ValidationFailureDetails> failures, String field) {
+		checkFailure(failures, null, true, null, field, null);
+	}
+
+	void checkFailureForMissingValue(List<ValidationFailureDetails> failures, String field, String subField) {
+		checkFailure(failures, null, true, null, field, subField);
+	}
+
+	void checkFailureForInternalError(List<ValidationFailureDetails> failures, String fieldName) {
+		checkFailure(failures, true, null, null, fieldName, null);
+	}
+
+	void checkFailureForInternalError(List<ValidationFailureDetails> failures) {
+		checkFailure(failures, true, null, null, null, null);
+	}
+
+	void checkFailure(List<ValidationFailureDetails> failures, Boolean internalError, Boolean missing, Boolean semanticError, String field, String subField) {
+		if (CollectionUtils.isEmpty(failures)) {
+			fail("List of failures is null/empty!");
+		} else {
+			boolean found = false;
+			for (ValidationFailureDetails f : failures) {
+				if ((internalError == null || internalError == f._internalError) &&
+						(missing == null || missing == f._missing) &&
+						(semanticError == null || semanticError == f._semanticError) &&
+						(field == null || field.equals(f._fieldName)) &&
+						(subField == null || subField.equals(f._subFieldName))) {
+					found = true;
+				}
+			}
+			assertTrue(found);
+		}
+	}
+
+	List<RangerAccessTypeDef> createAccessTypeDefs(String[] names) {
+		assertFalse(names == null); // fail if null is passed in!
+		List<RangerAccessTypeDef> defs = new ArrayList<RangerServiceDef.RangerAccessTypeDef>();
+		for (String name : names) {
+			RangerAccessTypeDef def = mock(RangerAccessTypeDef.class);
+			when(def.getName()).thenReturn(name);
+			defs.add(def);
+		}
+		return defs;
+	}
+
+
+	List<RangerAccessTypeDef> createAccessTypeDefs(Object[][] data) {
+		if (data == null) {
+			return null;
+		}
+		List<RangerAccessTypeDef> result = new ArrayList<RangerAccessTypeDef>();
+		if (data.length == 0) {
+			return result;
+		}
+		for (Object[] entry : data) {
+			String accessType = (String)entry[0];
+			String[] impliedAccessArray = (String[])entry[1];
+			List<String> impliedAccesses = null;
+			if (impliedAccessArray != null) {
+				impliedAccesses = Arrays.asList(impliedAccessArray);
+			}
+			RangerAccessTypeDef aTypeDef = mock(RangerAccessTypeDef.class);
+			when(aTypeDef.getName()).thenReturn(accessType);
+			when(aTypeDef.getImpliedGrants()).thenReturn(impliedAccesses);
+			result.add(aTypeDef);
+		}
+		return result;
+	}
+
+	RangerServiceDef createServiceDefWithAccessTypes(String[] accesses) {
+		RangerServiceDef serviceDef = mock(RangerServiceDef.class);
+		List<RangerAccessTypeDef> accessTypeDefs = new ArrayList<RangerServiceDef.RangerAccessTypeDef>();
+		for (String access : accesses) {
+			RangerAccessTypeDef accessTypeDef = mock(RangerAccessTypeDef.class);
+			when(accessTypeDef.getName()).thenReturn(access);
+			accessTypeDefs.add(accessTypeDef);
+		}
+		when(serviceDef.getAccessTypes()).thenReturn(accessTypeDefs);
+		return serviceDef;
+	}
+
+	List<RangerPolicyItemAccess> createItemAccess(Object[][] data) {
+		List<RangerPolicyItemAccess> accesses = new ArrayList<RangerPolicyItemAccess>();
+		for (Object[] row : data) {
+			RangerPolicyItemAccess access = mock(RangerPolicyItemAccess.class);
+			when(access.getType()).thenReturn((String)row[0]);
+			when(access.getIsAllowed()).thenReturn((Boolean)row[1]);
+			accesses.add(access);
+		}
+		return accesses;
+	}
+
+	List<RangerPolicyItem> createPolicyItems(Object[] data) {
+		List<RangerPolicyItem> policyItems = new ArrayList<RangerPolicyItem>();
+		for (Object object : data) {
+			@SuppressWarnings("unchecked")
+			Map<String, Object[]> map = (Map<String, Object[]>) object; 
+			RangerPolicyItem policyItem = mock(RangerPolicyItem.class);
+			
+			List<String> usersList = null;
+			if (map.containsKey("users")) {
+				usersList = Arrays.asList((String[])map.get("users"));
+			}
+			when(policyItem.getUsers()).thenReturn(usersList);
+			
+			List<String> groupsList = null;
+			if (map.containsKey("groups")) {
+				groupsList = Arrays.asList((String[])map.get("groups"));
+			}
+			when(policyItem.getGroups()).thenReturn(groupsList);
+			
+			String[] accesses = (String[])map.get("accesses");;
+			Boolean[] isAllowedFlags = (Boolean[])map.get("isAllowed");
+			List<RangerPolicyItemAccess> accessesList = null;
+			if (accesses != null && isAllowedFlags != null) {
+				accessesList = new ArrayList<RangerPolicyItemAccess>();
+				for (int i = 0; i < accesses.length; i++) {
+					String access = accesses[i];
+					Boolean isAllowed = isAllowedFlags[i]; 
+					RangerPolicyItemAccess itemAccess = mock(RangerPolicyItemAccess.class);
+					when(itemAccess.getType()).thenReturn(access);
+					when(itemAccess.getIsAllowed()).thenReturn(isAllowed);
+					accessesList.add(itemAccess);
+				}
+			}
+			when(policyItem.getAccesses()).thenReturn(accessesList);
+			
+			policyItems.add(policyItem);
+		}
+		return policyItems;
+	}
+
+	List<RangerResourceDef> createResourceDefs(Object[][] data) {
+		// if data itself is null then return null back
+		if (data == null) {
+			return null;
+		}
+		List<RangerResourceDef> defs = new ArrayList<RangerResourceDef>();
+		for (Object[] row : data) {
+			RangerResourceDef aDef = null;
+			if (row != null) {
+				String name = null;
+				Boolean mandatory = null;
+				String regExPattern = null;
+				Boolean isExcludesSupported = null;
+				Boolean isRecursiveSupported = null;
+				switch(row.length) {
+				case 5:
+					isRecursiveSupported = (Boolean)row[4];
+				case 4:
+					isExcludesSupported = (Boolean)row[3];
+				case 3:
+					regExPattern = (String)row[2];
+				case 2:
+					mandatory = (Boolean)row[1];
+				case 1:
+					name = (String)row[0];
+				}
+				aDef = mock(RangerResourceDef.class);
+				when(aDef.getName()).thenReturn(name);
+				when(aDef.getMandatory()).thenReturn(mandatory);
+				when(aDef.getValidationRegEx()).thenReturn(regExPattern);
+				when(aDef.getExcludesSupported()).thenReturn(isExcludesSupported);
+				when(aDef.getRecursiveSupported()).thenReturn(isRecursiveSupported);
+			}
+			defs.add(aDef);
+		}
+		return defs;
+	}
+
+	List<RangerResourceDef> createResourceDefs2(Object[][] data) {
+		// if data itself is null then return null back
+		if (data == null) {
+			return null;
+		}
+		List<RangerResourceDef> defs = new ArrayList<RangerResourceDef>();
+		for (Object[] row : data) {
+			RangerResourceDef aDef = null;
+			if (row != null) {
+				String name = null;
+				Boolean isExcludesSupported = null;
+				Boolean isRecursiveSupported = null;
+				switch(row.length) {
+				case 3:
+					isRecursiveSupported = (Boolean)row[2]; // note: falls through to next case
+				case 2:
+					isExcludesSupported = (Boolean)row[1]; // note: falls through to next case
+				case 1:
+					name = (String)row[0];
+				}
+				aDef = mock(RangerResourceDef.class);
+				when(aDef.getName()).thenReturn(name);
+				when(aDef.getExcludesSupported()).thenReturn(isExcludesSupported);
+				when(aDef.getRecursiveSupported()).thenReturn(isRecursiveSupported);
+			}
+			defs.add(aDef);
+		}
+		return defs;
+	}
+
+	List<RangerResourceDef> createResourceDefsWithRegEx(String[][] data) {
+		// if data itself is null then return null back
+		if (data == null) {
+			return null;
+		}
+		List<RangerResourceDef> defs = new ArrayList<RangerResourceDef>();
+		for (String[] row : data) {
+			RangerResourceDef aDef = null;
+			if (row != null) {
+				String name = row[0];
+				String regEx = row[1];
+				aDef = mock(RangerResourceDef.class);
+				when(aDef.getName()).thenReturn(name);
+				when(aDef.getValidationRegEx()).thenReturn(regEx);
+			}
+			defs.add(aDef);
+		}
+		return defs;
+	}
+
+	Map<String, RangerPolicyResource> createPolicyResourceMap2(Object[][] input) {
+		if (input == null) {
+			return null;
+		}
+		Map<String, RangerPolicyResource> result = new HashMap<String, RangerPolicyResource>(input.length);
+		for (Object[] row : input) {
+			String resourceName = (String)row[0];
+			Boolean isExcludes = (Boolean)row[1];
+			Boolean isRecursive = (Boolean)row[2];
+			RangerPolicyResource aResource = mock(RangerPolicyResource.class);
+			when(aResource.getIsExcludes()).thenReturn(isExcludes);
+			when(aResource.getIsRecursive()).thenReturn(isRecursive);
+			result.put(resourceName, aResource);
+		}
+		return result;
+	}
+
+	List<RangerEnumElementDef> createEnumElementDefs(String[] input) {
+		if (input == null) {
+			return null;
+		}
+		List<RangerEnumElementDef> output = new ArrayList<RangerEnumElementDef>();
+		for (String elementName : input) {
+			RangerEnumElementDef aDef = mock(RangerEnumElementDef.class);
+			when(aDef.getName()).thenReturn(elementName);
+			output.add(aDef);
+		}
+		return output;
+	}
+
+	List<RangerEnumDef> createEnumDefs(Map<String, String[]> input) {
+		if (input == null) {
+			return null;
+		}
+		List<RangerEnumDef> defs = new ArrayList<RangerEnumDef>();
+		for (Map.Entry<String, String[]> entry : input.entrySet()) {
+			RangerEnumDef enumDef = mock(RangerEnumDef.class);
+			String enumName = entry.getKey();
+			if ("null".equals(enumName)) { // special handling to process null hint in enum-name
+				enumName = null;
+			}
+			when(enumDef.getName()).thenReturn(enumName);
+			List<RangerEnumElementDef> elements = createEnumElementDefs(entry.getValue());
+			when(enumDef.getElements()).thenReturn(elements);
+			// by default set default index to last element
+			when(enumDef.getDefaultIndex()).thenReturn(elements.size() - 1);
+			defs.add(enumDef);
+		}
+		return defs;
+	}
+
+	Map<String, RangerPolicyResource> createPolicyResourceMap(Object[][] input) {
+		if (input == null) {
+			return null;
+		}
+		Map<String, RangerPolicyResource> result = new HashMap<String, RangerPolicyResource>(input.length);
+		for (Object[] row : input) {
+			String resourceName = (String)row[0];
+			String[] valuesArray = (String[])row[1];
+			Boolean isExcludes = (Boolean)row[2];
+			Boolean isRecursive = (Boolean)row[3];
+			RangerPolicyResource aResource = mock(RangerPolicyResource.class);
+			if (valuesArray == null) {
+				when(aResource.getValues()).thenReturn(null);
+			} else {
+				when(aResource.getValues()).thenReturn(Arrays.asList(valuesArray));
+			}
+			when(aResource.getIsExcludes()).thenReturn(isExcludes);
+			when(aResource.getIsRecursive()).thenReturn(isRecursive);
+			result.put(resourceName, aResource);
+		}
+		return result;
+	}
+}


Mime
View raw message