/* (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.security.decorators; import java.awt.Dimension; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.logging.Logger; import org.geoserver.ows.util.ResponseUtils; import org.geoserver.security.SecureCatalogImpl; import org.geoserver.security.WMSAccessLimits; import org.geoserver.security.WrapperPolicy; import org.geotools.data.ows.CRSEnvelope; import org.geotools.data.ows.HTTPResponse; import org.geotools.data.ows.Layer; import org.geotools.data.ows.Response; import org.geotools.data.ows.StyleImpl; import org.geotools.data.wms.request.GetMapRequest; import org.geotools.filter.text.cql2.CQL; import org.geotools.ows.ServiceException; import org.geotools.util.logging.Logging; import org.opengis.filter.Filter; import org.opengis.geometry.BoundingBox; /** * Wraps a GetMap request enforcing map limits for each of the layers * @author Andrea Aime - GeoSolutions */ public class SecuredGetMapRequest implements GetMapRequest { static final Logger LOGGER = Logging.getLogger(SecuredGetMapRequest.class); GetMapRequest delegate; List<Layer> layers = new ArrayList<Layer>(); List<String> styles = new ArrayList<String>(); // we should add layers to the delegate only once, also if // getFinalURL is called many times boolean layersAddedToDelegate = false; public SecuredGetMapRequest(GetMapRequest delegate) { this.delegate = delegate; } public void addLayer(Layer layer, String styleName) { layers.add(layer); if(styleName != null) { styles.add(styleName); } else { styles.add(""); } } public void addLayer(Layer layer, StyleImpl style) { layers.add(layer); if(style != null && style.getName() != null) { styles.add(style.getName()); } else { styles.add(""); } } public void addLayer(Layer layer) { layers.add(layer); styles.add(""); } public void addLayer(String layerName, String styleName) { throw new UnsupportedOperationException("The secured implementation only supports adding layers using Layer and StyleImpl objects"); } public void addLayer(String layerName, StyleImpl style) { throw new UnsupportedOperationException("The secured implementation only supports adding layers using Layer and StyleImpl objects"); } public URL getFinalURL() { String encodedFilter = buildCQLFilter(); if(encodedFilter != null) { delegate.setProperty("CQL_FILTER", encodedFilter); } return delegate.getFinalURL(); } /** * Checks security and build the eventual CQL filter to cascade * @param layerFilters * */ public String buildCQLFilter() { List<Filter> layerFilters = new ArrayList<Filter>(); // scan and check the layers boolean layerFiltersFound = false; for(int i = 0; i < layers.size(); i++) { Layer layer = layers.get(i); if(layer instanceof SecuredWMSLayer) { SecuredWMSLayer secured = (SecuredWMSLayer) layer; final WrapperPolicy policy = secured.getPolicy(); if(policy.getResponse() == org.geoserver.security.Response.CHALLENGE) { SecureCatalogImpl.unauthorizedAccess(layer.getName()); } // collect read filters if(policy.getLimits() instanceof WMSAccessLimits) { WMSAccessLimits limits = (WMSAccessLimits) policy.getLimits(); layerFilters.add(limits.getReadFilter()); layerFiltersFound |= limits.getReadFilter() != null; if(limits.getRasterFilter() != null) { /* * To implement this we'd have to change the code in * SecuredWebMapServer.issueRequest(GetMapRequest) to parse the image, * apply a crop, and encode it back. * Also, if there are multiple layers in the request we'd have to group * them by same raster filter, issue separate request in a format that * supports transparency, crop, merge them back */ LOGGER.severe("Sorry, raster filters for cascaded wms layers " + "have not been implemented yet"); } } if (!layersAddedToDelegate) { // add into the request delegate.addLayer(layer, styles.get(i)); } } } // do we have filters? If so encode as cql hoping to find a GeoServer on the other side // TODO: handle eventual original CQL filters String encodedFilter = null; if(layerFiltersFound) { StringBuilder sb = new StringBuilder(); for (Filter filter : layerFilters) { if(filter != null) { sb.append(CQL.toCQL(filter)); } sb.append(";"); } // remove last ";" sb.setLength(sb.length() - 1); encodedFilter = ResponseUtils.urlEncode(sb.toString()); } layersAddedToDelegate = true; return encodedFilter; } // ----------------------------------------------------------------------------------------- // Purely delegated methods // ----------------------------------------------------------------------------------------- public String getPostContentType() { return delegate.getPostContentType(); } public Properties getProperties() { return delegate.getProperties(); } public void performPostOutput(OutputStream outputStream) throws IOException { delegate.performPostOutput(outputStream); } public boolean requiresPost() { return delegate.requiresPost(); } public void setBBox(org.opengis.geometry.Envelope box) { delegate.setBBox(box); } public void setBBox(CRSEnvelope box) { delegate.setBBox(box); } public void setBBox(String bbox) { delegate.setBBox(bbox); } public void setBGColour(String bgColour) { delegate.setBGColour(bgColour); } public void setDimensions(Dimension imageDimension) { delegate.setDimensions(imageDimension); } public void setDimensions(int width, int height) { delegate.setDimensions(width, height); } public void setDimensions(String width, String height) { delegate.setDimensions(width, height); } public void setElevation(String elevation) { delegate.setElevation(elevation); } public void setExceptions(String exceptions) { delegate.setExceptions(exceptions); } public void setFormat(String format) { delegate.setFormat(format); } public void setProperties(Properties p) { delegate.setProperties(p); } public void setProperty(String name, String value) { delegate.setProperty(name, value); } public void setSampleDimensionValue(String name, String value) { delegate.setSampleDimensionValue(name, value); } public void setSRS(String srs) { delegate.setSRS(srs); } public void setTime(String time) { delegate.setTime(time); } public void setTransparent(boolean transparent) { delegate.setTransparent(transparent); } public void setVendorSpecificParameter(String name, String value) { delegate.setVendorSpecificParameter(name, value); } public void setVersion(String version) { delegate.setVersion(version); } public Response createResponse(HTTPResponse response) throws ServiceException, IOException { return delegate.createResponse(response); } }