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;
+       }
+}

Reply via email to