/* * 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.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.HashSet; import java.util.List; import java.util.Set; import org.apache.ranger.plugin.model.RangerPolicy; import org.apache.ranger.plugin.model.RangerServiceDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef; import org.apache.ranger.plugin.model.validation.RangerServiceDefHelper.Delegate; import org.junit.Before; import org.junit.Test; import com.google.common.collect.Lists; public class TestRangerServiceDefHelper { @Before public void before() { _serviceDef = mock(RangerServiceDef.class); when(_serviceDef.getName()).thenReturn("a-service-def"); // wipe the cache clean RangerServiceDefHelper._Cache.clear(); } @Test public void test_getResourceHierarchies() { /* * Create a service-def with following resource graph * * Database -> UDF * | * v * Table -> Column * | * v * Table-Attribute * * It contains following hierarchies * - [ Database UDF] * - [ Database Table Column ] * - [ Database Table Table-Attribute ] */ RangerResourceDef Database = createResourceDef("Database", ""); RangerResourceDef UDF = createResourceDef("UDF", "Database"); RangerResourceDef Table = createResourceDef("Table", "Database"); RangerResourceDef Column = createResourceDef("Column", "Table"); RangerResourceDef Table_Atrribute = createResourceDef("Table-Attribute", "Table"); // order of resources in list sould not matter List<RangerResourceDef> resourceDefs = Lists.newArrayList(Column, Database, Table, Table_Atrribute, UDF); // stuff this into a service-def when(_serviceDef.getResources()).thenReturn(resourceDefs); // now assert the behavior _helper = new RangerServiceDefHelper(_serviceDef); assertTrue(_helper.isResourceGraphValid()); Set<List<RangerResourceDef>> hierarchies = _helper.getResourceHierarchies(RangerPolicy.POLICY_TYPE_ACCESS); // there should be List<RangerResourceDef> hierarchy = Lists.newArrayList(Database, UDF); assertTrue(hierarchies.contains(hierarchy)); hierarchy = Lists.newArrayList(Database, Table, Column); assertTrue(hierarchies.contains(hierarchy)); hierarchy = Lists.newArrayList(Database, Table, Table_Atrribute); assertTrue(hierarchies.contains(hierarchy)); } @Test public final void test_isResourceGraphValid_detectCycle() { /* * Create a service-def with cycles in resource graph * A --> B --> C * ^ | * | | * |---- D <--- */ RangerResourceDef A = createResourceDef("A", "D"); // A's parent is D, etc. RangerResourceDef B = createResourceDef("B", "C"); RangerResourceDef C = createResourceDef("C", "D"); RangerResourceDef D = createResourceDef("D", "A"); // order of resources in list sould not matter List<RangerResourceDef> resourceDefs = Lists.newArrayList(A, B, C, D); when(_serviceDef.getResources()).thenReturn(resourceDefs); _helper = new RangerServiceDefHelper(_serviceDef); assertFalse("Graph was valid!", _helper.isResourceGraphValid()); } @Test public final void test_isResourceGraphValid_forest() { /* * Create a service-def which is a forest * Database -> Table-space * | * v * Table -> Column * * Namespace -> package * | * v * function * * Check that helper corrects reports back all of the hierarchies: levels in it and their order. */ RangerResourceDef database = createResourceDef("database", ""); RangerResourceDef tableSpace = createResourceDef("table-space", "database"); RangerResourceDef table = createResourceDef("table", "database"); RangerResourceDef column = createResourceDef("column", "table"); RangerResourceDef namespace = createResourceDef("namespace", ""); RangerResourceDef function = createResourceDef("function", "namespace"); RangerResourceDef Package = createResourceDef("package", "namespace"); List<RangerResourceDef> resourceDefs = Lists.newArrayList(database, tableSpace, table, column, namespace, function, Package); when(_serviceDef.getResources()).thenReturn(resourceDefs); _helper = new RangerServiceDefHelper(_serviceDef); assertTrue(_helper.isResourceGraphValid()); Set<List<RangerResourceDef>> hierarchies = _helper.getResourceHierarchies(RangerPolicy.POLICY_TYPE_ACCESS); Set<List<String>> expectedHierarchies = new HashSet<>(); expectedHierarchies.add(Lists.newArrayList("database", "table-space")); expectedHierarchies.add(Lists.newArrayList("database", "table", "column")); expectedHierarchies.add(Lists.newArrayList("namespace", "package")); expectedHierarchies.add(Lists.newArrayList("namespace", "function")); for (List<RangerResourceDef> aHierarchy : hierarchies) { List<String> resourceNames = _helper.getAllResourceNamesOrdered(aHierarchy); assertTrue(expectedHierarchies.contains(resourceNames)); expectedHierarchies.remove(resourceNames); } assertTrue("Missing hierarchies: " + expectedHierarchies.toString(), expectedHierarchies.isEmpty()); // make sure we got back all hierarchies } @Test public final void test_isResourceGraphValid_forest_singleNodeTrees() { /* * Create a service-def which is a forest with a few single node trees * * Database * * Server * * Namespace -> package * | * v * function * * Check that helper corrects reports back all of the hierarchies: levels in it and their order. */ RangerResourceDef database = createResourceDef("database", ""); RangerResourceDef server = createResourceDef("server", ""); RangerResourceDef namespace = createResourceDef("namespace", ""); RangerResourceDef function = createResourceDef("function", "namespace"); RangerResourceDef Package = createResourceDef("package", "namespace"); List<RangerResourceDef> resourceDefs = Lists.newArrayList(database, server, namespace, function, Package); when(_serviceDef.getResources()).thenReturn(resourceDefs); _helper = new RangerServiceDefHelper(_serviceDef); assertTrue(_helper.isResourceGraphValid()); Set<List<RangerResourceDef>> hierarchies = _helper.getResourceHierarchies(RangerPolicy.POLICY_TYPE_ACCESS); Set<List<String>> expectedHierarchies = new HashSet<>(); expectedHierarchies.add(Lists.newArrayList("database")); expectedHierarchies.add(Lists.newArrayList("server")); expectedHierarchies.add(Lists.newArrayList("namespace", "package")); expectedHierarchies.add(Lists.newArrayList("namespace", "function")); for (List<RangerResourceDef> aHierarchy : hierarchies) { List<String> resourceNames = _helper.getAllResourceNamesOrdered(aHierarchy); assertTrue(expectedHierarchies.contains(resourceNames)); expectedHierarchies.remove(resourceNames); } assertTrue("Missing hierarchies: " + expectedHierarchies.toString(), expectedHierarchies.isEmpty()); // make sure we got back all hierarchies } @Test public final void test_cacheBehavior() { // wipe the cache clean RangerServiceDefHelper._Cache.clear(); // let's add one entry to the cache Delegate delegate = mock(Delegate.class); Date aDate = getNow(); String serviceName = "a-service-def"; when(delegate.getServiceFreshnessDate()).thenReturn(aDate); when(delegate.getServiceName()).thenReturn(serviceName); RangerServiceDefHelper._Cache.put(serviceName, delegate); // create a service def with matching date value _serviceDef = mock(RangerServiceDef.class); when(_serviceDef.getName()).thenReturn(serviceName); when(_serviceDef.getUpdateTime()).thenReturn(aDate); // since cache has it, we should get back the one that we have added _helper = new RangerServiceDefHelper(_serviceDef); assertTrue("Didn't get back the same object that was put in cache", delegate == _helper._delegate); // if we change the date then that should force helper to create a new delegate instance /* * NOTE:: current logic would replace the cache instance even if the one in the cache is newer. This is not likely to happen but it is important to call this out * as in rare cases one may end up creating re creating delegate if threads have stale copies of service def. */ when(_serviceDef.getUpdateTime()).thenReturn(getLastMonth()); _helper = new RangerServiceDefHelper(_serviceDef); assertTrue("Didn't get a delegate different than what was put in the cache", delegate != _helper._delegate); // now that a new instance was added to the cache let's ensure that it got added to the cache Delegate newDelegate = _helper._delegate; _helper = new RangerServiceDefHelper(_serviceDef); assertTrue("Didn't get a delegate different than what was put in the cache", newDelegate == _helper._delegate); } RangerResourceDef createResourceDef(String name, String parent) { RangerResourceDef resourceDef = mock(RangerResourceDef.class); when(resourceDef.getName()).thenReturn(name); when(resourceDef.getParent()).thenReturn(parent); return resourceDef; } Date getLastMonth() { Calendar cal = GregorianCalendar.getInstance(); cal.add( Calendar.MONTH, 1); Date lastMonth = cal.getTime(); return lastMonth; } Date getNow() { Calendar cal = GregorianCalendar.getInstance(); Date now = cal.getTime(); return now; } private RangerServiceDef _serviceDef; private RangerServiceDefHelper _helper; }