/* * 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.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.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.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.Action; import org.apache.ranger.plugin.store.ServiceStore; import org.apache.ranger.plugin.util.SearchFilter; import org.junit.Assert; 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; return "valid".equals(behavior); } } @Before public void before() { _store = mock(ServiceStore.class); _validator = new RangerValidatorForTest(_store); _failures = new ArrayList<>(); } @Test public void test_ctor_firewalling() { try { // service store can't be null during construction new RangerValidatorForTest(null); Assert.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); Assert.fail("Should have thrown exception!"); } catch (Exception e) { // ok expected exception String message = e.getMessage(); Assert.assertTrue(message.contains("internal error")); } } @Test public void test_getServiceConfigParameters() { // reasonable protection against null values Set<String> parameters = _validator.getServiceConfigParameters(null); Assert.assertNotNull(parameters); Assert.assertTrue(parameters.isEmpty()); RangerService service = mock(RangerService.class); when(service.getConfigs()).thenReturn(null); parameters = _validator.getServiceConfigParameters(service); Assert.assertNotNull(parameters); Assert.assertTrue(parameters.isEmpty()); when(service.getConfigs()).thenReturn(new HashMap<String, String>()); parameters = _validator.getServiceConfigParameters(service); Assert.assertNotNull(parameters); Assert.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) { Assert.assertTrue("key", parameters.contains(key)); } } @Test public void test_getRequiredParameters() { // reasonable protection against null things Set<String> parameters = _validator.getRequiredParameters(null); Assert.assertNotNull(parameters); Assert.assertTrue(parameters.isEmpty()); RangerServiceDef serviceDef = mock(RangerServiceDef.class); when(serviceDef.getConfigs()).thenReturn(null); parameters = _validator.getRequiredParameters(null); Assert.assertNotNull(parameters); Assert.assertTrue(parameters.isEmpty()); List<RangerServiceConfigDef> configs = new ArrayList<>(); when(serviceDef.getConfigs()).thenReturn(configs); parameters = _validator.getRequiredParameters(null); Assert.assertNotNull(parameters); Assert.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); Assert.assertTrue("result does not contain: param2", parameters.contains("param2")); Assert.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(); Assert.fail("Unexpected exception during mocking!"); } Assert.assertNull(_validator.getServiceDef("return null")); Assert.assertNull(_validator.getServiceDef("throw")); Assert.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); Assert.assertNull(_validator.getPolicy(1L)); Assert.assertNull(_validator.getPolicy(2L)); Assert.assertTrue(_validator.getPolicy(3L) != null); } @Test public final void test_getPoliciesForResourceSignature() throws Exception { // return null if store returns null or throws an exception String hexSignature = "aSignature"; String serviceName = "service-name"; boolean isPolicyEnabled = true; when(_store.getPoliciesByResourceSignature(serviceName, hexSignature, isPolicyEnabled)).thenReturn(null); Assert.assertNull(_validator.getPoliciesForResourceSignature(serviceName, hexSignature)); when(_store.getPoliciesByResourceSignature(serviceName, hexSignature, isPolicyEnabled)).thenThrow(new Exception()); Assert.assertNull(_validator.getPoliciesForResourceSignature(serviceName, hexSignature)); // what ever store returns should come back hexSignature = "anotherSignature"; List<RangerPolicy> policies = new ArrayList<>(); RangerPolicy policy1 = mock(RangerPolicy.class); policies.add(policy1); RangerPolicy policy2 = mock(RangerPolicy.class); policies.add(policy2); when(_store.getPoliciesByResourceSignature(serviceName, hexSignature, isPolicyEnabled)).thenReturn(policies); List<RangerPolicy> result = _validator.getPoliciesForResourceSignature(serviceName, hexSignature); Assert.assertTrue(result.contains(policy1) && result.contains(policy2)); } @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); Assert.assertNull(_validator.getService(1L)); Assert.assertNull(_validator.getService(2L)); Assert.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(); Assert.fail("Unexpected exception during mocking!"); } Assert.assertNull(_validator.getService("return null")); Assert.assertNull(_validator.getService("throw")); Assert.assertFalse(_validator.getService("good-service") == null); } @Test public void test_getAccessTypes() { // passing in null service def Set<String> accessTypes = _validator.getAccessTypes((RangerServiceDef)null); Assert.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); Assert.assertTrue(accessTypes.isEmpty()); List<RangerAccessTypeDef> accessTypeDefs = new ArrayList<>(); when(serviceDef.getAccessTypes()).thenReturn(accessTypeDefs); accessTypes = _validator.getAccessTypes(serviceDef); Assert.assertTrue(accessTypes.isEmpty()); // having null accesstypedefs accessTypeDefs.add(null); accessTypes = _validator.getAccessTypes(serviceDef); Assert.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); Assert.assertEquals(4, accessTypes.size()); Assert.assertTrue(accessTypes.contains("a")); Assert.assertTrue(accessTypes.contains("b ")); Assert.assertTrue(accessTypes.contains(" c")); Assert.assertTrue(accessTypes.contains(" d ")); } @Test public void test_getResourceNames() { // passing in null service def Set<String> accessTypes = _validator.getMandatoryResourceNames((RangerServiceDef)null); Assert.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); Assert.assertTrue(accessTypes.isEmpty()); List<RangerResourceDef> resourceDefs = new ArrayList<>(); when(serviceDef.getResources()).thenReturn(resourceDefs); accessTypes = _validator.getMandatoryResourceNames(serviceDef); Assert.assertTrue(accessTypes.isEmpty()); // having null accesstypedefs resourceDefs.add(null); accessTypes = _validator.getMandatoryResourceNames(serviceDef); Assert.assertTrue(accessTypes.isEmpty()); // access type defs with null empty blank names are skipped, spaces within names are preserved Object[][] data = { // { name, excludes recursive mandatory, reg-exp, parent-level } // Supported?, Supported?, { "a", null, null, true }, // all good null, // this should put a null element in the resource def! { "b", null, null, null }, // mandatory field is null, i.e. false { "c", null, null, false }, // non-mandatory field false - upper case { "D", null, null, true }, // resource specified in upper case { "E", null, null, false }, // all good }; resourceDefs.addAll(_utils.createResourceDefs(data)); accessTypes = _validator.getMandatoryResourceNames(serviceDef); Assert.assertEquals(2, accessTypes.size()); Assert.assertTrue(accessTypes.contains("a")); Assert.assertTrue(accessTypes.contains("d")); // name should come back lower case accessTypes = _validator.getAllResourceNames(serviceDef); Assert.assertEquals(5, accessTypes.size()); Assert.assertTrue(accessTypes.contains("b")); Assert.assertTrue(accessTypes.contains("c")); Assert.assertTrue(accessTypes.contains("e")); } @Test public void test_getValidationRegExes() { // passing in null service def Map<String, String> regExMap = _validator.getValidationRegExes((RangerServiceDef)null); Assert.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); Assert.assertTrue(regExMap.isEmpty()); List<RangerResourceDef> resourceDefs = new ArrayList<>(); when(serviceDef.getResources()).thenReturn(resourceDefs); regExMap = _validator.getValidationRegExes(serviceDef); Assert.assertTrue(regExMap.isEmpty()); // having null accesstypedefs resourceDefs.add(null); regExMap = _validator.getValidationRegExes(serviceDef); Assert.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); Assert.assertEquals(3, regExMap.size()); Assert.assertEquals("regex1", regExMap.get("b")); Assert.assertEquals("regex2", regExMap.get("d")); Assert.assertEquals("regex3", regExMap.get("f")); } @Test public void test_getPolicyResources() { Set<String> result; RangerPolicy policy = null; // null policy result = _validator.getPolicyResources(null); Assert.assertTrue(result != null); Assert.assertTrue(result.isEmpty()); // null resource map policy = mock(RangerPolicy.class); when(policy.getResources()).thenReturn(null); result = _validator.getPolicyResources(null); Assert.assertTrue(result != null); Assert.assertTrue(result.isEmpty()); // empty resource map Map<String, RangerPolicyResource> input = Maps.newHashMap(); when(policy.getResources()).thenReturn(input); result = _validator.getPolicyResources(policy); Assert.assertTrue(result != null); Assert.assertTrue(result.isEmpty()); // known resource map input.put("r1", mock(RangerPolicyResource.class)); input.put("R2", mock(RangerPolicyResource.class)); result = _validator.getPolicyResources(policy); Assert.assertEquals(2, result.size()); Assert.assertTrue("r1", result.contains("r1")); Assert.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); Assert.assertFalse(result); // null isAuditEnabled Boolean is supposed to be TRUE!! policy = mock(RangerPolicy.class); when(policy.getIsAuditEnabled()).thenReturn(null); result = _validator.getIsAuditEnabled(policy); Assert.assertTrue(result); // non-null value when(policy.getIsAuditEnabled()).thenReturn(Boolean.FALSE); result = _validator.getIsAuditEnabled(policy); Assert.assertFalse(result); when(policy.getIsAuditEnabled()).thenReturn(Boolean.TRUE); result = _validator.getIsAuditEnabled(policy); Assert.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(serviceName, policyName); // validate store is queried with both parameters verify(_store).getPolicies(filter); Assert.assertNull(result); // returns null if store throws an exception when(_store.getPolicies(filter)).thenThrow(new Exception()); result = _validator.getPolicies(serviceName, policyName); Assert.assertNull(result); // does not shove policy into search filter if policy name passed in is "blank" filter = new SearchFilter(); filter.setParam(SearchFilter.SERVICE_NAME, serviceName); List<RangerPolicy> policies = new ArrayList<>(); RangerPolicy policy = mock(RangerPolicy.class); policies.add(policy); when(_store.getPolicies(filter)).thenReturn(policies); for (String aName : new String[]{ null, "", " "}) { result = _validator.getPolicies(serviceName, aName); Assert.assertTrue(result.iterator().next() == policy); } } @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); Assert.assertNull(_validator.getServiceDef(1L)); Assert.assertNull(_validator.getServiceDef(2L)); Assert.assertTrue(_validator.getServiceDef(3L) != null); } @Test public void test_getEnumDefaultIndex() { RangerEnumDef enumDef = mock(RangerEnumDef.class); Assert.assertEquals(-1, _validator.getEnumDefaultIndex(null)); when(enumDef.getDefaultIndex()).thenReturn(null); Assert.assertEquals(0, _validator.getEnumDefaultIndex(enumDef)); when(enumDef.getDefaultIndex()).thenReturn(-5); Assert.assertEquals(-5, _validator.getEnumDefaultIndex(enumDef)); } @Test public void test_getImpliedGrants() { // passing in null gets back a null Collection<String> result = _validator.getImpliedGrants(null); Assert.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); Assert.assertTrue(result.isEmpty()); List<String> impliedGrants = new ArrayList<>(); when(accessTypeDef.getImpliedGrants()).thenReturn(impliedGrants); result = _validator.getImpliedGrants(accessTypeDef); Assert.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); Assert.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); Assert.assertEquals(4, result.size()); Assert.assertTrue(result.contains("a")); Assert.assertTrue(result.contains("b")); Assert.assertTrue(result.contains("c ")); Assert.assertTrue(result.contains(" d ")); } @Test public void test_isValid_string() { String fieldName = "value-field-Name"; String collectionName = "value-collection-Name"; Set<String> alreadySeen = new HashSet<>(); // null/empty string value is invalid for (String value : new String[] { null, "", " " }) { Assert.assertFalse(_validator.isUnique(value, alreadySeen, fieldName, collectionName, _failures)); _utils.checkFailureForMissingValue(_failures, fieldName); } // value should not have been seen so far. String value = "blah"; _failures.clear(); Assert.assertTrue(_validator.isUnique(value, alreadySeen, fieldName, collectionName, _failures)); Assert.assertTrue(_failures.isEmpty()); Assert.assertTrue(alreadySeen.contains(value)); // since "blah" has already been seen doing this test again should fail _failures.clear(); Assert.assertFalse(_validator.isUnique(value, alreadySeen, fieldName, collectionName, _failures)); _utils.checkFailureForSemanticError(_failures, fieldName, value); // not see check is done in a case-insenstive manner value = "bLaH"; _failures.clear(); Assert.assertFalse(_validator.isUnique(value, alreadySeen, fieldName, collectionName, _failures)); _utils.checkFailureForSemanticError(_failures, fieldName, value); } @Test public void test_isValid_long() { String fieldName = "field-Name"; String collectionName = "field-collection-Name"; Set<Long> alreadySeen = new HashSet<>(); Long value = null; // null value is invalid Assert.assertFalse(_validator.isUnique(value, alreadySeen, fieldName, collectionName, _failures)); _utils.checkFailureForMissingValue(_failures, fieldName); // value should not have been seen so far. value = 7L; _failures.clear(); Assert.assertTrue(_validator.isUnique(value, alreadySeen, fieldName, collectionName, _failures)); Assert.assertTrue(_failures.isEmpty()); Assert.assertTrue(alreadySeen.contains(value)); // since 7L has already been seen doing this test again should fail _failures.clear(); Assert.assertFalse(_validator.isUnique(value, alreadySeen, fieldName, collectionName, _failures)); _utils.checkFailureForSemanticError(_failures, fieldName, value.toString()); } @Test public void test_isValid_integer() { String fieldName = "field-Name"; String collectionName = "field-collection-Name"; Set<Integer> alreadySeen = new HashSet<>(); Integer value = null; // null value is invalid Assert.assertFalse(_validator.isUnique(value, alreadySeen, fieldName, collectionName, _failures)); _utils.checkFailureForMissingValue(_failures, fieldName); // value should not have been seen so far. value = 49; _failures.clear(); Assert.assertTrue(_validator.isUnique(value, alreadySeen, fieldName, collectionName, _failures)); Assert.assertTrue(_failures.isEmpty()); Assert.assertTrue(alreadySeen.contains(value)); // since 7L has already been seen doing this test again should fail _failures.clear(); Assert.assertFalse(_validator.isUnique(value, alreadySeen, fieldName, collectionName, _failures)); _utils.checkFailureForSemanticError(_failures, fieldName, value.toString()); } private RangerValidatorForTest _validator; private ServiceStore _store; private ValidationTestUtils _utils = new ValidationTestUtils(); private List<ValidationFailureDetails> _failures; }