/* (c) 2017 Open Source Geospatial Foundation - all rights reserved * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.security.impl; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.*; import java.io.File; import java.net.URL; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Set; import java.util.stream.Collectors; import javax.xml.namespace.QName; import org.geoserver.catalog.CascadeDeleteVisitor; import org.geoserver.catalog.Catalog; import org.geoserver.catalog.CatalogBuilder; import org.geoserver.catalog.DataStoreInfo; import org.geoserver.catalog.FeatureTypeInfo; import org.geoserver.catalog.LayerGroupInfo; import org.geoserver.catalog.LayerGroupInfo.Mode; import org.geoserver.catalog.LayerInfo; import org.geoserver.catalog.NamespaceInfo; import org.geoserver.catalog.PublishedInfo; import org.geoserver.catalog.WorkspaceInfo; import org.geoserver.catalog.impl.CatalogImpl; import org.geoserver.catalog.impl.NamespaceInfoImpl; import org.geoserver.catalog.impl.WorkspaceInfoImpl; import org.geoserver.data.test.MockData; import org.geoserver.platform.GeoServerResourceLoader; import org.geoserver.security.impl.LayerGroupContainmentCache.LayerGroupSummary; import org.geotools.data.DataUtilities; import org.geotools.data.property.PropertyDataStore; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.opengis.feature.type.Name; /** * Tests {@link LayerGroupContainmentCache} udpates in face of catalog setup and changes */ public class LayerGroupContainmentCacheTest { private static final String WS = "ws"; private static final String ANOTHER_WS = "anotherWs"; private static final String NATURE_GROUP = "nature"; private static final String CONTAINER_GROUP = "containerGroup"; private LayerGroupContainmentCache cc; private LayerGroupInfo nature; private LayerGroupInfo container; private static Catalog catalog; @BeforeClass public static void setupBaseCatalog() throws Exception { catalog = new CatalogImpl(); catalog.setResourceLoader(new GeoServerResourceLoader()); // the workspace addWorkspaceNamespace(WS); addWorkspaceNamespace(ANOTHER_WS); // the builder CatalogBuilder cb = new CatalogBuilder(catalog); final WorkspaceInfo defaultWorkspace = catalog.getDefaultWorkspace(); cb.setWorkspace(defaultWorkspace); // setup the store String nsURI = catalog.getDefaultNamespace().getURI(); URL buildings = MockData.class.getResource("Buildings.properties"); File testData = DataUtilities.urlToFile(buildings).getParentFile(); DataStoreInfo storeInfo = cb.buildDataStore("store"); storeInfo.getConnectionParameters().put("directory", testData); storeInfo.getConnectionParameters().put("namespace", nsURI); catalog.save(storeInfo); // setup all the layers PropertyDataStore store = new PropertyDataStore(testData); store.setNamespaceURI(nsURI); cb.setStore(catalog.getDefaultDataStore(defaultWorkspace)); for (Name name : store.getNames()) { FeatureTypeInfo ft = cb.buildFeatureType(name); cb.setupBounds(ft); catalog.add(ft); LayerInfo layer = cb.buildLayer(ft); catalog.add(layer); } } private static void addWorkspaceNamespace(String wsName) { WorkspaceInfoImpl ws = new WorkspaceInfoImpl(); ws.setName(wsName); catalog.add(ws); NamespaceInfo ns = new NamespaceInfoImpl(); ns.setPrefix(wsName); ns.setURI("http://www.geoserver.org/" + wsName); catalog.add(ns); } @Before public void setupLayerGrups() throws Exception { LayerInfo lakes = catalog.getLayerByName(getLayerId(MockData.LAKES)); LayerInfo forests = catalog.getLayerByName(getLayerId(MockData.FORESTS)); LayerInfo roads = catalog.getLayerByName(getLayerId(MockData.ROAD_SEGMENTS)); WorkspaceInfo ws = catalog.getDefaultWorkspace(); this.nature = addLayerGroup(NATURE_GROUP, Mode.SINGLE, ws, lakes, forests); this.container = addLayerGroup(CONTAINER_GROUP, Mode.CONTAINER, null, nature, roads); cc = new LayerGroupContainmentCache(catalog); } @After public void clearLayerGroups() throws Exception { CascadeDeleteVisitor remover = new CascadeDeleteVisitor(catalog); for (LayerGroupInfo lg : catalog.getLayerGroups()) { if(catalog.getLayerGroup(lg.getId()) != null) { remover.visit(lg); } } } private LayerGroupInfo addLayerGroup(String name, LayerGroupInfo.Mode mode, WorkspaceInfo ws, PublishedInfo... layers) throws Exception { CatalogBuilder cb = new CatalogBuilder(catalog); LayerGroupInfo group = catalog.getFactory().createLayerGroup(); group.setName(name); group.setMode(mode); if(ws != null) { group.setWorkspace(ws); } if(layers != null) { for (PublishedInfo layer : layers) { group.getLayers().add(layer); group.getStyles().add(null); } } cb.calculateLayerGroupBounds(group); catalog.add(group); if(ws != null) { return catalog.getLayerGroupByName(ws.getName(), name); } else { return catalog.getLayerGroupByName(name); } } private Set<String> set(String... names) { if (names == null) { return Collections.emptySet(); } return new HashSet<>(Arrays.asList(names)); } private Set<String> containerNamesForGroup(LayerGroupInfo lg) { Collection<LayerGroupSummary> summaries = cc.getContainerGroupsFor(lg); return summaries.stream().map(gs -> gs.prefixedName()).collect(Collectors.toSet()); } private Set<String> containerNamesForResource(QName name) { Collection<LayerGroupSummary> summaries = cc.getContainerGroupsFor(getResource(name)); return summaries.stream().map(gs -> gs.prefixedName()).collect(Collectors.toSet()); } private FeatureTypeInfo getResource(QName name) { return catalog.getResourceByName(getLayerId(name), FeatureTypeInfo.class); } private String getLayerId(QName name) { return "ws:" + name.getLocalPart(); } @Test public void testInitialSetup() throws Exception { // nature Collection<LayerGroupSummary> natureContainers = cc.getContainerGroupsFor(nature); assertEquals(1, natureContainers.size()); assertThat(natureContainers, contains(new LayerGroupSummary(container))); LayerGroupSummary summary = natureContainers.iterator().next(); assertNull(summary.getWorkspace()); assertEquals(CONTAINER_GROUP, summary.getName()); assertThat(summary.getContainerGroups(), empty()); // container has no contaning groups assertThat(cc.getContainerGroupsFor(container), empty()); // now check the groups containing the layers (nature being SINGLE, not a container) assertThat(containerNamesForResource(MockData.LAKES), equalTo(set(CONTAINER_GROUP))); assertThat(containerNamesForResource(MockData.FORESTS), equalTo(set(CONTAINER_GROUP))); assertThat(containerNamesForResource(MockData.ROAD_SEGMENTS), equalTo(set(CONTAINER_GROUP))); } @Test public void testAddLayerToNature() throws Exception { LayerInfo neatline = catalog.getLayerByName(getLayerId(MockData.MAP_NEATLINE)); nature.getLayers().add(neatline); nature.getStyles().add(null); catalog.save(nature); assertThat(containerNamesForResource(MockData.MAP_NEATLINE), equalTo(set(CONTAINER_GROUP))); } @Test public void testAddLayerToContainer() throws Exception { LayerInfo neatline = catalog.getLayerByName(getLayerId(MockData.MAP_NEATLINE)); container.getLayers().add(neatline); container.getStyles().add(null); catalog.save(container); assertThat(containerNamesForResource(MockData.MAP_NEATLINE), equalTo(set(CONTAINER_GROUP))); } @Test public void testRemoveLayerFromNature() throws Exception { LayerInfo lakes = catalog.getLayerByName(getLayerId(MockData.LAKES)); nature.getLayers().remove(lakes); nature.getStyles().remove(0); catalog.save(nature); assertThat(containerNamesForResource(MockData.LAKES), empty()); assertThat(containerNamesForResource(MockData.FORESTS), equalTo(set(CONTAINER_GROUP))); assertThat(containerNamesForResource(MockData.ROAD_SEGMENTS), equalTo(set(CONTAINER_GROUP))); } @Test public void testRemoveLayerFromContainer() throws Exception { LayerInfo roads = catalog.getLayerByName(getLayerId(MockData.ROAD_SEGMENTS)); container.getLayers().remove(roads); container.getStyles().remove(0); catalog.save(container); assertThat(containerNamesForResource(MockData.LAKES), equalTo(set(CONTAINER_GROUP))); assertThat(containerNamesForResource(MockData.FORESTS), equalTo(set(CONTAINER_GROUP))); assertThat(containerNamesForResource(MockData.ROAD_SEGMENTS), empty()); } @Test public void testRemoveNatureFromContainer() throws Exception { container.getLayers().remove(nature); container.getStyles().remove(0); catalog.save(container); assertThat(containerNamesForGroup(nature), empty()); assertThat(containerNamesForResource(MockData.LAKES), empty()); assertThat(containerNamesForResource(MockData.FORESTS), empty()); assertThat(containerNamesForResource(MockData.ROAD_SEGMENTS), equalTo(set(CONTAINER_GROUP))); } @Test public void testRemoveAllGrups() throws Exception { catalog.remove(container); catalog.remove(nature); assertThat(containerNamesForGroup(nature), empty()); assertThat(containerNamesForResource(MockData.LAKES), empty()); assertThat(containerNamesForResource(MockData.FORESTS), empty()); assertThat(containerNamesForResource(MockData.ROAD_SEGMENTS), empty()); } @Test public void testAddRemoveNamed() throws Exception { final String NAMED_GROUP = "named"; LayerInfo neatline = catalog.getLayerByName(getLayerId(MockData.MAP_NEATLINE)); LayerInfo lakes = catalog.getLayerByName(getLayerId(MockData.LAKES)); // add and check containment LayerGroupInfo named = addLayerGroup(NAMED_GROUP, Mode.NAMED, null, lakes, neatline); assertThat(containerNamesForResource(MockData.LAKES), equalTo(set(CONTAINER_GROUP, NAMED_GROUP))); assertThat(containerNamesForResource(MockData.MAP_NEATLINE), equalTo(set(NAMED_GROUP))); assertThat(containerNamesForGroup(named), empty()); // delete and check containment catalog.remove(named); assertThat(containerNamesForResource(MockData.LAKES), equalTo(set(CONTAINER_GROUP))); assertThat(containerNamesForResource(MockData.MAP_NEATLINE), empty()); assertThat(containerNamesForGroup(named), empty()); } @Test public void testAddRemoveNestedNamed() throws Exception { final String NESTED_NAMED = "nestedNamed"; LayerInfo neatline = catalog.getLayerByName(getLayerId(MockData.MAP_NEATLINE)); LayerInfo lakes = catalog.getLayerByName(getLayerId(MockData.LAKES)); // add, nest, and check containment LayerGroupInfo nestedNamed = addLayerGroup(NESTED_NAMED, Mode.NAMED, null, lakes, neatline); container.getLayers().add(nestedNamed); container.getStyles().add(null); catalog.save(container); assertThat(containerNamesForResource(MockData.LAKES), equalTo(set(CONTAINER_GROUP, NESTED_NAMED))); assertThat(containerNamesForResource(MockData.MAP_NEATLINE), equalTo(set(CONTAINER_GROUP, NESTED_NAMED))); assertThat(containerNamesForGroup(nestedNamed), equalTo(set(CONTAINER_GROUP))); // delete and check containment new CascadeDeleteVisitor(catalog).visit(nestedNamed); assertThat(containerNamesForResource(MockData.LAKES), equalTo(set(CONTAINER_GROUP))); assertThat(containerNamesForResource(MockData.MAP_NEATLINE), empty()); assertThat(containerNamesForGroup(nestedNamed), empty()); } @Test public void testRenameGroup() throws Exception { nature.setName("renamed"); catalog.save(nature); LayerGroupSummary summary = cc.groupCache.get(nature.getId()); assertEquals("renamed", summary.getName()); assertEquals(WS, summary.getWorkspace()); } @Test public void testRenameWorkspace() throws Exception { WorkspaceInfo ws = catalog.getDefaultWorkspace(); ws.setName("renamed"); try { catalog.save(ws); LayerGroupSummary summary = cc.groupCache.get(nature.getId()); assertEquals(NATURE_GROUP, summary.getName()); assertEquals("renamed", summary.getWorkspace()); } finally { ws.setName(WS); catalog.save(ws); } } @Test public void testChangeWorkspace() throws Exception { DataStoreInfo store = catalog.getDataStores().get(0); try { WorkspaceInfo aws = catalog.getWorkspaceByName(ANOTHER_WS); store.setWorkspace(aws); catalog.save(store); nature.setWorkspace(aws); catalog.save(nature); LayerGroupSummary summary = cc.groupCache.get(nature.getId()); assertEquals(NATURE_GROUP, summary.getName()); assertEquals(ANOTHER_WS, summary.getWorkspace()); } finally { WorkspaceInfo ws = catalog.getWorkspaceByName(WS); store.setWorkspace(ws); catalog.save(store); } } @Test public void testChangeGroupMode() throws Exception { LayerGroupSummary summary = cc.groupCache.get(nature.getId()); assertEquals(Mode.SINGLE, summary.getMode()); nature.setMode(Mode.OPAQUE_CONTAINER); catalog.save(nature); summary = cc.groupCache.get(nature.getId()); assertEquals(Mode.OPAQUE_CONTAINER, summary.getMode()); } }