/* (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.impl; import static org.geoserver.security.impl.DataAccessRule.*; import java.io.IOException; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.logging.Logger; import org.geoserver.catalog.Catalog; import org.geoserver.config.GeoServerDataDirectory; import org.geoserver.platform.GeoServerExtensions; import org.geoserver.platform.resource.Resource; import org.geotools.util.logging.Logging; /** * Allows one to manage the rules used by the service layer security subsystem * TODO: consider splitting the persistence of properties into two strategies, * and in memory one, and a file system one (this class is so marginal that * I did not do so right away, in memory access is mostly handy for testing) */ public class ServiceAccessRuleDAO extends AbstractAccessRuleDAO<ServiceAccessRule> { private final static Logger LOGGER = Logging.getLogger(ServiceAccessRuleDAO.class); /** * property file name */ static final String SERVICES = "services.properties"; /** * the catalog */ Catalog rawCatalog; /** * Returns the instanced contained in the Spring context for the UI to use * */ public static ServiceAccessRuleDAO get() { return GeoServerExtensions.bean(ServiceAccessRuleDAO.class); } public ServiceAccessRuleDAO(GeoServerDataDirectory dd, Catalog rawCatalog) throws IOException { super(dd, SERVICES); this.rawCatalog = rawCatalog; } /** * Builds a new dao * * @param rawCatalog */ public ServiceAccessRuleDAO() throws IOException { super(GeoServerExtensions.bean(GeoServerDataDirectory.class), SERVICES); } /** * Builds a new dao with a custom security dir. Used mostly for testing purposes * * @param rawCatalog */ ServiceAccessRuleDAO(Catalog rawCatalog, Resource securityDir) { super(securityDir, SERVICES); } /** * Parses the rules contained in the property file * * @param props * */ protected void loadRules(Properties props) { TreeSet<ServiceAccessRule> result = new TreeSet<ServiceAccessRule>(); for (Map.Entry<Object,Object> entry : props.entrySet()) { String ruleKey = (String) entry.getKey(); String ruleValue = (String) entry.getValue(); ServiceAccessRule rule = parseServiceAccessRule(ruleKey, ruleValue); if (rule != null) { if (result.contains(rule)) LOGGER.warning("Rule " + ruleKey + "." + ruleValue + " overwrites another rule on the same path"); result.add(rule); } } // make sure to add the "all access alloed" rule if the set if empty if(result.size() == 0) { result.add(new ServiceAccessRule(new ServiceAccessRule())); } rules = result; } /** * Parses a single layer.properties line into a {@link DataAccessRule}, returns false if the * rule is not valid * * */ ServiceAccessRule parseServiceAccessRule(String ruleKey, String ruleValue) { final String rule = ruleKey + "=" + ruleValue; // parse String[] elements = parseElements(ruleKey); if(elements.length != 2) { LOGGER.warning("Invalid rule " + rule + ", the expected format is service.method=role1,role2,..."); return null; } String service = elements[0]; String method = elements[1]; Set<String> roles = parseRoles(ruleValue); // check ANY usage sanity if (ANY.equals(service)) { if (!ANY.equals(method)) { LOGGER.warning("Invalid rule " + rule + ", when namespace " + "is * then also layer must be *. Skipping rule " + rule); return null; } } // build the rule return new ServiceAccessRule(service, method, roles); } /** * Turns the rules list into a property bag * */ protected Properties toProperties() { Properties props = new Properties(); for (ServiceAccessRule rule : rules) { props.put(rule.getKey(), rule.getValue()); } return props; } /** * Parses workspace.layer.mode into an array of strings * * @param path * */ private String[] parseElements(String path) { // regexp: ignore extra spaces, split on dot return path.split("\\s*\\.\\s*"); } /** * Returns a sorted set of rules associated to the role * * @param role * */ public SortedSet<ServiceAccessRule> getRulesAssociatedWithRole(String role) { SortedSet<ServiceAccessRule> result = new TreeSet<ServiceAccessRule>(); for (ServiceAccessRule rule: getRules()) if (rule.getRoles().contains(role)) result.add(rule); return result; } }