/* (c) 2014 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.catalog.impl; import java.util.ArrayList; import java.util.List; import org.geoserver.catalog.Catalog; import org.geoserver.catalog.CatalogInfo; import org.geoserver.catalog.LayerGroupInfo; import org.geoserver.catalog.LayerGroupVisibilityPolicy; 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.StoreInfo; import org.geoserver.catalog.StyleInfo; import org.geoserver.catalog.WorkspaceInfo; import org.geoserver.ows.Dispatcher; import org.geoserver.ows.Request; import org.geoserver.security.decorators.DecoratingLayerGroupInfo; import org.geotools.filter.expression.InternalVolatileFunction; import org.opengis.filter.Filter; import org.opengis.filter.FilterFactory; /** * Filters out the non advertised layers and resources. * * @author Davide Savazzi - GeoSolutions */ public class AdvertisedCatalog extends AbstractFilteredCatalog { private static final long serialVersionUID = 3361872345280114573L; /** * Exposes a filtered down view of a layer group * * @author Andrea Aime - GeoSolutions */ static final class AdvertisedLayerGroup extends DecoratingLayerGroupInfo { private static final long serialVersionUID = 1037043388874118840L; private List<PublishedInfo> filteredLayers; private List<StyleInfo> filteredStyles; public AdvertisedLayerGroup(LayerGroupInfo delegate, List<PublishedInfo> filteredLayers, List<StyleInfo> filteredStyles) { super(delegate); this.filteredLayers = filteredLayers; this.filteredStyles = filteredStyles; } @Override public List<PublishedInfo> getLayers() { return new FilteredList<>(filteredLayers, delegate.getLayers()); } @Override public List<StyleInfo> getStyles() { return new FilteredList<>(filteredStyles, delegate.getStyles()); } } private LayerGroupVisibilityPolicy layerGroupPolicy = LayerGroupVisibilityPolicy.HIDE_NEVER; /** * @param catalog wrapped Catalog */ public AdvertisedCatalog(Catalog catalog) { super(catalog); } /** * Set LayerGroup visibility policy. * @param layerGroupPolicy */ public void setLayerGroupVisibilityPolicy(LayerGroupVisibilityPolicy layerGroupPolicy) { this.layerGroupPolicy = layerGroupPolicy; } /** * Hide Layer if Request is GetCapabilities and Layer or its Resource are not advertised. * * @param layer * */ private boolean hideLayer(LayerInfo layer) { if (!layer.isAdvertised()) { return checkCapabilitiesRequest(layer.getResource()); } else { return hideResource(layer.getResource()); } } /** * Hide Resource if it's not advertised and Request is GetCapabilities. * * @param resource * */ private boolean hideResource(ResourceInfo resource) { if (!resource.isAdvertised()) { return checkCapabilitiesRequest(resource); } else { return false; } } private boolean isOgcCapabilitiesRequest() { Request request = Dispatcher.REQUEST.get(); return request != null && "GetCapabilities".equalsIgnoreCase(request.getRequest()); } /** * Returns true if the layer should be hidden, false otherwise * <ol> * <li>has a request</li> * <li>is a GetCapabilities request</li> * <li>is not for a layer-specific virtual service</li> * </ol> */ boolean checkCapabilitiesRequest(ResourceInfo resource) { Request request = Dispatcher.REQUEST.get(); if (request != null) { if ("GetCapabilities".equalsIgnoreCase(request.getRequest())) { String resourceContext = resource.getNamespace().getPrefix() + "/" + resource.getName(); return !resourceContext.equalsIgnoreCase(request.getContext()); } } return false; } @Override protected <T extends ResourceInfo> T checkAccess(T resource) { if (resource == null || hideResource(resource)) { return null; } else { return resource; } } @Override protected LayerInfo checkAccess(LayerInfo layer) { if (layer == null || hideLayer(layer)) { return null; } else { return layer; } } @Override protected LayerGroupInfo checkAccess(LayerGroupInfo group) { if (group == null) { return null; } // do not go and check every layer if the request is not a GetCapabilities Request request = Dispatcher.REQUEST.get(); if (request == null || !"GetCapabilities".equalsIgnoreCase(request.getRequest())) { return group; } final List<PublishedInfo> layers = group.getLayers(); final List<StyleInfo> styles = group.getStyles(); final List<PublishedInfo> filteredLayers = new ArrayList<>(); final List<StyleInfo> filteredStyles = new ArrayList<>(); for (int i = 0; i < layers.size(); i++) { PublishedInfo p = layers.get(i); StyleInfo style = (styles != null && styles.size() > i) ? styles.get(i) : null; if (p instanceof LayerInfo) { p = checkAccess((LayerInfo) p); } else { p = checkAccess((LayerGroupInfo) p); } if (p != null) { filteredLayers.add(p); filteredStyles.add(style); } } if (layerGroupPolicy.hideLayerGroup(group, filteredLayers)) { return null; } else { if (!group.getLayers().equals(filteredLayers)) { return new AdvertisedLayerGroup(group, filteredLayers, filteredStyles); } else { return group; } } } @Override protected <T extends ResourceInfo> List<T> filterResources(List<T> resources) { List<T> filtered = new ArrayList<T>(resources.size()); for (T resource : resources) { resource = checkAccess(resource); if (resource != null) { filtered.add(resource); } } return filtered; } @Override protected List<LayerGroupInfo> filterGroups(List<LayerGroupInfo> groups) { List<LayerGroupInfo> filtered = new ArrayList<LayerGroupInfo>(groups.size()); for (LayerGroupInfo group : groups) { group = checkAccess(group); if (group != null) { filtered.add(group); } } return filtered; } @Override protected List<LayerInfo> filterLayers(List<LayerInfo> layers) { List<LayerInfo> filtered = new ArrayList<LayerInfo>(layers.size()); for (LayerInfo layer : layers) { layer = checkAccess(layer); if (layer != null) { filtered.add(layer); } } return filtered; } @Override protected <T extends CatalogInfo> Filter securityFilter(Class<T> infoType, Filter filter) { if(!isOgcCapabilitiesRequest()) { // Not needed for other kinds of request // TODO use a common implementation for GetCapabilities and Layer Preview return filter; } if (!ResourceInfo.class.isAssignableFrom(infoType) && !LayerInfo.class.isAssignableFrom(infoType) && !LayerGroupInfo.class.isAssignableFrom(infoType)) { // these kind of objects are not secured return filter; } org.opengis.filter.expression.Function visible = new InternalVolatileFunction() { /** * Returns {@code false} if the catalog info shall be hidden, {@code true} otherwise. */ @Override public Boolean evaluate(Object info) { if (info instanceof ResourceInfo) { return !hideResource((ResourceInfo) info); } else if (info instanceof LayerInfo) { return !hideLayer((LayerInfo) info); } else if (info instanceof LayerGroupInfo) { return checkAccess((LayerGroupInfo) info) != null; } else { throw new IllegalArgumentException("Can't build filter for objects of type " + info.getClass().getName()); } } }; FilterFactory factory = Predicates.factory; // create a filter combined with the security credentials check Filter securityFilter = factory.equals(factory.literal(Boolean.TRUE), visible); return Predicates.and(filter, securityFilter); } @Override protected <T extends StoreInfo> T checkAccess(T store) { return store; } @Override protected <T extends NamespaceInfo> T checkAccess(T ns) { return ns; } @Override protected <T extends WorkspaceInfo> T checkAccess(T ws) { return ws; } @Override protected StyleInfo checkAccess(StyleInfo style) { return style; } @Override protected <T extends StoreInfo> List<T> filterStores(List<T> stores) { return stores; } @Override protected List<StyleInfo> filterStyles(List<StyleInfo> styles) { return styles; } @Override protected <T extends NamespaceInfo> List<T> filterNamespaces(List<T> namespaces) { return namespaces; } @Override protected <T extends WorkspaceInfo> List<T> filterWorkspaces(List<T> workspaces) { return workspaces; } @Override public void save(LayerGroupInfo layerGroup) { if (layerGroup instanceof AdvertisedLayerGroup) { AbstractDecorator<LayerGroupInfo> decorator = (AbstractDecorator<LayerGroupInfo>) layerGroup; LayerGroupInfo unwrapped = decorator.unwrap(LayerGroupInfo.class); delegate.save(unwrapped); } else { delegate.save(layerGroup); } } }