/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.ows; import java.util.ArrayList; import java.util.List; import org.geoserver.catalog.Catalog; import org.geoserver.catalog.CatalogInfo; import org.geoserver.catalog.LayerGroupHelper; 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.Predicates; import org.geoserver.catalog.PublishedInfo; import org.geoserver.catalog.ResourceInfo; import org.geoserver.catalog.StyleInfo; import org.geoserver.catalog.WorkspaceInfo; import org.geoserver.catalog.Wrapper; import org.geoserver.platform.GeoServerExtensions; import org.geoserver.security.AbstractCatalogFilter; import org.geotools.filter.expression.InternalVolatileFunction; import org.opengis.filter.Filter; import org.opengis.filter.FilterFactory; import org.opengis.filter.expression.Function; /** * Filters the resources that are not in the current workspace (used only if virtual services are * active) * * @author Justin DeOliveira * */ public class LocalWorkspaceCatalogFilter extends AbstractCatalogFilter { /** the real/raw catalog, can't be a wrapper */ Catalog catalog; public LocalWorkspaceCatalogFilter(Catalog catalog) { //unwrap it just to be sure while (catalog instanceof Wrapper && ((Wrapper)catalog).isWrapperFor(Catalog.class)) { Catalog unwrapped = ((Wrapper)catalog).unwrap(Catalog.class); if (unwrapped == catalog || unwrapped == null) { break; } catalog = unwrapped; } this.catalog = catalog; } public boolean hideLayer(LayerInfo layer) { PublishedInfo local = LocalPublished.get(); if(local == null) { return false; } else if(local instanceof LayerInfo) { return !local.equals(layer); } else if(local instanceof LayerGroupInfo) { LayerGroupInfo lg = (LayerGroupInfo) local; Request request = Dispatcher.REQUEST.get(); if(request != null && "WMS".equalsIgnoreCase(request.getService()) && "GetCapabilities".equals(request.getRequest()) && lg.getMode() == Mode.SINGLE) { return true; } else { return !new LayerGroupHelper(lg).allLayers().contains(layer); } } else { throw new RuntimeException("Unknown PublishedInfo of type " + local.getClass()); } } public boolean hideResource(ResourceInfo resource) { if (LocalPublished.get() != null) { for (LayerInfo l : resource.getCatalog().getLayers(resource)) { if (hideLayer(l)) { return true; } } } return hideWorkspace(resource.getStore().getWorkspace()); } public boolean hideWorkspace(WorkspaceInfo workspace) { return LocalWorkspace.get() != null && !LocalWorkspace.get().equals(workspace); } public boolean hideStyle(StyleInfo style) { if (style.getWorkspace() == null) { //global style, hide it if a local workspace style shars the same name, ie overrides it if (LocalWorkspace.get() != null) { if (catalog.getStyleByName(LocalWorkspace.get(), style.getName()) != null) { return true; } } return false; } return hideWorkspace(style.getWorkspace()); } static Boolean groupInherit = null; /** * Should local workspaces include layer groups from the global workspace * @return */ public static boolean workspaceLayerGroupInherit() { if(groupInherit==null){ // Just sets it based on the property so no need to synchronize String value = GeoServerExtensions.getProperty("GEOSERVER_GLOBAL_LAYER_GROUP_INHERIT"); if(value != null){ groupInherit = Boolean.parseBoolean(value); } else { // Local workspaces inherit global layer groups by default. groupInherit = true; } } return groupInherit; } @Override public boolean hideLayerGroup(LayerGroupInfo layerGroup) { PublishedInfo local = LocalPublished.get(); if(local != null && local instanceof LayerGroupInfo) { LayerGroupInfo lg = (LayerGroupInfo) local; Request request = Dispatcher.REQUEST.get(); if(request != null && "WMS".equalsIgnoreCase(request.getService()) && "GetCapabilities".equals(request.getRequest()) && lg.getMode() == Mode.SINGLE) { return !lg.equals(layerGroup); } else if(!lg.equals(layerGroup) && !new LayerGroupHelper(lg).allGroups().contains(layerGroup)) { return true; } } if (layerGroup.getWorkspace() == null) { if(workspaceLayerGroupInherit()) { //global layer group, hide it if a local workspace layer group shared the same name, ie // overrides it if (LocalWorkspace.get() != null) { if (catalog.getLayerGroupByName(LocalWorkspace.get(), layerGroup.getName()) != null) { return true; } } } else { // Only show a global layer group in the global workspace. return LocalWorkspace.get() != null; } return false; } return hideWorkspace(layerGroup.getWorkspace()); } /** * Returns true if the sublayers of a layer group are all hidden. * @param layerGroup * */ protected boolean subLayersHidden(LayerGroupInfo layerGroup) { boolean anySublayersVisible=false; for(PublishedInfo subLayer: layerGroup.getLayers()) { if(subLayer instanceof LayerInfo) { if(!hideLayer((LayerInfo)subLayer)){ anySublayersVisible=true; break; }; } else if (subLayer instanceof LayerGroupInfo) { if(!hideLayerGroup((LayerGroupInfo)subLayer)) { anySublayersVisible=true; break; }; } } return !anySublayersVisible; } private Filter inWorkspace() { WorkspaceInfo localWS = LocalWorkspace.get(); if(localWS==null) return Predicates.acceptAll(); return Predicates.equal("workspace.id", localWS.getId()); } private Filter standardFilter(Class<? extends CatalogInfo> clazz) { final Filter forGlobal; if (LocalWorkspace.get() != null) { // TODO need a well known implementation // Show globals unless an object with the same name is in the local workspace forGlobal = super.getSecurityFilter(clazz); } else { // Global request, show all globals forGlobal = Predicates.acceptAll(); } // If it's a global use the global filter, otherwise check if it's in the local workspace return Predicates.or( Predicates.and(Predicates.isNull("workspace.id"), forGlobal), Predicates.and(Predicates.factory.not(Predicates.isNull("workspace.id")), inWorkspace()) ); } @Override public Filter getSecurityFilter(final Class<? extends CatalogInfo> clazz) { WorkspaceInfo localWS = LocalWorkspace.get(); PublishedInfo localPublished = LocalPublished.get(); if(localWS == null && localPublished == null) { return Predicates.acceptAll(); } if(ResourceInfo.class.isAssignableFrom(clazz)) { // Show if it's in a visible workspace or used by the local layer Filter localLayerFilter; if(localPublished == null) { localLayerFilter=Predicates.acceptAll(); } else { // TODO Well known check if it's used by the local layer return super.getSecurityFilter(clazz); } return Predicates.or(localLayerFilter, inWorkspace()); } else if(WorkspaceInfo.class.isAssignableFrom(clazz)) { // Show if there's no local workspace or if it is the local workspace if(localWS==null) return Predicates.acceptAll(); return Predicates.equal("id", localWS.getId()); } else if(LayerGroupInfo.class.isAssignableFrom(clazz)) { Filter filter = standardFilter(clazz); // Only show a layer group in a layer local request if it is the local layer if(localPublished != null) { if(localPublished instanceof LayerInfo) { // TODO Need a well known recursive filter for layer groups instead of using an // InternalVolatileFunction, KS Function subLayersHidden = new InternalVolatileFunction() { @Override public Boolean evaluate(Object object) { return !subLayersHidden((LayerGroupInfo) object); } }; FilterFactory factory = Predicates.factory; // hide the layer if its sublayers are hidden filter = Predicates.and(filter, factory.equals(factory.literal(Boolean.TRUE), subLayersHidden)); Predicates.and(filter, Predicates.equal("id", localPublished.getId())); } else if(localPublished instanceof LayerGroupInfo) { LayerGroupInfo lg = (LayerGroupInfo) localPublished; List<LayerGroupInfo> groups = new LayerGroupHelper(lg).allGroups(); List<Filter> groupIdFilters = new ArrayList<>(); for (LayerGroupInfo group : groups) { groupIdFilters.add(Predicates.equal("id", group.getId())); } return Predicates.or(groupIdFilters); } } return filter; } else if(StyleInfo.class.isAssignableFrom(clazz)) { return standardFilter(clazz); } else if(LayerInfo.class.isAssignableFrom(clazz)) { // If there's a local Layer, only show that layer, otherwise show all. if(localPublished == null) { return Predicates.acceptAll(); } else if(localPublished instanceof LayerInfo){ return Predicates.equal("id", localPublished.getId()); } else if(localPublished instanceof LayerGroupInfo) { LayerGroupInfo lg = (LayerGroupInfo) localPublished; Request request = Dispatcher.REQUEST.get(); if(request != null && "WMS".equalsIgnoreCase(request.getService()) && "GetCapabilities".equals(request.getRequest()) && lg.getMode() == Mode.SINGLE) { // wms GetCapabilies with a group in "single" mode, meaning the layers are also showing up stand alone // but we only asked for the group, so don't accept any sub-layer return Predicates.acceptNone(); } else { // not a WMS capabilities or not a "single" mode layer group, allow any layer in the group, List<LayerInfo> layers = new LayerGroupHelper(lg).allLayers(); List<Filter> layersIdFilters = new ArrayList<>(); for (LayerInfo layer : layers) { layersIdFilters.add(Predicates.equal("id", layer.getId())); } return Predicates.or(layersIdFilters); } } else { throw new RuntimeException("Unexpected local published reference of type " + localPublished.getClass()); } } else if(NamespaceInfo.class.isAssignableFrom(clazz)) { // TODO return super.getSecurityFilter(clazz); } else { return super.getSecurityFilter(clazz); } } }