org.geoserver.security.SecureCatalogImpl Maven / Gradle / Ivy
The newest version!
/* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved.
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.security;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.acegisecurity.AccessDeniedException;
import org.acegisecurity.AcegiSecurityException;
import org.acegisecurity.Authentication;
import org.acegisecurity.InsufficientAuthenticationException;
import org.acegisecurity.context.SecurityContextHolder;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.CatalogFactory;
import org.geoserver.catalog.CoverageInfo;
import org.geoserver.catalog.CoverageStoreInfo;
import org.geoserver.catalog.DataStoreInfo;
import org.geoserver.catalog.FeatureTypeInfo;
import org.geoserver.catalog.LayerGroupInfo;
import org.geoserver.catalog.LayerInfo;
import org.geoserver.catalog.MapInfo;
import org.geoserver.catalog.NamespaceInfo;
import org.geoserver.catalog.ResourceInfo;
import org.geoserver.catalog.ResourcePool;
import org.geoserver.catalog.StoreInfo;
import org.geoserver.catalog.StyleInfo;
import org.geoserver.catalog.WorkspaceInfo;
import org.geoserver.catalog.event.CatalogListener;
import org.geoserver.catalog.impl.AbstractDecorator;
import org.geoserver.ows.Dispatcher;
import org.geoserver.ows.Request;
import org.geoserver.platform.GeoServerExtensions;
import org.geoserver.security.DataAccessManager.CatalogMode;
import org.geoserver.security.decorators.SecuredCoverageInfo;
import org.geoserver.security.decorators.SecuredCoverageStoreInfo;
import org.geoserver.security.decorators.SecuredDataStoreInfo;
import org.geoserver.security.decorators.SecuredFeatureTypeInfo;
import org.geoserver.security.decorators.SecuredLayerGroupInfo;
import org.geoserver.security.decorators.SecuredLayerInfo;
/**
*
* @author Andrea Aime - TOPP TODO: - docs - uniform argument order in helper
* methods - create wrappers around the returned values so that they can
* be used only within the limits of the current user power - move the
* layer group checks into this class out of DataAccessManager? - does
* administration goes thru this catalog, or directly accesses the
* insecured one? Option one, admin directly accesses. Option two, admin
* users get a special role that make them uber-powerful (root like) and
* everything goes thru the secured catalog
*/
public class SecureCatalogImpl extends AbstractDecorator implements Catalog {
/**
* The kind of access we can give the user for a given resource
*/
public enum AccessLevel {
HIDDEN,
METADATA,
READ_ONLY,
READ_WRITE
}
/**
* The response to be used when the user tries to go beyond the level
* that he's authorized to see
*/
public enum Response {
HIDE,
CHALLENGE
}
/**
* The combination of access level granted and response policy (lists only possible cases)
*/
public enum WrapperPolicy {
HIDE(AccessLevel.HIDDEN, Response.HIDE),
METADATA(AccessLevel.METADATA, Response.CHALLENGE),
RO_CHALLENGE(AccessLevel.READ_ONLY, Response.CHALLENGE),
RO_HIDE(AccessLevel.READ_ONLY, Response.HIDE),
RW(AccessLevel.READ_WRITE, Response.HIDE);
public final AccessLevel level;
public final Response response;
WrapperPolicy(AccessLevel level, Response response) {
this.level = level;
this.response = response;
}
}
protected DataAccessManager accessManager;
public SecureCatalogImpl(Catalog catalog) throws Exception {
this(catalog, lookupDataAccessManager(catalog));
}
static DataAccessManager lookupDataAccessManager(Catalog catalog) throws Exception {
DataAccessManager manager = GeoServerExtensions.bean(DataAccessManager.class);
if (manager == null) {
manager = new DefaultDataAccessManager(catalog);
}
return manager;
}
SecureCatalogImpl(Catalog catalog, DataAccessManager manager) {
super(catalog);
this.accessManager = manager;
}
// -------------------------------------------------------------------
// SECURED METHODS
// -------------------------------------------------------------------
public CoverageInfo getCoverage(String id) {
return (CoverageInfo) checkAccess(user(), delegate.getCoverage(id));
}
public CoverageInfo getCoverageByName(String ns, String name) {
return (CoverageInfo) checkAccess(user(), delegate.getCoverageByName(ns, name));
}
public CoverageInfo getCoverageByName(String name) {
return (CoverageInfo) checkAccess(user(), delegate.getCoverageByName(name));
}
public List getCoverages() {
return filterResources(user(), delegate.getCoverages());
}
public List getCoveragesByNamespace(NamespaceInfo namespace) {
return filterResources(user(), delegate.getCoveragesByNamespace(namespace));
}
public CoverageStoreInfo getCoverageStore(String id) {
return checkAccess(user(), delegate.getCoverageStore(id));
}
public CoverageStoreInfo getCoverageStoreByName(String name) {
return checkAccess(user(), delegate.getCoverageStoreByName(name));
}
public List getCoverageStores() {
return filterStores(user(), delegate.getCoverageStores());
}
public DataStoreInfo getDataStore(String id) {
return checkAccess(user(), delegate.getDataStore(id));
}
public DataStoreInfo getDataStoreByName(String name) {
return checkAccess(user(), delegate.getDataStoreByName(name));
}
public List getDataStores() {
return filterStores(user(), delegate.getDataStores());
}
public NamespaceInfo getDefaultNamespace() {
return delegate.getDefaultNamespace();
}
public WorkspaceInfo getDefaultWorkspace() {
return delegate.getDefaultWorkspace();
}
public FeatureTypeInfo getFeatureType(String id) {
return checkAccess(user(), delegate.getFeatureType(id));
}
public FeatureTypeInfo getFeatureTypeByName(String ns, String name) {
return checkAccess(user(), delegate.getFeatureTypeByName(ns, name));
}
public FeatureTypeInfo getFeatureTypeByName(String name) {
return checkAccess(user(), delegate.getFeatureTypeByName(name));
}
public List getFeatureTypes() {
return filterResources(user(), delegate.getFeatureTypes());
}
public List getFeatureTypesByNamespace(NamespaceInfo namespace) {
return filterResources(user(), delegate.getFeatureTypesByNamespace(namespace));
}
public LayerInfo getLayer(String id) {
return checkAccess(user(), delegate.getLayer(id));
}
public LayerInfo getLayerByName(String name) {
return checkAccess(user(), delegate.getLayerByName(name));
}
public LayerGroupInfo getLayerGroup(String id) {
return checkAccess(user(), delegate.getLayerGroup(id));
}
public LayerGroupInfo getLayerGroupByName(String name) {
return checkAccess(user(), delegate.getLayerGroupByName(name));
}
public List getLayerGroups() {
return filterGroups(user(), delegate.getLayerGroups());
}
public List getLayers() {
return filterLayers(user(), delegate.getLayers());
}
public List getLayers(ResourceInfo resource) {
return filterLayers(user(), delegate.getLayers(unwrap(resource)));
}
public NamespaceInfo getNamespace(String id) {
return checkAccess(user(), delegate.getNamespace(id));
}
public NamespaceInfo getNamespaceByPrefix(String prefix) {
return checkAccess(user(), delegate.getNamespaceByPrefix(prefix));
}
public NamespaceInfo getNamespaceByURI(String uri) {
return checkAccess(user(), delegate.getNamespaceByURI(uri));
}
public List getNamespaces() {
return filterNamespaces(user(), delegate.getNamespaces());
}
public T getResource(String id, Class clazz) {
return checkAccess(user(), delegate.getResource(id, clazz));
}
public T getResourceByName(String name, Class clazz) {
return checkAccess(user(), delegate.getResourceByName(name, clazz));
}
public T getResourceByName(String ns, String name, Class clazz) {
return checkAccess(user(), delegate.getResourceByName(ns, name, clazz));
}
public List getResources(Class clazz) {
return filterResources(user(), delegate.getResources(clazz));
}
public List getResourcesByNamespace(NamespaceInfo namespace,
Class clazz) {
return filterResources(user(), delegate.getResourcesByNamespace(namespace, clazz));
}
public T getStore(String id, Class clazz) {
return checkAccess(user(), delegate.getStore(id, clazz));
}
public T getStoreByName(String name, Class clazz) {
return checkAccess(user(), delegate.getStoreByName(name, clazz));
}
public List getStores(Class clazz) {
return filterStores(user(), delegate.getStores(clazz));
}
public List getStoresByWorkspace(WorkspaceInfo workspace,
Class clazz) {
return filterStores(user(), delegate.getStoresByWorkspace(workspace, clazz));
}
public WorkspaceInfo getWorkspace(String id) {
return checkAccess(user(), delegate.getWorkspace(id));
}
public WorkspaceInfo getWorkspaceByName(String name) {
return checkAccess(user(), delegate.getWorkspaceByName(name));
}
public List getWorkspaces() {
return filterWorkspaces(user(), delegate.getWorkspaces());
}
// -------------------------------------------------------------------
// Security support method
// -------------------------------------------------------------------
protected static Authentication user() {
return SecurityContextHolder.getContext().getAuthentication();
}
/**
* Given a {@link FeatureTypeInfo} and a user, returns it back if the user
* can access it in write mode, makes it read only if the user can access it
* in read only mode, returns null otherwise
* @return
*/
protected T checkAccess(Authentication user,
T info) {
// handle null case
if (info == null)
return null;
// first off, handle the case where the user cannot even read the data
boolean canRead = accessManager.canAccess(user, info, AccessMode.READ);
boolean canWrite = accessManager.canAccess(user, info, AccessMode.WRITE);
WrapperPolicy policy = checkWrapperPolicy(user, canRead, canWrite, info.getName());
// handle the modes that do not require wrapping
if(policy == WrapperPolicy.HIDE)
return null;
else if(policy.level == AccessLevel.READ_WRITE ||
(policy.level == AccessLevel.READ_ONLY && info instanceof CoverageInfo))
return info;
// otherwise we are in a mixed case where the user can read but not write, or
// cannot read but is allowed by the operation mode to access the metadata
if(info instanceof FeatureTypeInfo) {
return (T) new SecuredFeatureTypeInfo((FeatureTypeInfo) info, policy);
} else if(info instanceof CoverageInfo) {
return (T) new SecuredCoverageInfo((CoverageInfo) info, policy);
} else {
throw new RuntimeException("Unknown resource type " + info.getClass());
}
}
/**
* Given a store and a user, returns it back if the user can access its
* workspace in read mode, null otherwise
* @return
*/
protected T checkAccess(Authentication user, T store) {
if (store == null)
return null;
// first off, handle the case where the user cannot even read the data
boolean canRead = accessManager.canAccess(user, store.getWorkspace(), AccessMode.READ);
boolean canWrite = accessManager.canAccess(user, store.getWorkspace(), AccessMode.WRITE);
WrapperPolicy policy = checkWrapperPolicy(user, canRead, canWrite, store.getName());
// handle the modes that do not require wrapping
if(policy == WrapperPolicy.HIDE)
return null;
else if(policy.level == AccessLevel.READ_WRITE ||
(policy.level == AccessLevel.READ_ONLY && store instanceof CoverageStoreInfo))
return store;
// otherwise we are in a mixed case where the user can read but not write, or
// cannot read but is allowed by the operation mode to access the metadata
if(store instanceof DataStoreInfo) {
return (T) new SecuredDataStoreInfo((DataStoreInfo) store, policy);
} else if(store instanceof CoverageStoreInfo) {
return (T) new SecuredCoverageStoreInfo((CoverageStoreInfo) store, policy);
} else {
throw new RuntimeException("Unknown store type " + store.getClass());
}
}
/**
* Given a layer and a user, returns it back if the user can access it, null
* otherwise
* @return
*/
protected LayerInfo checkAccess(Authentication user, LayerInfo layer) {
if (layer == null)
return null;
// first off, handle the case where the user cannot even read the data
boolean canRead = accessManager.canAccess(user, layer, AccessMode.READ);
boolean canWrite = accessManager.canAccess(user, layer, AccessMode.WRITE);
WrapperPolicy policy = checkWrapperPolicy(user, canRead, canWrite, layer.getName());
// handle the modes that do not require wrapping
if(policy == WrapperPolicy.HIDE)
return null;
else if(policy.level == AccessLevel.READ_WRITE)
return layer;
// otherwise we are in a mixed case where the user can read but not write, or
// cannot read but is allowed by the operation mode to access the metadata
return new SecuredLayerInfo(layer, policy);
}
/**
* Given a layer group and a user, returns it back if the user can access
* it, null otherwise
* @return
*/
protected LayerGroupInfo checkAccess(Authentication user, LayerGroupInfo group) {
if (group == null)
return null;
// scan thru the layers, if any cannot be accessed, we hide the group, otherwise
// we return the group back, eventually wrapping the read only layers
final List layers = group.getLayers();
ArrayList wrapped = new ArrayList(layers.size());
boolean needsWrapping = false;
for (LayerInfo layer : layers) {
LayerInfo checked = checkAccess(user, layer);
if(checked == null)
return null;
else if(checked != null)
needsWrapping = true;
wrapped.add(checked);
}
if(needsWrapping)
return new SecuredLayerGroupInfo(group, wrapped);
else
return group;
}
/**
* Given a namespace and user, returns it back if the user can access it,
* null otherwise
* @return
*/
protected T checkAccess(Authentication user, T ns) {
if(ns == null)
return null;
// route the security check thru the associated workspace info
WorkspaceInfo info = checkAccess(user, delegate.getWorkspace(ns.getPrefix()));
if (info == null)
return null;
else
return ns;
}
/**
* Given a workspace and user, returns it back if the user can access it,
* null otherwise
* @return
*/
protected T checkAccess(Authentication user, T ws) {
if (ws == null)
return null;
// first off, handle the case where the user cannot even read the data
boolean canRead = accessManager.canAccess(user, ws, AccessMode.READ);
boolean canWrite = accessManager.canAccess(user, ws, AccessMode.WRITE);
WrapperPolicy policy = checkWrapperPolicy(user, canRead, canWrite, ws.getName());
// if we don't need to hide it, then we can return it as is since it
// can only provide metadata.
if(policy == WrapperPolicy.HIDE)
return null;
else
return ws;
}
/**
* Factors out the policy that decides what access level the current user
* has to a specific resource considering the read/write access, the security
* mode, and the filtering status
* @param user
* @param canRead
* @param canWrite
* @param resourceName
* @return
*/
WrapperPolicy checkWrapperPolicy(Authentication user,
boolean canRead, boolean canWrite, String resourceName) {
if (!canRead) {
// if in hide mode, we just hide the resource
if (accessManager.getMode() == CatalogMode.HIDE) {
return WrapperPolicy.HIDE;
} else if (accessManager.getMode() == CatalogMode.MIXED) {
// if request is a get capabilities and mixed, we hide again
Request request = Dispatcher.REQUEST.get();
if(request != null && request.getRequest().equalsIgnoreCase("GetCapabilities"))
return WrapperPolicy.HIDE;
// otherwise challenge the user for credentials
else
throw unauthorizedAccess(resourceName);
} else {
// for challenge mode we agree to show freely only the metadata, every
// other access will trigger a security exception
return WrapperPolicy.METADATA;
}
} else if (!canWrite) {
if (accessManager.getMode() == CatalogMode.HIDE) {
return WrapperPolicy.RO_HIDE;
} else {
return WrapperPolicy.RO_CHALLENGE;
}
}
return WrapperPolicy.RW;
}
public static AcegiSecurityException unauthorizedAccess(String resourceName) {
// not hide, and not filtering out a list, this
// is an unauthorized direct resource access, complain
Authentication user = user();
if (user == null || user.getAuthorities().length == 0)
return new InsufficientAuthenticationException("Cannot access "
+ resourceName + " as anonymous");
else
return new AccessDeniedException("Cannot access "
+ resourceName + " with the current privileges");
}
public static AcegiSecurityException unauthorizedAccess() {
// not hide, and not filtering out a list, this
// is an unauthorized direct resource access, complain
Authentication user = user();
if (user == null || user.getAuthorities().length == 0)
return new InsufficientAuthenticationException("Operation unallowed with the current privileges");
else
return new AccessDeniedException("Operation unallowed with the current privileges");
}
/**
* Given a list of resources, returns a copy of it containing only the
* resources the user can access
*
* @param user
* @param resources
*
* @return
*/
protected List filterResources(Authentication user,
List resources) {
List result = new ArrayList();
for (T original : resources) {
T secured = checkAccess(user, original);
if (secured != null)
result.add(secured);
}
return result;
}
/**
* Given a list of stores, returns a copy of it containing only the
* resources the user can access
*
* @param user
* @param resources
*
* @return
*/
protected List filterStores(Authentication user, List resources) {
List result = new ArrayList();
for (T original : resources) {
T secured = checkAccess(user, original);
if (secured != null)
result.add(secured);
}
return result;
}
/**
* Given a list of layer groups, returns a copy of it containing only the
* groups the user can access
*
* @param user
* @param groups
*
* @return
*/
protected List filterGroups(Authentication user, List groups) {
List result = new ArrayList();
for (LayerGroupInfo original : groups) {
LayerGroupInfo secured = checkAccess(user, original);
if (secured != null)
result.add(secured);
}
return result;
}
/**
* Given a list of layers, returns a copy of it containing only the layers
* the user can access
*
* @param user
* @param layers
*
* @return
*/
protected List filterLayers(Authentication user, List layers) {
List result = new ArrayList();
for (LayerInfo original : layers) {
LayerInfo secured = checkAccess(user, original);
if (secured != null)
result.add(secured);
}
return result;
}
/**
* Given a list of namespaces, returns a copy of it containing only the
* namespaces the user can access
*
* @param user
* @param namespaces
*
* @return
*/
protected List filterNamespaces(Authentication user,
List namespaces) {
List result = new ArrayList();
for (T original : namespaces) {
T secured = checkAccess(user, original);
if (secured != null)
result.add(secured);
}
return result;
}
/**
* Given a list of workspaces, returns a copy of it containing only the
* workspaces the user can access
*
* @param user
* @param namespaces
*
* @return
*/
protected List filterWorkspaces(Authentication user,
List workspaces) {
List result = new ArrayList();
for (T original : workspaces) {
T secured = checkAccess(user, original);
if (secured != null)
result.add(secured);
}
return result;
}
// -------------------------------------------------------------------
// Unwrappers, used to make sure the lower level does not get hit by
// read only wrappers
// -------------------------------------------------------------------
LayerGroupInfo unwrap(LayerGroupInfo layerGroup) {
if(layerGroup instanceof SecuredLayerGroupInfo)
return ((SecuredLayerGroupInfo) layerGroup).unwrap(LayerGroupInfo.class);
return layerGroup;
}
LayerInfo unwrap(LayerInfo layer) {
if(layer instanceof SecuredLayerInfo)
return ((SecuredLayerInfo) layer).unwrap(LayerInfo.class);
return layer;
}
ResourceInfo unwrap(ResourceInfo info) {
if(info instanceof SecuredFeatureTypeInfo)
return ((SecuredFeatureTypeInfo) info).unwrap(ResourceInfo.class);
return info;
}
StoreInfo unwrap(StoreInfo info) {
if(info instanceof SecuredDataStoreInfo)
return ((SecuredDataStoreInfo) info).unwrap(StoreInfo.class);
return info;
}
// -------------------------------------------------------------------
// PURE DELEGATING METHODS
// (MapInfo being here since its role in the grand scheme of things
// is still undefined)
// -------------------------------------------------------------------
public MapInfo getMap(String id) {
return delegate.getMap(id);
}
public MapInfo getMapByName(String name) {
return delegate.getMapByName(name);
}
public List getMaps() {
return delegate.getMaps();
}
public void add(LayerGroupInfo layerGroup) {
delegate.add(unwrap(layerGroup));
}
public void add(LayerInfo layer) {
delegate.add(unwrap(layer));
}
public void add(MapInfo map) {
delegate.add(map);
}
public void add(NamespaceInfo namespace) {
delegate.add(namespace);
}
public void add(ResourceInfo resource) {
delegate.add(unwrap(resource));
}
public void add(StoreInfo store) {
delegate.add(unwrap(store));
}
public void add(StyleInfo style) {
delegate.add(style);
}
public void add(WorkspaceInfo workspace) {
delegate.add(workspace);
}
public void addListener(CatalogListener listener) {
delegate.addListener(listener);
}
public void dispose() {
delegate.dispose();
}
public CatalogFactory getFactory() {
return delegate.getFactory();
}
public Collection getListeners() {
return delegate.getListeners();
}
// TODO: why is resource pool being exposed???
public ResourcePool getResourcePool() {
return delegate.getResourcePool();
}
public StyleInfo getStyle(String id) {
return delegate.getStyle(id);
}
public StyleInfo getStyleByName(String name) {
return delegate.getStyleByName(name);
}
public List getStyles() {
return delegate.getStyles();
}
public void remove(LayerGroupInfo layerGroup) {
delegate.remove(unwrap(layerGroup));
}
public void remove(LayerInfo layer) {
delegate.remove(unwrap(layer));
}
public void remove(MapInfo map) {
delegate.remove(map);
}
public void remove(NamespaceInfo namespace) {
delegate.remove(namespace);
}
public void remove(ResourceInfo resource) {
delegate.remove(unwrap(resource));
}
public void remove(StoreInfo store) {
delegate.remove(unwrap(store));
}
public void remove(StyleInfo style) {
delegate.remove(style);
}
public void remove(WorkspaceInfo workspace) {
delegate.remove(workspace);
}
public void removeListener(CatalogListener listener) {
delegate.removeListener(listener);
}
public void save(LayerGroupInfo layerGroup) {
delegate.save(unwrap(layerGroup));
}
public void save(LayerInfo layer) {
delegate.save(unwrap(layer));
}
public void save(MapInfo map) {
delegate.save(map);
}
public void save(NamespaceInfo namespace) {
delegate.save(namespace);
}
public void save(ResourceInfo resource) {
delegate.save(unwrap(resource));
}
public void save(StoreInfo store) {
delegate.save(unwrap(store));
}
public void save(StyleInfo style) {
delegate.save(style);
}
public void save(WorkspaceInfo workspace) {
delegate.save(workspace);
}
public void setDefaultNamespace(NamespaceInfo defaultNamespace) {
delegate.setDefaultNamespace(defaultNamespace);
}
public void setDefaultWorkspace(WorkspaceInfo workspace) {
delegate.setDefaultWorkspace(workspace);
}
public void setResourcePool(ResourcePool resourcePool) {
delegate.setResourcePool(resourcePool);
}
}