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

org.geoserver.security.DefaultDataAccessManager 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.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.acegisecurity.Authentication;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.LayerInfo;
import org.geoserver.catalog.ResourceInfo;
import org.geoserver.catalog.WorkspaceInfo;
import org.geotools.util.logging.Logging;
import org.vfny.geoserver.global.GeoserverDataDirectory;

/**
 * Default implementation of {@link DataAccessManager}, loads simple access
 * rules from a properties file or a Properties object. The format of each
 * property is:
* workspace.layer.mode=[role]*
* where: *
    *
  • workspace: either a workspace name or a * to indicate any workspace (in * this case, the layer must also be *)
  • *
  • layer: either a layer name (feature type, coverage, layer group) or * to * indicate any layer
  • *
  • mode: the access mode, at the time or writing, either "r" * (read) or "w" (write)
  • *
  • role: a user role
  • *
* A special line is used to specify the security mode in which GeoServer operates: * mode=HIDE|CHALLENGE|MIDEX * For the meaning of these three constants see {@link CatalogMode}

* For more details on how the security rules are applied, see the <a * href="http://geoserver.org/display/GEOS/GSIP+19+-+Per+layer+security"/>per * layer security proposal</a> on the <a * href="www.geoserver.org">GeoServer</a> web site. *

* If no {@link Properties} is provided, one will be looked upon in * GEOSERVER_DATA_DIR/security/layers.properties, and the class will * keep up to date vs changes in the file * * @author Andrea Aime - TOPP */ public class DefaultDataAccessManager implements DataAccessManager { static final Logger LOGGER = Logging.getLogger(DataAccessManager.class); SecureTreeNode root; Catalog catalog; PropertyFileWatcher watcher; File layers; /** * Default to the highest security mode */ CatalogMode mode = CatalogMode.HIDE; DefaultDataAccessManager(Catalog catalog, Properties layers) { this.catalog = catalog; this.root = buildAuthorizationTree(layers); } DefaultDataAccessManager(Catalog catalog) throws Exception { this.catalog = catalog; File security = GeoserverDataDirectory.findConfigDir(GeoserverDataDirectory .getGeoserverDataDirectory(), "security"); // no security folder, let's work against an empty properties then if (security == null || !security.exists()) { this.root = new SecureTreeNode(); } else { // no security config, let's work against an empty properties then layers = new File(security, "layers.properties"); if (!layers.exists()) { this.root = new SecureTreeNode(); } else { // ok, something is there, let's load it watcher = new PropertyFileWatcher(layers); this.root = buildAuthorizationTree(watcher.getProperties()); } } } /** * Checks the property file is up to date, eventually rebuilds the tree */ void checkPropertyFile() { try { if (watcher != null && watcher.isStale()) root = buildAuthorizationTree(watcher.getProperties()); } catch (Exception e) { LOGGER.log(Level.SEVERE, "Failed to reload data access rules from " + layers + ", keeping old rules", e); } } public CatalogMode getMode() { return mode; } public boolean canAccess(Authentication user, WorkspaceInfo workspace, AccessMode mode) { checkPropertyFile(); SecureTreeNode node = root.getDeepestNode(new String[] { workspace.getName() }); return node.canAccess(user, mode); } public boolean canAccess(Authentication user, LayerInfo layer, AccessMode mode) { checkPropertyFile(); if (layer.getResource() == null) { LOGGER.log(Level.FINE, "Layer " + layer + " has no attached resource, " + "assuming it's possible to access it"); // it's a layer whose resource we don't know about return true; } else { return canAccess(user, layer.getResource(), mode); } } public boolean canAccess(Authentication user, ResourceInfo resource, AccessMode mode) { checkPropertyFile(); String workspace; try { workspace = resource.getStore().getWorkspace().getName(); } catch (Exception e) { LOGGER.log(Level.FINE, "Errors occurred trying to gather workspace of resource " + resource.getName()); // it's a layer whose resource we don't know about return true; } SecureTreeNode node = root.getDeepestNode(new String[] { workspace, resource.getName() }); return node.canAccess(user, mode); } private SecureTreeNode buildAuthorizationTree(File layers) throws IOException, FileNotFoundException { Properties props = new Properties(); props.load(new FileInputStream(layers)); return buildAuthorizationTree(props); } SecureTreeNode buildAuthorizationTree(Properties props) { SecureTreeNode root = new SecureTreeNode(); for (Map.Entry entry : props.entrySet()) { final String ruleKey = (String) entry.getKey(); final String ruleValue = (String) entry.getValue(); final String rule = ruleKey + "=" + ruleValue; // check for the mode if("mode".equalsIgnoreCase(ruleKey)) { try { mode = CatalogMode.valueOf(ruleValue.toUpperCase()); } catch(Exception e) { LOGGER.warning("Invalid security mode " + ruleValue + " acceptable values are " + Arrays.asList(CatalogMode.values())); } continue; } // parse String[] elements = parseElements(ruleKey); final String workspace = elements[0]; final String layerName = elements[1]; final String modeAlias = elements[2]; Set roles = parseRoles(ruleValue); // perform basic checks on the elements if (elements.length != 3) LOGGER.warning("Invalid rule '" + rule + "', the standard form is [namespace].[layer].[mode]=[role]+ " + "Rule has been ignored"); if (!"*".equals(workspace) && catalog.getWorkspace(workspace) == null) LOGGER.warning("Namespace/Workspace " + workspace + " is unknown in rule " + rule); if (!"*".equals(layerName) && catalog.getLayer(layerName) == null) LOGGER.warning("Layer " + workspace + " is unknown in rule + " + rule); // check the access mode AccessMode mode = AccessMode.getByAlias(modeAlias); if (mode == null) { LOGGER.warning("Unknown access mode " + modeAlias + " in " + entry.getKey() + ", skipping rule " + rule); continue; } // look for the node where the rules will have to be set SecureTreeNode node; // check for the * ws definition if ("*".equals(workspace)) { if (!"*".equals(layerName)) { LOGGER.warning("Invalid rule " + entry.getKey() + " when namespace " + "is * then also layer must be *. Skipping rule " + rule); continue; } node = root; } else { // get or create the workspace SecureTreeNode ws = root.getChild(workspace); if (ws == null) { ws = root.addChild(workspace); } // if layer is "*" the rule applies to the ws, otherwise // get/create the layer if ("*".equals(layerName)) { node = ws; } else { SecureTreeNode layer = ws.getChild(layerName); if (layer == null) layer = ws.addChild(layerName); node = layer; } } // actually set the rule if (node.getAuthorizedRoles(mode) != null && node.getAuthorizedRoles(mode).size() > 0) { LOGGER.warning("Rule " + rule + " is overriding another rule targetting the same resource"); } node.setAuthorizedRoles(mode, roles); } return root; } Set parseRoles(String roleCsv) { // regexp: treat extra spaces as separators, ignore extra commas // "a,,b, ,, c" --> ["a","b","c"] String[] rolesArray = roleCsv.split("[\\s,]+"); Set roles = new HashSet(rolesArray.length); roles.addAll(Arrays.asList(rolesArray)); // if any of the roles is * we just remove all of the others for (String role : roles) { if ("*".equals(role)) return Collections.singleton("*"); } return roles; } private String[] parseElements(String path) { // regexp: ignore extra spaces return path.split("\\s*\\.\\s*"); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy