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