/* (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.security.decorators;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import org.geoserver.catalog.Predicates;
import org.geoserver.data.util.CoverageUtils;
import org.geoserver.security.CoverageAccessLimits;
import org.geoserver.security.WrapperPolicy;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.io.GridCoverage2DReader;
import org.geotools.coverage.processing.CoverageProcessor;
import org.geotools.coverage.processing.operation.Crop;
import org.geotools.data.ResourceInfo;
import org.geotools.data.ServiceInfo;
import org.geotools.factory.Hints;
import org.geotools.geometry.jts.JTS;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.opengis.coverage.grid.Format;
import org.opengis.filter.Filter;
import org.opengis.parameter.GeneralParameterDescriptor;
import org.opengis.parameter.GeneralParameterValue;
import org.opengis.parameter.ParameterValue;
import org.opengis.parameter.ParameterValueGroup;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.MultiPolygon;
/**
* Applies access limits policies around the wrapped reader
*
* @author Andrea Aime - GeoSolutions
*/
public class SecuredGridCoverage2DReader extends DecoratingGridCoverage2DReader {
/** Parameters used to control the {@link Crop} operation. */
private static final ParameterValueGroup cropParams;
/**
* Cached crop factory
*/
private final static Crop coverageCropFactory = new Crop();
static {
final CoverageProcessor processor = new CoverageProcessor(
new Hints(Hints.LENIENT_DATUM_SHIFT, Boolean.TRUE));
cropParams = processor.getOperation("CoverageCrop").getParameters();
}
WrapperPolicy policy;
public SecuredGridCoverage2DReader(GridCoverage2DReader delegate, WrapperPolicy policy) {
super(delegate);
this.policy = policy;
}
public Format getFormat() {
Format format = delegate.getFormat();
if (format == null) {
return null;
} else {
return (Format) SecuredObjects.secure(format, policy);
}
}
public GridCoverage2D read(GeneralParameterValue[] parameters) throws IllegalArgumentException, IOException {
return SecuredGridCoverage2DReader.read(delegate, policy, parameters);
}
static GridCoverage2D read(GridCoverage2DReader delegate, WrapperPolicy policy,
GeneralParameterValue[] parameters) throws IllegalArgumentException, IOException {
//Package private static method to share reading code with Structured reader
MultiPolygon rasterFilter = null;
if (policy.getLimits() instanceof CoverageAccessLimits) {
CoverageAccessLimits limits = (CoverageAccessLimits) policy.getLimits();
// get the crop filter
rasterFilter = limits.getRasterFilter();
Filter readFilter = limits.getReadFilter();
// update the read params
final GeneralParameterValue[] limitParams = limits.getParams();
if (parameters == null) {
parameters = limitParams;
} else if (limitParams != null) {
// scan the input params, add and overwrite with the limits params as needed
List<GeneralParameterValue> params = new ArrayList<GeneralParameterValue>(Arrays
.asList(parameters));
for (GeneralParameterValue lparam : limitParams) {
// remove the overwritten param, if any
final GeneralParameterDescriptor ldescriptor = lparam.getDescriptor();
for (Iterator it = params.iterator(); it.hasNext();) {
GeneralParameterValue param = (GeneralParameterValue) it.next();
if (param.getDescriptor().equals(lparam.getDescriptor())) {
it.remove();
break;
}
}
// add the overwrite param (will be an overwrite if it was already there, an
// addition otherwise)
params.add(lparam);
}
parameters = params
.toArray(new GeneralParameterValue[params.size()]);
}
if(readFilter != null && !Filter.INCLUDE.equals(readFilter)) {
Format format = delegate.getFormat();
ParameterValueGroup readParameters = format.getReadParameters();
List<GeneralParameterDescriptor> descriptors = readParameters.getDescriptor()
.descriptors();
// scan all the params looking for the one we want to add
boolean replacedOriginalFilter = false;
for (GeneralParameterValue pv : parameters) {
String pdCode = pv.getDescriptor().getName().getCode();
if ("FILTER".equals(pdCode) || "Filter".equals(pdCode)) {
replacedOriginalFilter = true;
ParameterValue pvalue = (ParameterValue) pv;
Filter originalFilter = (Filter) pvalue.getValue();
if (originalFilter == null || Filter.INCLUDE.equals(originalFilter)) {
pvalue.setValue(readFilter);
} else {
Filter combined = Predicates.and(originalFilter, readFilter);
pvalue.setValue(combined);
}
}
}
if (!replacedOriginalFilter) {
parameters = CoverageUtils.mergeParameter(descriptors, parameters, readFilter,
"FILTER", "Filter");
}
}
}
GridCoverage2D grid = delegate.read(parameters);
// crop if necessary
if (rasterFilter != null) {
Geometry coverageBounds = JTS.toGeometry((Envelope) new ReferencedEnvelope(grid.getEnvelope2D()));
if(coverageBounds.intersects(rasterFilter)) {
final ParameterValueGroup param = cropParams.clone();
param.parameter("source").setValue(grid);
param.parameter("ROI").setValue(rasterFilter);
grid = (GridCoverage2D) coverageCropFactory.doOperation(param, null);
} else {
return null;
}
}
return grid;
}
@Override
public ServiceInfo getInfo() {
ServiceInfo info = delegate.getInfo();
if (info == null) {
return null;
} else {
return (ServiceInfo) SecuredObjects.secure(info, policy);
}
}
@Override
public ResourceInfo getInfo(String coverageName) {
ResourceInfo info = delegate.getInfo(coverageName);
if (info == null) {
return null;
} else {
return (ResourceInfo) SecuredObjects.secure(info, policy);
}
}
}