/* (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 java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
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.platform.GeoServerExtensions;
import org.geoserver.platform.resource.Resource;
import org.geoserver.security.GeoServerRoleService;
import org.geoserver.security.GeoServerRoleStore;
import org.geoserver.security.config.SecurityNamedServiceConfig;
import org.geoserver.security.config.SecurityRoleServiceConfig;
import org.geoserver.security.event.RoleLoadedEvent;
import org.geoserver.security.event.RoleLoadedListener;
import org.geoserver.security.filter.GeoServerJ2eeAuthenticationFilter;
import org.geotools.util.logging.Logging;
import org.springframework.util.StringUtils;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
/**
* Implementation for {@link GeoServerRoleService} obtaining
* roles from <b>role-name</b> elements contained in WEB-INF/web.xml
*
* This implementation could be used in combination with {@link GeoServerJ2eeAuthenticationFilter} objects.
*
* @author Christian
*
*/
public class GeoServerJ2eeRoleService extends AbstractGeoServerSecurityService
implements GeoServerRoleService {
public class WebXMLContentHandler implements ContentHandler {
public final static String SECURITY_ROLE_REF="security-role-ref";
public final static String AUTH_CONSTRAINT="auth-constraint";
public final static String SECURITY_ROLE="security-role";
public final static String ROLE_NAME="role-name";
public final static String ROLE_LINK="role-link";
private String currentValue;
private String roleName;
private boolean inSecRoleRef, inAuthConstraint, inSecRole;
private Map<String,String> inSecRoleRefRoles = new HashMap<String,String>();
private List<String> inAuthConstraintRoles = new ArrayList<String>();
private List<String> inSecRoleRoles = new ArrayList<String>();
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
currentValue = new String(ch, start, length);
}
@Override
public void endDocument() throws SAXException {
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if (SECURITY_ROLE_REF.equals(localName)) {
inSecRoleRef=false;
}
if (AUTH_CONSTRAINT.equals(localName)) {
inAuthConstraint=false;
}
if (SECURITY_ROLE.equals(localName)) {
inSecRole=false;
}
if (ROLE_NAME.endsWith(localName)) {
if (inSecRoleRef)
roleName=currentValue.trim();
if (inAuthConstraint)
inAuthConstraintRoles.add(currentValue.trim());;
if (inSecRole)
inSecRoleRoles.add(currentValue.trim());
}
if (ROLE_LINK.endsWith(localName)) {
inSecRoleRefRoles.put(roleName,currentValue.trim());
}
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attrs)
throws SAXException {
if (SECURITY_ROLE_REF.equals(localName)) {
inSecRoleRef=true;
}
if (AUTH_CONSTRAINT.equals(localName)) {
inAuthConstraint=true;
}
if (SECURITY_ROLE.equals(localName)) {
inSecRole=true;
}
}
@Override
public void endPrefixMapping(String arg0) throws SAXException {
}
@Override
public void ignorableWhitespace(char[] arg0, int arg1, int arg2) throws SAXException {
}
@Override
public void processingInstruction(String arg0, String arg1) throws SAXException {
}
@Override
public void setDocumentLocator(Locator arg0) {
}
@Override
public void skippedEntity(String arg0) throws SAXException {
}
@Override
public void startDocument() throws SAXException {
}
@Override
public void startPrefixMapping(String arg0, String arg1) throws SAXException {
}
public Map<String, String> getInSecRoleRefRoles() {
return inSecRoleRefRoles;
}
public List<String> getInAuthConstraintRoles() {
return inAuthConstraintRoles;
}
public List<String> getInSecRoleRoles() {
return inSecRoleRoles;
}
};
protected static Logger LOGGER = Logging.getLogger("org.geoserver.security");
protected String adminRoleName, groupAdminRoleName;
protected SortedSet<GeoServerRole> emptySet;
protected SortedSet<String> emptyStringSet;
protected Map<String,String> parentMappings;
protected HashMap<String, GeoServerRole> roleMap;
protected SortedSet<GeoServerRole> roleSet;
protected Set<RoleLoadedListener> listeners =
Collections.synchronizedSet(new HashSet<RoleLoadedListener>());
protected GeoServerJ2eeRoleService() throws IOException{
emptySet=Collections.unmodifiableSortedSet(new TreeSet<GeoServerRole>());
emptyStringSet=Collections.unmodifiableSortedSet(new TreeSet<String>());
parentMappings=new HashMap<String,String>();
load();
}
@Override
public void initializeFromConfig(SecurityNamedServiceConfig config) throws IOException {
super.initializeFromConfig(config);
adminRoleName = ((SecurityRoleServiceConfig)config).getAdminRoleName();
groupAdminRoleName = ((SecurityRoleServiceConfig)config).getGroupAdminRoleName();
load();
}
@Override
public GeoServerRole getAdminRole() {
if (StringUtils.hasLength(adminRoleName)==false)
return null;
try {
return getRoleByName(adminRoleName);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public GeoServerRole getGroupAdminRole() {
if (StringUtils.hasLength(groupAdminRoleName)==false)
return null;
try {
return getRoleByName(groupAdminRoleName);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public GeoServerRoleStore createStore() throws IOException {
return null;
}
/* (non-Javadoc)
* @see org.geoserver.security.GeoserverRoleService#registerRoleLoadedListener(org.geoserver.security.event.RoleLoadedListener)
*/
public void registerRoleLoadedListener (RoleLoadedListener listener) {
listeners.add(listener);
}
/* (non-Javadoc)
* @see org.geoserver.security.GeoserverRoleService#unregisterRoleLoadedListener(org.geoserver.security.event.RoleLoadedListener)
*/
public void unregisterRoleLoadedListener (RoleLoadedListener listener) {
listeners.remove(listener);
}
/* (non-Javadoc)
* @see org.geoserver.security.GeoserverRoleService#getRoles()
*/
public SortedSet<GeoServerRole> getRoles() throws IOException{
if (roleSet!=null)
return roleSet;
return emptySet;
}
/* (non-Javadoc)
* @see org.geoserver.security.GeoserverRoleService#load()
*/
public synchronized void load() throws IOException{
// parse web.xml only once because it cannot change during runtime
if (roleMap!=null)
return;
LOGGER.info("Start reloading roles for service named "+getName());
File webXML = GeoServerExtensions.file( "WEB-INF/web.xml" );
if (webXML==null){
throw new IOException("Cannot open /WEB-INF/web.xml");
}
LOGGER.info("Extracting roles from: "+webXML.getCanonicalPath());
Set<String> roles = parseWebXML(webXML);
roleMap = new HashMap<String,GeoServerRole>();
for (String role : roles) {
roleMap.put(role,createRoleObject(role));
parentMappings.put(role,null);
}
roleSet = new TreeSet<GeoServerRole>();
roleSet.addAll(roleMap.values());
LOGGER.info("Reloading roles successful for service named "+getName());
fireRoleLoadedEvent();
}
protected Set<String> parseWebXML (File file) throws IOException {
WebXMLContentHandler handler = new WebXMLContentHandler();
Set<String> result = new HashSet<String>();
try {
XMLReader xmlReader = XMLReaderFactory.createXMLReader();
InputSource inputSource = new InputSource(new FileInputStream(file));
xmlReader.setContentHandler(handler);
// suppress validation
xmlReader.setEntityResolver(new EntityResolver() {
@Override
public InputSource resolveEntity(String publicId, String systemId) throws SAXException,
IOException {
return new InputSource(new StringReader(""));
}
});
xmlReader.parse(inputSource);
} catch (SAXException e) {
throw new IOException(e);
}
result.addAll(handler.getInAuthConstraintRoles());
result.addAll(handler.getInSecRoleRoles());
result.addAll(handler.getInSecRoleRefRoles().keySet());
result.addAll(handler.getInSecRoleRefRoles().values());
return result;
}
/* (non-Javadoc)
* @see org.geoserver.security.GeoserverRoleService#getRolesForUser(java.lang.String)
*/
public SortedSet<GeoServerRole> getRolesForUser(String username) throws IOException{
return emptySet;
}
/* (non-Javadoc)
* @see org.geoserver.security.GeoserverRoleService#getRolesForGroup(java.lang.String)
*/
public SortedSet<GeoServerRole> getRolesForGroup(String groupname) throws IOException{
return emptySet;
}
/* (non-Javadoc)
* @see org.geoserver.security.GeoserverRoleService#createRoleObject(java.lang.String)
*/
public GeoServerRole createRoleObject(String role) throws IOException{
return new GeoServerRole(role);
}
/* (non-Javadoc)
* @see org.geoserver.security.GeoserverRoleService#getParentRole(org.geoserver.security.impl.GeoserverRole)
*/
public GeoServerRole getParentRole(GeoServerRole role) throws IOException{
return null;
}
// protected void checkRole(GeoserverRole role) {
// if (roleMap.containsKey(role.getAuthority())==false)
// throw new IllegalArgumentException("Role: " + role.getAuthority()+ " does not exist");
// }
/* (non-Javadoc)
* @see org.geoserver.security.GeoserverRoleService#getRoleByName(java.lang.String)
*/
public GeoServerRole getRoleByName(String role) throws IOException {
if (roleMap!=null)
return roleMap.get(role);
return null;
}
/**
* Fire {@link RoleLoadedEvent} for all listeners
*/
protected void fireRoleLoadedEvent() {
RoleLoadedEvent event = new RoleLoadedEvent(this);
for (RoleLoadedListener listener : listeners) {
listener.rolesChanged(event);
}
}
/* (non-Javadoc)
* @see org.geoserver.security.GeoserverRoleService#getGroupNamesForRole(org.geoserver.security.impl.GeoserverRole)
*/
public SortedSet<String> getGroupNamesForRole(GeoServerRole role) throws IOException {
return emptyStringSet;
}
/* (non-Javadoc)
* @see org.geoserver.security.GeoserverRoleService#getUserNamesForRole(org.geoserver.security.impl.GeoserverRole)
*/
public SortedSet<String> getUserNamesForRole(GeoServerRole role) throws IOException{
return emptyStringSet;
}
/* (non-Javadoc)
* @see org.geoserver.security.GeoserverRoleService#getParentMappings()
*/
public Map<String,String> getParentMappings() throws IOException {
return parentMappings;
}
/** (non-Javadoc)
* @see org.geoserver.security.GeoServerRoleService#personalizeRoleParams(java.lang.String, java.util.Properties, java.lang.String, java.util.Properties)
*
* Do nothing, J2EE roles have no role params
*/
public Properties personalizeRoleParams (String roleName,Properties roleParams,
String userName,Properties userProps) throws IOException {
return null;
}
/**
* The root configuration for the role service.
*/
public Resource getConfigRoot() throws IOException {
return getSecurityManager().role().get(getName());
}
public int getRoleCount() throws IOException {
if (roleSet != null)
return roleSet.size();
return 0;
}
}