/* (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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.logging.Logger;
import org.geoserver.platform.resource.Resource;
import org.geoserver.security.GeoServerSecurityManager;
import org.geoserver.security.GeoServerRoleService;
import org.geoserver.security.GeoServerRoleStore;
import org.geoserver.security.config.SecurityNamedServiceConfig;
import org.geoserver.security.event.RoleLoadedListener;
/**
* Abstract base class for role store implementations
*
* @author christian
*
*/
public abstract class AbstractRoleStore implements GeoServerRoleStore {
/** logger */
static Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.geoserver.security");
private boolean modified=false;
protected AbstractRoleService service;
protected RoleStoreHelper helper;
public AbstractRoleStore() {
helper=new RoleStoreHelper();
}
public String getName() {
return service.getName();
}
public void setName(String name) {
service.setName(name);
}
public GeoServerSecurityManager getSecurityManager() {
return service.getSecurityManager();
}
public void setSecurityManager(GeoServerSecurityManager securityManager) {
service.setSecurityManager(securityManager);
}
public boolean canCreateStore() {
return service.canCreateStore();
}
public GeoServerRole getAdminRole() {
return service.getAdminRole();
}
@Override
public GeoServerRole getGroupAdminRole() {
return service.getGroupAdminRole();
}
public GeoServerRoleStore createStore() throws IOException {
return service.createStore();
}
public void registerRoleLoadedListener(RoleLoadedListener listener) {
service.registerRoleLoadedListener(listener);
}
public void unregisterRoleLoadedListener(RoleLoadedListener listener) {
service.unregisterRoleLoadedListener(listener);
}
public GeoServerRole createRoleObject(String role) throws IOException {
return service.createRoleObject(role);
}
public Resource getConfigRoot() throws IOException {
return service.getConfigRoot();
}
/* (non-Javadoc)
* @see org.geoserver.security.GeoserverRoleStore#isModified()
*/
public boolean isModified() {
return modified;
}
/**
* Setter for modified flag
* @param value
*/
public void setModified(Boolean value) {
modified=value;
}
/* (non-Javadoc)
* @see org.geoserver.security.GeoserverRoleStore#addRole(org.geoserver.security.impl.GeoserverRole)
*/
public void addRole(GeoServerRole role) throws IOException{
if(helper.roleMap.containsKey(role.getAuthority()))
throw new IllegalArgumentException("The role " + role.getAuthority() + " already exists");
else {
helper.roleMap.put(role.getAuthority(),role);
setModified(true);
}
}
/* (non-Javadoc)
* @see org.geoserver.security.GeoserverRoleStore#updateRole(org.geoserver.security.impl.GeoserverRole)
*/
public void updateRole(GeoServerRole role) throws IOException {
if(helper.roleMap.containsKey(role.getAuthority())) {
helper.roleMap.put(role.getAuthority(),role);
setModified(true);
}
else
throw new IllegalArgumentException("The role " + role.getAuthority() + " does not exist");
}
/* (non-Javadoc)
* @see org.geoserver.security.GeoserverRoleStore#removeRole(org.geoserver.security.impl.GeoserverRole)
*/
public boolean removeRole(GeoServerRole role) throws IOException{
if (helper.roleMap.containsKey(role.getAuthority())==false) // nothing to do
return false;
for (SortedSet<GeoServerRole> set: helper.user_roleMap.values()) {
set.remove(role);
}
for (SortedSet<GeoServerRole> set: helper.group_roleMap.values()) {
set.remove(role);
}
// role hierarchy
helper.role_parentMap.remove(role);
Set<GeoServerRole> toBeRemoved = new HashSet<GeoServerRole>();
for (Entry<GeoServerRole,GeoServerRole> entry : helper.role_parentMap.entrySet()) {
if (role.equals(entry.getValue()))
toBeRemoved.add(entry.getKey());
}
for (GeoServerRole ga : toBeRemoved) {
helper.role_parentMap.put(ga,null);
}
// remove role
helper.roleMap.remove(role.getAuthority());
setModified(true);
return true;
}
/* (non-Javadoc)
* @see org.geoserver.security.GeoserverRoleStore#store()
*/
public void store() throws IOException {
if (isModified()) {
LOGGER.info("Start storing roles for service named "+getName());
// prevent concurrent write from store and
// read from service
synchronized (service) {
serialize();
}
setModified(false);
LOGGER.info("Storing roles successful for service named "+getName());
service.load(); // service must reload
} else {
LOGGER.info("Storing unnecessary, no change for roles");
}
}
/**
* Subclasses must implement this method
* Save role assignments to backend
*/
protected abstract void serialize() throws IOException;
/* (non-Javadoc)
* @see org.geoserver.security.GeoserverRoleStore#disAssociateRoleFromGroup(org.geoserver.security.impl.GeoserverRole, java.lang.String)
*/
public void disAssociateRoleFromGroup(GeoServerRole role, String groupname) throws IOException{
SortedSet<GeoServerRole> roles = helper.group_roleMap.get(groupname);
if (roles!=null && roles.contains(role)) {
roles.remove(role);
setModified(true);
}
}
/* (non-Javadoc)
* @see org.geoserver.security.GeoserverRoleStore#associateRoleToGroup(org.geoserver.security.impl.GeoserverRole, java.lang.String)
*/
public void associateRoleToGroup(GeoServerRole role, String groupname) throws IOException{
SortedSet<GeoServerRole> roles = helper.group_roleMap.get(groupname);
if (roles == null) {
roles=new TreeSet<GeoServerRole>();
helper.group_roleMap.put(groupname, roles);
}
if (roles.contains(role)==false) { // something changed ?
roles.add(role);
setModified(true);
}
}
/* (non-Javadoc)
* @see org.geoserver.security.GeoserverRoleStore#associateRoleToUser(org.geoserver.security.impl.GeoserverRole, java.lang.String)
*/
public void associateRoleToUser(GeoServerRole role, String username) throws IOException{
SortedSet<GeoServerRole> roles = helper.user_roleMap.get(username);
if (roles == null) {
roles=new TreeSet<GeoServerRole>();
helper.user_roleMap.put(username, roles);
}
if (roles.contains(role)==false) { // something changed
roles.add(role);
setModified(true);
}
}
/* (non-Javadoc)
* @see org.geoserver.security.GeoserverRoleStore#disAssociateRoleFromUser(org.geoserver.security.impl.GeoserverRole, java.lang.String)
*/
public void disAssociateRoleFromUser(GeoServerRole role, String username) throws IOException{
SortedSet<GeoServerRole> roles = helper.user_roleMap.get(username);
if (roles!=null && roles.contains(role)) {
roles.remove(role);
setModified(true);
}
}
/* (non-Javadoc)
* @see org.geoserver.security.GeoserverRoleStore#setParentRole(org.geoserver.security.impl.GeoserverRole, org.geoserver.security.impl.GeoserverRole)
*/
public void setParentRole(GeoServerRole role, GeoServerRole parentRole) throws IOException{
RoleHierarchyHelper hhelper = new RoleHierarchyHelper(getParentMappings());
if (hhelper.isValidParent(role.getAuthority(),
parentRole==null ? null : parentRole.getAuthority())==false)
throw new IOException(parentRole.getAuthority() +
" is not a valid parent for " + role.getAuthority());
checkRole(role);
if (parentRole==null) {
helper.role_parentMap.remove(role);
} else {
checkRole(parentRole);
helper.role_parentMap.put(role,parentRole);
}
setModified(true);
}
/* (non-Javadoc)
* @see org.geoserver.security.GeoserverRoleStore#clear()
*/
public void clear() throws IOException {
clearMaps();
setModified(true);
}
/**
* Make a deep copy (using serialization) from the
* service to the store.
*
*
* @see org.geoserver.security.impl.AbstractRoleService#deserialize()
*/
@SuppressWarnings("unchecked")
protected void deserialize() throws IOException {
// make a deep copy of the maps using serialization
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream oout = new ObjectOutputStream(out);
oout.writeObject(service.helper.roleMap);
oout.writeObject(service.helper.role_parentMap);
oout.writeObject(service.helper.user_roleMap);
oout.writeObject(service.helper.group_roleMap);
byte[] byteArray=out.toByteArray();
oout.close();
clearMaps();
ByteArrayInputStream in = new ByteArrayInputStream(byteArray);
ObjectInputStream oin = new ObjectInputStream(in);
try {
helper.roleMap = (TreeMap<String,GeoServerRole>) oin.readObject();
helper.role_parentMap =(HashMap<GeoServerRole,GeoServerRole>) oin.readObject();
helper.user_roleMap = (TreeMap<String,SortedSet<GeoServerRole>>)oin.readObject();
helper.group_roleMap = (TreeMap<String,SortedSet<GeoServerRole>>)oin.readObject();
} catch (ClassNotFoundException e) {
throw new IOException(e);
}
setModified(false);
}
/* (non-Javadoc)
* @see org.geoserver.security.GeoserverRoleStore#initializeFromService(org.geoserver.security.GeoserverRoleService)
*/
@Override
public void initializeFromService(GeoServerRoleService service)
throws IOException {
this.service=(AbstractRoleService)service;
load();
}
/* (non-Javadoc)
* @see org.geoserver.security.GeoServerSecurityService#initializeFromConfig(org.geoserver.security.config.SecurityNamedServiceConfig)
*/
@Override
public void initializeFromConfig(SecurityNamedServiceConfig config) throws IOException {
service.initializeFromConfig(config);
}
protected void checkRole(GeoServerRole role) {
if (helper.roleMap.containsKey(role.getAuthority())==false)
throw new IllegalArgumentException("Role: " + role.getAuthority()+ " does not exist");
}
/**
* internal use, clear the maps
*/
protected void clearMaps() {
helper.clearMaps();
}
@Override
public SortedSet<String> getGroupNamesForRole(GeoServerRole role) throws IOException {
return helper.getGroupNamesForRole(role);
}
@Override
public SortedSet<String> getUserNamesForRole(GeoServerRole role) throws IOException {
return helper.getUserNamesForRole(role);
}
@Override
public SortedSet<GeoServerRole> getRolesForUser(String username) throws IOException {
return helper.getRolesForUser(username);
}
@Override
public SortedSet<GeoServerRole> getRolesForGroup(String groupname) throws IOException {
return helper.getRolesForGroup(groupname);
}
@Override
public SortedSet<GeoServerRole> getRoles() throws IOException {
return helper.getRoles();
}
@Override
public Map<String, String> getParentMappings() throws IOException {
return helper.getParentMappings();
}
@Override
public GeoServerRole getParentRole(GeoServerRole role) throws IOException {
return helper.getParentRole(role);
}
@Override
public GeoServerRole getRoleByName(String role) throws IOException {
return helper.getRoleByName(role);
}
@Override
public void load() throws IOException {
deserialize();
}
@Override
public Properties personalizeRoleParams(String roleName, Properties roleParams,
String userName, Properties userProps) throws IOException {
return service.personalizeRoleParams(roleName, roleParams, userName, userProps);
}
public int getRoleCount() throws IOException {
return helper.getRoleCount();
}
}