All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy