/* (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 org.geoserver.platform.ExtensionPriority;
import org.geoserver.security.AccessLevel;
import org.geoserver.security.Response;
import org.geoserver.security.WrapperPolicy;
import org.geotools.coverage.grid.io.GridCoverage2DReader;
import org.geotools.coverage.grid.io.StructuredGridCoverage2DReader;
import org.geotools.coverage.grid.io.AbstractGridFormat;
import org.geotools.data.DataAccess;
import org.geotools.data.DataStore;
import org.geotools.data.FeatureLocking;
import org.geotools.data.FeatureSource;
import org.geotools.data.FeatureStore;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureLocking;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.data.simple.SimpleFeatureStore;
import org.geotools.data.wms.WebMapServer;
/**
* The default secured wrapper factory, used as a fallback when no other, more
* specific factory can be used.
* <p>
* <b>Implementation note</b>: this factory uses actual decorator objects to
* perform the secure wrapping. <br>
* Proxies and invocation handlers could be used instead, the catch is that they
* would be likely to fail in an event of a refactoring or the wrapped
* interfaces. <br>
* Given that it's security we're talking about, a type safe approach that gives
* a compile error right away has been preferred.
*
* @author Andrea Aime - TOPP
*
*/
public class DefaultSecureDataFactory implements SecuredObjectFactory {
public boolean canSecure(Class clazz) {
return DataAccess.class.isAssignableFrom(clazz) || DataStore.class.isAssignableFrom(clazz)
|| FeatureSource.class.isAssignableFrom(clazz)
|| FeatureStore.class.isAssignableFrom(clazz)
|| FeatureLocking.class.isAssignableFrom(clazz)
|| FeatureCollection.class.isAssignableFrom(clazz)
|| FeatureIterator.class.isAssignableFrom(clazz)
|| GridCoverage2DReader.class.isAssignableFrom(clazz)
|| StructuredGridCoverage2DReader.class.isAssignableFrom(clazz)
|| AbstractGridFormat.class.isAssignableFrom(clazz)
|| WebMapServer.class.isAssignableFrom(clazz);
}
public Object secure(Object object, WrapperPolicy policy) {
// null check
if (object == null)
return null;
// wrapping check
Class clazz = object.getClass();
if (!canSecure(clazz))
throw new IllegalArgumentException("Don't know how to wrap objects of class "
+ object.getClass());
// scan classes from the most specific to the most general (inheritance
// wise). Start with data stores and data access, which do provide
// metadata
if (DataStore.class.isAssignableFrom(clazz)) {
return new ReadOnlyDataStore((DataStore) object, policy);
} else if (DataAccess.class.isAssignableFrom(clazz)) {
return new ReadOnlyDataAccess((DataAccess) object, policy);
}
// for FeatureSource and family, we only return writable wrappers if the
// challenge mode is set to true, otherwise we're hide mode and we
// should just return a read only wrapper, a FeatureSource
if (SimpleFeatureSource.class.isAssignableFrom(clazz)) {
if ((policy.level == AccessLevel.READ_ONLY || policy.level == AccessLevel.METADATA
|| policy.level == AccessLevel.HIDDEN) && policy.response != Response.CHALLENGE) {
return new SecuredSimpleFeatureSource((SimpleFeatureSource) object, policy);
} else if (SimpleFeatureLocking.class.isAssignableFrom(clazz)) {
return new SecuredSimpleFeatureLocking((SimpleFeatureLocking) object, policy);
} else if (SimpleFeatureStore.class.isAssignableFrom(clazz)) {
return new SecuredSimpleFeatureStore((SimpleFeatureStore) object, policy);
} else if (SimpleFeatureSource.class.isAssignableFrom(clazz)) {
return new SecuredSimpleFeatureSource((SimpleFeatureSource) object, policy);
}
} else if (FeatureSource.class.isAssignableFrom(clazz)) {
if ((policy.level == AccessLevel.READ_ONLY || policy.level == AccessLevel.METADATA
|| policy.level == AccessLevel.HIDDEN) && policy.response != Response.CHALLENGE) {
return new SecuredFeatureSource((FeatureSource) object, policy);
} else if (FeatureLocking.class.isAssignableFrom(clazz)) {
return new SecuredFeatureLocking((FeatureLocking) object, policy);
} else if (FeatureStore.class.isAssignableFrom(clazz)) {
return new SecuredFeatureStore((FeatureStore) object, policy);
} else if (FeatureSource.class.isAssignableFrom(clazz)) {
return new SecuredFeatureSource((FeatureSource) object, policy);
}
}
// deal with feature collection and family
if (SimpleFeatureCollection.class.isAssignableFrom(clazz)) {
return new SecuredSimpleFeatureCollection((SimpleFeatureCollection) object, policy);
} else if (FeatureCollection.class.isAssignableFrom(clazz)) {
return new SecuredFeatureCollection((FeatureCollection) object, policy);
} else if (SimpleFeatureIterator.class.isAssignableFrom(clazz)) {
return new SecuredSimpleFeatureIterator((SimpleFeatureIterator) object);
} else if (FeatureIterator.class.isAssignableFrom(clazz)) {
return new SecuredFeatureIterator((FeatureIterator) object);
}
// try coverage readers and formats
if (StructuredGridCoverage2DReader.class.isAssignableFrom(clazz)) {
return new SecuredStructuredGridCoverage2DReader((StructuredGridCoverage2DReader) object, policy);
} else if(GridCoverage2DReader.class.isAssignableFrom(clazz)) {
return new SecuredGridCoverage2DReader((GridCoverage2DReader) object, policy);
} else if(AbstractGridFormat.class.isAssignableFrom(clazz)) {
return new SecuredGridFormat((AbstractGridFormat) object, policy);
}
// wms cascading related
if(WebMapServer.class.isAssignableFrom(clazz)) {
try {
return new SecuredWebMapServer((WebMapServer) object);
} catch(Exception e) {
throw new RuntimeException("Unexpected error wrapping the web map server", e);
}
}
// all attempts have been made, we don't know how to handle this object
throw new IllegalArgumentException("Don't know how to wrap objects of class "
+ object.getClass());
}
/**
* Returns {@link ExtensionPriority#LOWEST} since the wrappers generated by
* this factory
*/
public int getPriority() {
return ExtensionPriority.LOWEST;
}
}