org.geoserver.security.GeoServerSecurityManager.java Source code

Java tutorial

Introduction

Here is the source code for org.geoserver.security.GeoServerSecurityManager.java

Source

/* Copyright (c) 2001 - 2013 OpenPlans - 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 static org.geoserver.data.util.IOUtils.xStreamPersist;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.rmi.server.UID;
import java.security.InvalidKeyException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.LinkedHashMap;
import java.util.Properties;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.StoreInfo;
import org.geoserver.config.GeoServerDataDirectory;
import org.geoserver.config.util.XStreamPersister;
import org.geoserver.config.util.XStreamPersisterFactory;
import org.geoserver.filters.GeoServerFilter;
import org.geoserver.platform.ContextLoadedEvent;
import org.geoserver.platform.GeoServerExtensions;
import org.geoserver.security.auth.AuthenticationCache;
import org.geoserver.security.auth.AuthenticationCacheImpl;
import org.geoserver.security.auth.GeoServerRootAuthenticationProvider;
import org.geoserver.security.auth.LRUAuthenticationCacheImpl;
import org.geoserver.security.auth.UsernamePasswordAuthenticationProvider;
import org.geoserver.security.concurrent.LockingKeyStoreProvider;
import org.geoserver.security.concurrent.LockingRoleService;
import org.geoserver.security.concurrent.LockingUserGroupService;
import org.geoserver.security.config.AnonymousAuthenticationFilterConfig;
import org.geoserver.security.config.BasicAuthenticationFilterConfig;
import org.geoserver.security.config.ExceptionTranslationFilterConfig;
import org.geoserver.security.config.FileBasedSecurityServiceConfig;
import org.geoserver.security.config.RoleFilterConfig;
import org.geoserver.security.config.LogoutFilterConfig;
import org.geoserver.security.config.PasswordPolicyConfig;
import org.geoserver.security.config.RememberMeAuthenticationFilterConfig;
import org.geoserver.security.config.SSLFilterConfig;
import org.geoserver.security.config.SecurityAuthProviderConfig;
import org.geoserver.security.config.SecurityConfig;
import org.geoserver.security.config.SecurityContextPersistenceFilterConfig;
import org.geoserver.security.config.SecurityFilterConfig;
import org.geoserver.security.config.SecurityInterceptorFilterConfig;
import org.geoserver.security.config.SecurityManagerConfig;
import org.geoserver.security.config.SecurityNamedServiceConfig;
import org.geoserver.security.config.SecurityRoleServiceConfig;
import org.geoserver.security.config.SecurityUserGroupServiceConfig;
import org.geoserver.security.config.UsernamePasswordAuthenticationFilterConfig;
import org.geoserver.security.config.UsernamePasswordAuthenticationProviderConfig;
import org.geoserver.security.file.FileWatcher;
import org.geoserver.security.file.RoleFileWatcher;
import org.geoserver.security.file.UserGroupFileWatcher;
import org.geoserver.security.filter.GeoServerPreAuthenticationFilter;
import org.geoserver.security.filter.GeoServerAnonymousAuthenticationFilter;
import org.geoserver.security.filter.GeoServerBasicAuthenticationFilter;
import org.geoserver.security.filter.GeoServerExceptionTranslationFilter;
import org.geoserver.security.filter.GeoServerLogoutFilter;
import org.geoserver.security.filter.GeoServerRememberMeAuthenticationFilter;
import org.geoserver.security.filter.GeoServerRoleFilter;
import org.geoserver.security.filter.GeoServerSSLFilter;
import org.geoserver.security.filter.GeoServerSecurityContextPersistenceFilter;
import org.geoserver.security.filter.GeoServerSecurityFilter;
import org.geoserver.security.filter.GeoServerSecurityInterceptorFilter;
import org.geoserver.security.filter.GeoServerUserNamePasswordAuthenticationFilter;
import org.geoserver.security.impl.DataAccessRuleDAO;
import org.geoserver.security.impl.GeoServerRole;
import org.geoserver.security.impl.GeoServerUser;
import org.geoserver.security.impl.GeoServerUserGroup;
import org.geoserver.security.impl.GroupAdminProperty;
import org.geoserver.security.impl.ServiceAccessRuleDAO;
import org.geoserver.security.impl.Util;
import org.geoserver.security.password.ConfigurationPasswordEncryptionHelper;
import org.geoserver.security.password.GeoServerDigestPasswordEncoder;
import org.geoserver.security.password.GeoServerPBEPasswordEncoder;
import org.geoserver.security.password.GeoServerPasswordEncoder;
import org.geoserver.security.password.MasterPasswordChangeRequest;
import org.geoserver.security.password.MasterPasswordConfig;
import org.geoserver.security.password.MasterPasswordProviderConfig;
import org.geoserver.security.password.PasswordValidator;
import org.geoserver.security.password.RandomPasswordProvider;
import org.geoserver.security.password.URLMasterPasswordProvider;
import org.geoserver.security.password.URLMasterPasswordProviderConfig;
import org.geoserver.security.rememberme.GeoServerTokenBasedRememberMeServices;
import org.geoserver.security.rememberme.RememberMeServicesConfig;
import org.geoserver.security.validation.MasterPasswordChangeException;
import org.geoserver.security.validation.MasterPasswordChangeValidator;
import org.geoserver.security.validation.MasterPasswordConfigValidator;
import org.geoserver.security.validation.PasswordPolicyException;
import org.geoserver.security.validation.PasswordValidatorImpl;
import org.geoserver.security.validation.SecurityConfigException;
import org.geoserver.security.validation.SecurityConfigValidator;
import org.geoserver.security.xml.XMLConstants;
import org.geoserver.security.xml.XMLRoleService;
import org.geoserver.security.xml.XMLRoleServiceConfig;
import org.geoserver.security.xml.XMLRoleStore;
import org.geoserver.security.xml.XMLUserGroupService;
import org.geoserver.security.xml.XMLUserGroupServiceConfig;
import org.geotools.util.logging.Logging;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.security.authentication.AnonymousAuthenticationProvider;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.RememberMeAuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.memory.UserAttribute;
import org.springframework.security.core.userdetails.memory.UserAttributeEditor;
import org.springframework.security.web.authentication.RememberMeServices;
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
import org.springframework.util.StringUtils;
import org.vfny.geoserver.crs.GeoserverGridShiftLocator;

import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.converters.collections.AbstractCollectionConverter;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.mapper.Mapper;

/**
 * Top level singleton/facade/dao for the security authentication/authorization subsystem.  
 * 
 * 
 * @author Justin Deoliveira, OpenGeo
 *
 */
public class GeoServerSecurityManager extends ProviderManager
        implements ApplicationContextAware, ApplicationListener {

    static Logger LOGGER = Logging.getLogger("org.geoserver.security");

    /** default config file name */
    public static final String CONFIG_FILENAME = "config.xml";

    /** master password config file name */
    public static final String MASTER_PASSWD_CONFIG_FILENAME = "masterpw.xml";

    /** master password info file name */
    public static final String MASTER_PASSWD_INFO_FILENAME = "masterpw.info";

    /** master password digest file name */
    public static final String MASTER_PASSWD_DIGEST_FILENAME = "masterpw.digest";

    /** default master password */
    public static final char[] MASTER_PASSWD_DEFAULT = "geoserver".toCharArray();

    /** data directory file system access */
    GeoServerDataDirectory dataDir;

    /** app context for loading plugins */
    ApplicationContext appContext;

    /** the active role service */
    GeoServerRoleService activeRoleService;

    /** configured authentication providers */
    List<GeoServerAuthenticationProvider> authProviders;

    /** current security config */
    SecurityManagerConfig securityConfig = new SecurityManagerConfig();

    /** current master password config */
    MasterPasswordConfig masterPasswordConfig = new MasterPasswordConfig();

    /** digested master password */
    volatile String masterPasswdDigest;

    /** cached user groups */
    ConcurrentHashMap<String, GeoServerUserGroupService> userGroupServices = new ConcurrentHashMap<String, GeoServerUserGroupService>();

    /** cached role services */
    ConcurrentHashMap<String, GeoServerRoleService> roleServices = new ConcurrentHashMap<String, GeoServerRoleService>();

    /** cached password validators services */
    ConcurrentHashMap<String, PasswordValidator> passwordValidators = new ConcurrentHashMap<String, PasswordValidator>();

    /** some helper instances for storing/loading service config */
    RoleServiceHelper roleServiceHelper = new RoleServiceHelper();
    UserGroupServiceHelper userGroupServiceHelper = new UserGroupServiceHelper();
    AuthProviderHelper authProviderHelper = new AuthProviderHelper();
    FilterHelper filterHelper = new FilterHelper();
    PasswordValidatorHelper passwordValidatorHelper = new PasswordValidatorHelper();
    MasterPasswordProviderHelper masterPasswordProviderHelper = new MasterPasswordProviderHelper();

    /** helper for encrypting store configuration parameters */
    ConfigurationPasswordEncryptionHelper configPasswordEncryptionHelper;

    /**
     * listeners
     */
    List<SecurityManagerListener> listeners = new ArrayList<SecurityManagerListener>();

    /** cached flag determining is strong cryptography is available */
    Boolean strongEncryptionAvaialble;

    /** flag set once the security manager has been fully initialized */
    boolean initialized = false;

    /** keystore provider, loaded lazily */
    volatile KeyStoreProvider keyStoreProvider;

    /** generator of random passwords */
    RandomPasswordProvider randomPasswdProvider = new RandomPasswordProvider();

    /** authentication cache */
    volatile AuthenticationCache authCache;

    /** rememmber me service */
    volatile RememberMeServices rememberMeService;

    public static final String REALM = "GeoServer Realm";

    public GeoServerSecurityManager(GeoServerDataDirectory dataDir) throws Exception {
        this.dataDir = dataDir;
        setEraseCredentialsAfterAuthentication(true);

        /*
         * JD we have to ensure that the master password is initialized first thing, before the 
         * catalog since we need to decrypt configuration the passwords, the rest of the security 
         * initializes occurs at the end of startup  
         */
        File masterpw = new File(getSecurityRoot(), MASTER_PASSWD_CONFIG_FILENAME);
        if (masterpw.exists()) {
            init(loadMasterPasswordConfig());
        } else {
            //if it doesn't exist this must be a migration startup... and this case should be
            // handled during migration where all the datastore passwords are processed 
            // explicitly
        }

        configPasswordEncryptionHelper = new ConfigurationPasswordEncryptionHelper(this);
    }

    public Catalog getCatalog() {
        //have to look this up dynamically on demand on avoid circular dependency on application
        // context startup
        return (Catalog) GeoServerExtensions.bean("catalog");
    }

    public ConfigurationPasswordEncryptionHelper getConfigPasswordEncryptionHelper() {
        return configPasswordEncryptionHelper;
    }

    @Override
    public void setApplicationContext(ApplicationContext appContext) throws BeansException {
        this.appContext = appContext;
    }

    public ApplicationContext getApplicationContext() {
        return appContext;
    }

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ContextLoadedEvent) {

            try {
                File masterPasswordInfo = new File(getSecurityRoot(), MASTER_PASSWD_INFO_FILENAME);
                if (masterPasswordInfo.exists()) {
                    LOGGER.warning(masterPasswordInfo.getCanonicalPath()
                            + " is a security risk. Please read this file and remove it afterward");
                }
            } catch (Exception e1) {
                throw new RuntimeException(e1);
            }

            // migrate from old security config
            try {
                boolean migratedFrom21 = migrateFrom21();
                migrateFrom22(migratedFrom21);
                migrateFrom23();
            } catch (Exception e1) {
                throw new RuntimeException(e1);
            }

            // read config and initialize... we do this now since we can be ensured that the spring
            // context has been property initialized, and we can successfully look up security
            // plugins
            KeyStoreProvider keyStoreProvider = getKeyStoreProvider();
            try {
                // check for an outstanding masster password change
                keyStoreProvider.commitMasterPasswordChange();
                // check if there is an outstanding master password change in case of SPrin injection                 
                init();
                for (GeoServerSecurityProvider securityProvider : GeoServerExtensions
                        .extensions(GeoServerSecurityProvider.class)) {
                    securityProvider.init(this);
                }
            } catch (Exception e) {
                throw new BeanCreationException("Error occured reading security configuration", e);
            }

            try {
                afterPropertiesSetInternal();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        if (event instanceof ContextClosedEvent) {
            try {
                destroy();
            } catch (Exception e) {
                LOGGER.log(Level.WARNING, "Error destroying security manager", e);
            }
        }
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        //this is a bit o a hack but override and do nothing for now, we will call the super 
        // method later, after the app context is loaded, see afterPropertiesSetInternal()
    }

    void afterPropertiesSetInternal() throws Exception {
        super.afterPropertiesSet();
    }

    public void destroy() throws Exception {
        for (GeoServerSecurityProvider securityProvider : GeoServerExtensions
                .extensions(GeoServerSecurityProvider.class)) {
            securityProvider.destroy(this);
        }
        userGroupServices.clear();
        roleServices.clear();

        userGroupServiceHelper.destroy();
        roleServiceHelper.destroy();

        rememberMeService = null;
        keyStoreProvider = null;

        listeners.clear();

        appContext = null;
    }

    /**
     * Adds a listener to the security manager.
     */
    public void addListener(SecurityManagerListener listener) {
        listeners.add(listener);
    }

    /**
     * Removes a listener to the security manager.
     */
    public void removeListener(SecurityManagerListener listener) {
        listeners.remove(listener);
    }

    /**
     * List of active/configured authentication providers
     */
    public List<GeoServerAuthenticationProvider> getAuthenticationProviders() {
        return authProviders;
    }

    /*
     * loads configuration and initializes the security subsystem.
     */
    void init() throws Exception {
        init(loadMasterPasswordConfig());
        init(loadSecurityConfig());
        fireChanged();
    }

    void init(SecurityManagerConfig config) throws Exception {

        // load the master password provider

        //  prepare the keystore providing needed key material    
        getKeyStoreProvider().reloadKeyStore();

        //load the role authority and ensure it is properly configured
        String roleServiceName = config.getRoleServiceName();
        GeoServerRoleService roleService = null;
        try {
            roleService = loadRoleService(roleServiceName);

            //TODO:
            //if (!roleService.isConfigured()) {
            //    roleService = null;
            //}
        } catch (Exception e) {
            LOGGER.log(Level.WARNING,
                    String.format(
                            "Error occured loading role service %s, " + "falling back to default role service",
                            roleServiceName),
                    e);
        }

        if (roleService == null) {
            try {
                roleService = loadRoleService("default");
            } catch (Exception e) {
                throw new RuntimeException("Fatal error occurred loading default role service", e);
            }
        }

        //configure the user details instance
        setActiveRoleService(roleService);

        //set up authentication providers
        this.authProviders = new ArrayList<GeoServerAuthenticationProvider>();

        // first provider is for the root user
        GeoServerRootAuthenticationProvider rootAuthProvider = new GeoServerRootAuthenticationProvider();
        rootAuthProvider.setSecurityManager(this);
        rootAuthProvider.initializeFromConfig(null);
        this.authProviders.add(rootAuthProvider);

        //add the custom/configured ones
        if (!config.getAuthProviderNames().isEmpty()) {
            for (String authProviderName : config.getAuthProviderNames()) {
                //TODO: handle failure here... perhaps simply disabling when auth provider
                // fails to load?
                GeoServerAuthenticationProvider authProvider = authProviderHelper.load(authProviderName);
                authProviders.add(authProvider);
            }
        }

        List<AuthenticationProvider> allAuthProviders = new ArrayList<AuthenticationProvider>();
        allAuthProviders.addAll(authProviders);

        //anonymous, not needed  anymore
        //        if (config.isAnonymousAuth()) {
        //            AnonymousAuthenticationProvider aap = new AnonymousAuthenticationProvider();
        //            aap.setKey("geoserver");
        //            aap.afterPropertiesSet();
        //            allAuthProviders.add(aap);
        //        }

        //remember me
        RememberMeAuthenticationProvider rap = new RememberMeAuthenticationProvider();
        rap.setKey(config.getRememberMeService().getKey());
        rap.afterPropertiesSet();
        allAuthProviders.add(rap);

        setProviders(allAuthProviders);

        this.securityConfig = new SecurityManagerConfig(config);
        this.initialized = true;
    }

    void init(MasterPasswordConfig config) {
        this.masterPasswordConfig = new MasterPasswordConfig(config);
    }

    public KeyStoreProvider getKeyStoreProvider() {
        if (keyStoreProvider == null) {
            synchronized (this) {
                if (keyStoreProvider == null) {
                    keyStoreProvider = lookupKeyStoreProvider();
                }
            }
        }
        return keyStoreProvider;
    }

    KeyStoreProvider lookupKeyStoreProvider() {
        KeyStoreProvider ksp = GeoServerExtensions.bean(KeyStoreProvider.class);
        if (ksp == null) {
            // use default key store provider
            ksp = new KeyStoreProviderImpl();
        }

        ksp.setSecurityManager(this);
        return new LockingKeyStoreProvider(ksp);
    }

    public RandomPasswordProvider getRandomPassworddProvider() {
        return randomPasswdProvider;
    }

    public AuthenticationCache getAuthenticationCache() {
        if (authCache == null) {
            synchronized (this) {
                if (authCache == null) {
                    authCache = lookupAuthenticationCache();
                }
            }
        }
        return authCache;
    }

    AuthenticationCache lookupAuthenticationCache() {
        AuthenticationCache authCache = GeoServerExtensions.bean(AuthenticationCache.class);
        return authCache != null ? authCache : new LRUAuthenticationCacheImpl(1000);
    }

    public RememberMeServices getRememberMeService() {
        if (rememberMeService == null) {
            synchronized (this) {
                if (rememberMeService == null) {
                    rememberMeService = lookupRememberMeService();
                }
            }
        }
        return rememberMeService;
    }

    RememberMeServices lookupRememberMeService() {
        return (RememberMeServices) GeoServerExtensions.bean("rememberMeServices");
    }

    public DataAccessRuleDAO getDataAccessRuleDAO() {
        return DataAccessRuleDAO.get();
    }

    public ServiceAccessRuleDAO getServiceAccessRuleDAO() {
        return ServiceAccessRuleDAO.get();
    }

    /**
     * Determines if the security manager has been initialized yet. 
     * <p>
     * TODO: this is a temporary hack, perhaps we should think about initializing the security 
     * subsystem as the very first thing on startup... but now we have dependencies on the catalog
     * so we cant. 
     * </p>
     */
    public boolean isInitialized() {
        return initialized;
    }

    /**
     * Security configuration root directory.
     */
    public File getSecurityRoot() throws IOException {
        return dataDir.findOrCreateSecurityRoot();
    }

    /**
     * Role configuration root directory.
     */
    public File getRoleRoot() throws IOException {
        return getRoleRoot(true);
    }

    public File getRoleRoot(boolean create) throws IOException {
        return create ? dataDir.findOrCreateSecurityDir("role") : dataDir.findSecurityDir("role");
    }

    /**
     * Password policy configuration root directory
     */
    public File getPasswordPolicyRoot() throws IOException {
        return dataDir.findOrCreateSecurityDir("pwpolicy");
    }

    /**
     * User/group configuration root directory.
     */
    public File getUserGroupRoot() throws IOException {
        return dataDir.findOrCreateSecurityDir("usergroup");

    }

    /**
     * authentication configuration root directory.
     */
    public File getAuthRoot() throws IOException {
        return dataDir.findOrCreateSecurityDir("auth");
    }

    /**
     * authentication filter root directory.
     */
    public File getFilterRoot() throws IOException {
        return dataDir.findOrCreateSecurityDir("filter");
    }

    /**
     * master password provider root
     */
    public File getMasterPasswordProviderRoot() throws IOException {
        return dataDir.findOrCreateSecurityDir("masterpw");
    }

    /**
     * Lists all available role service configurations.
     */
    public SortedSet<String> listRoleServices() throws IOException {
        return listFiles(getRoleRoot());
    }

    /**
     * Loads a role service from a named configuration.
     * 
     * @param name The name of the role service configuration.
     */
    public GeoServerRoleService loadRoleService(String name) throws IOException {
        GeoServerRoleService roleService = roleServices.get(name);
        if (roleService == null) {
            synchronized (this) {
                roleService = roleServices.get(name);
                if (roleService == null) {
                    roleService = roleServiceHelper.load(name);
                    if (roleService != null) {
                        roleServices.put(name, roleService);
                    }
                }
            }
        }

        return wrapRoleService(roleService);
    }

    GeoServerRoleService wrapRoleService(GeoServerRoleService roleService) throws IOException {
        if (!initialized) {
            //starting up
            return roleService;
        }

        //check for group administrator and wrap accordingly
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (checkAuthenticationForAdminRole(auth)) {
            //admin, no need to wrap
            return roleService;
        }

        //check for group admin
        if (checkAuthenticationForRole(auth, GeoServerRole.GROUP_ADMIN_ROLE)) {
            roleService = new GroupAdminRoleService(roleService,
                    calculateAdminGroups((UserDetails) auth.getPrincipal()));
        }
        return roleService;
    }

    List<String> calculateAdminGroups(UserDetails userDetails) throws IOException {
        if (userDetails instanceof GeoServerUser) {
            Properties props = ((GeoServerUser) userDetails).getProperties();
            if (GroupAdminProperty.has(props)) {
                return Arrays.asList(GroupAdminProperty.get(props));
            }
        }

        //fall back on including every group the user is part of
        List<String> groupNames = new ArrayList<String>();
        for (GeoServerUserGroupService ugService : loadUserGroupServices()) {
            GeoServerUser user = ugService.getUserByUsername(userDetails.getUsername());
            if (user != null) {
                for (GeoServerUserGroup group : ugService.getGroupsForUser(user)) {
                    groupNames.add(group.getGroupname());
                }
            }

        }
        return groupNames;
    }

    /**
     * Loads a role {@link SecurityRoleServiceConfig} from a named configuration.
     * <code>null</code> if not found
     * 
     * @param name The name of the role service configuration.
     */
    public SecurityRoleServiceConfig loadRoleServiceConfig(String name) throws IOException {
        return roleServiceHelper.loadConfig(name);
    }

    /**
     * Loads a password validator from a named configuration.
     * 
     * @param name The name of the password policy configuration.
     */
    public PasswordValidator loadPasswordValidator(String name) throws IOException {
        PasswordValidator validator = passwordValidators.get(name);
        if (validator == null) {
            synchronized (this) {
                validator = passwordValidators.get(name);
                if (validator == null) {
                    validator = passwordValidatorHelper.load(name);
                    if (validator != null) {
                        passwordValidators.put(name, validator);
                    }
                }
            }
        }
        return validator;
    }

    /**
     * Loads a password {@link PasswordPolicyConfig} from a named configuration.
     * <code>null</a> if not found
     * 
     * @param name The name of the password policy configuration.
     */
    public PasswordPolicyConfig loadPasswordPolicyConfig(String name) throws IOException {
        return passwordValidatorHelper.loadConfig(name);
    }

    /**
     * Loads a password encoder with the specified name.
     * 
     * @return The password encoder, or <code>null</code> if non found matching the name.
     */
    public GeoServerPasswordEncoder loadPasswordEncoder(String name) {
        GeoServerPasswordEncoder encoder = (GeoServerPasswordEncoder) GeoServerExtensions.bean(name);
        if (encoder != null) {
            try {
                encoder.initialize(this);
            } catch (IOException e) {
                throw new RuntimeException("Error occurred initializing password encoder");
            }
        }
        return encoder;
    }

    /**
     * Loads the first password encoder that matches the specified class filter.
     * <p>
     * This method is shorthand for:
     * <pre>
     *   loadPasswordEncoder(filter, null, null);
     * </pre>
     * </p> 
     *
     */
    public <T extends GeoServerPasswordEncoder> T loadPasswordEncoder(Class<T> filter) {
        return loadPasswordEncoder(filter, null, null);
    }

    /**
     * Loads the first password encoder that matches the specified criteria.
     * 
     * @param filter Class used to filter password encoders.
     * @param config Flag indicating if a reversible encoder is required, true forces reversible, 
     *  false forces irreversible, null means either.
     * @param strong Flag indicating if an encoder that supports strong encryption is required, true 
     *  forces strong encryption, false forces weak encryption, null means either.
     *  
     * @return The first encoder matching, or null if none was found.
     */
    public <T extends GeoServerPasswordEncoder> T loadPasswordEncoder(Class<T> filter, Boolean reversible,
            Boolean strong) {
        List<T> pw = loadPasswordEncoders(filter, reversible, strong);
        return pw.isEmpty() ? null : pw.get(0);
    }

    /**
     * Looks up all available password encoders.
     */
    public List<GeoServerPasswordEncoder> loadPasswordEncoders() {
        return loadPasswordEncoders(null);
    }

    /**
     * Looks up all available password encoders filtering out only those that are instances of the
     * specified class.
     * <p>
     * This method is convenience for:
     * <pre>
     * loadPasswordEncoders(filter, null, null)
     * </pre> 
     * </p>
     */
    public <T extends GeoServerPasswordEncoder> List<T> loadPasswordEncoders(Class<T> filter) {
        return loadPasswordEncoders(filter, null, null);
    }

    /**
     * Loads all the password encoders that match the specified criteria.
     * 
     * @param filter Class used to filter password encoders.
     * @param config Flag indicating if a reversible encoder is required, true forces reversible, 
     *  false forces irreversible, null means either.
     * @param strong Flag indicating if an encoder that supports strong encryption is required, true 
     *  forces strong encryption, false forces weak encryption, null means either.
     *  
     * @return All matching encoders, or an empty list.
     */
    public <T extends GeoServerPasswordEncoder> List<T> loadPasswordEncoders(Class<T> filter, Boolean reversible,
            Boolean strong) {

        filter = (Class<T>) (filter != null ? filter : GeoServerPasswordEncoder.class);

        List list = GeoServerExtensions.extensions(filter);
        for (Iterator it = list.iterator(); it.hasNext();) {
            boolean remove = false;
            T pw = (T) it.next();
            if (reversible != null && !reversible.equals(pw.isReversible())) {
                remove = true;
            }
            if (!remove && strong != null && strong.equals(pw.isAvailableWithoutStrongCryptogaphy())) {
                remove = true;
            }

            if (remove) {
                it.remove();
            } else {
                try {
                    pw.initialize(this);
                } catch (IOException e) {
                    LOGGER.log(Level.WARNING, "Error initializing password encoder " + pw.getName() + ", skipping",
                            e);
                    it.remove();
                }
            }
        }
        return list;
    }

    /**
     * Determines if strong encryption is available.
     * <p>
     * This method does the determination by trying to encrypt a value with AES 256 Bit encryption.
     * </p>
     * 
     * @return True if strong encryption avaialble, otherwise false.
     */
    public boolean isStrongEncryptionAvailable() {
        if (strongEncryptionAvaialble != null)
            return strongEncryptionAvaialble;

        KeyGenerator kgen;
        try {
            kgen = KeyGenerator.getInstance("AES");
            kgen.init(256);
            SecretKey skey = kgen.generateKey();
            byte[] raw = skey.getEncoded();
            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
            Cipher cipher = Cipher.getInstance("AES");

            cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
            cipher.doFinal("This is just an example".getBytes());
            strongEncryptionAvaialble = true;
            LOGGER.info("Strong cryptograhpy is available");
        } catch (InvalidKeyException e) {
            strongEncryptionAvaialble = false;
            LOGGER.warning("Strong cryptograhpy is NOT available"
                    + "\nDownload and install of policy files recommended"
                    + "\nfrom http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html");
        } catch (Exception ex) {
            LOGGER.log(Level.WARNING, "Strong cryptograhpy is NOT available, unexpected error", ex);
            strongEncryptionAvaialble = false; //should not happen
        }
        return strongEncryptionAvaialble;
    }

    /**
     * Saves/persists a role service configuration.
     */
    public void saveRoleService(SecurityRoleServiceConfig config) throws IOException, SecurityConfigException {
        SecurityConfigValidator validator = SecurityConfigValidator
                .getConfigurationValiator(GeoServerRoleService.class, config.getClassName());

        if (config.getId() == null) {
            config.initBeforeSave();
            validator.validateAddRoleService(config);
        } else {
            validator.validateModifiedRoleService(config, roleServiceHelper.loadConfig(config.getName()));
        }

        roleServiceHelper.saveConfig(config);
        // remove from cache
        roleServices.remove(config.getName());

        //update active role service 
        if (activeRoleService != null && config.getName().equals(activeRoleService.getName())) {
            synchronized (activeRoleService) {
                activeRoleService.initializeFromConfig(config);
            }
        }
    }

    /**
     * Saves/persists a password policy configuration.
     */
    public void savePasswordPolicy(PasswordPolicyConfig config) throws IOException, SecurityConfigException {
        SecurityConfigValidator validator = SecurityConfigValidator
                .getConfigurationValiator(PasswordValidator.class, config.getClassName());

        if (config.getId() == null) {
            config.initBeforeSave();
            validator.validateAddPasswordPolicy(config);
        } else {
            validator.validateModifiedPasswordPolicy(config, passwordValidatorHelper.loadConfig(config.getName()));
        }

        passwordValidatorHelper.saveConfig(config);
    }

    /**
     * Removes a role service configuration.
     * 
     * @param name The  role service configuration.
     */
    public void removeRoleService(SecurityRoleServiceConfig config) throws IOException, SecurityConfigException {

        SecurityConfigValidator validator = SecurityConfigValidator
                .getConfigurationValiator(GeoServerRoleService.class, config.getClassName());

        validator.validateRemoveRoleService(config);

        roleServices.remove(config.getName());
        roleServiceHelper.removeConfig(config.getName());
    }

    /**
     * Removes a password validator configuration.
     * 
     * @param  The  password validator configuration.
     */
    public void removePasswordValidator(PasswordPolicyConfig config) throws IOException, SecurityConfigException {
        SecurityConfigValidator validator = SecurityConfigValidator
                .getConfigurationValiator(PasswordValidator.class, config.getClassName());

        validator.validateRemovePasswordPolicy(config);
        passwordValidators.remove(config.getName());
        passwordValidatorHelper.removeConfig(config.getName());
    }

    /**
     * Lists all available user group service configurations.
     */
    public SortedSet<String> listUserGroupServices() throws IOException {
        return listFiles(getUserGroupRoot());
    }

    /**
     * Lists all available password Validators.
     */
    public SortedSet<String> listPasswordValidators() throws IOException {
        return listFiles(getPasswordPolicyRoot());
    }

    /**
     * Loads all available user group services.
     */
    public List<GeoServerUserGroupService> loadUserGroupServices() throws IOException {
        List<GeoServerUserGroupService> ugServices = new ArrayList<GeoServerUserGroupService>();

        for (String ugServiceName : listUserGroupServices()) {
            try {
                GeoServerUserGroupService ugService = userGroupServiceHelper.load(ugServiceName);
                ugServices.add(ugService);
            } catch (IOException e) {
                LOGGER.log(Level.WARNING, "Failed to load user group service " + ugServiceName, e);
            }
        }

        return ugServices;
    }

    /**
     * Loads a user group service from a named configuration.
     * 
     * @param name The name of the user group service configuration.
     */
    public GeoServerUserGroupService loadUserGroupService(String name) throws IOException {
        GeoServerUserGroupService ugService = userGroupServices.get(name);
        if (ugService == null) {
            synchronized (this) {
                ugService = userGroupServices.get(name);
                if (ugService == null) {
                    ugService = userGroupServiceHelper.load(name);
                    if (ugService != null) {
                        userGroupServices.put(name, ugService);
                    }
                }
            }
        }

        return wrapUserGroupService(ugService);
    }

    GeoServerUserGroupService wrapUserGroupService(GeoServerUserGroupService ugService) throws IOException {
        if (!initialized) {
            //starting up
            return ugService;
        }

        Authentication auth = SecurityContextHolder.getContext().getAuthentication();

        if (checkAuthenticationForAdminRole(auth)) {
            //full admin, no need to wrap
            return ugService;
        }

        //check for group administrator and wrap accordingly
        if (checkAuthenticationForRole(auth, GeoServerRole.GROUP_ADMIN_ROLE)) {
            ugService = new GroupAdminUserGroupService(ugService,
                    calculateAdminGroups((UserDetails) auth.getPrincipal()));
        }
        return ugService;
    }

    /**
     * Loads a user {@link SecurityUserGroupServiceConfig} from a named configuration.
     * <code>null</code> if not foun
     * 
     * @param name The name of the user group service configuration.
     */
    public SecurityUserGroupServiceConfig loadUserGroupServiceConfig(String name) throws IOException {
        return userGroupServiceHelper.loadConfig(name);
    }

    /**
     * Saves/persists a user group service configuration.
     */
    public void saveUserGroupService(SecurityUserGroupServiceConfig config)
            throws IOException, SecurityConfigException {
        SecurityConfigValidator validator = SecurityConfigValidator
                .getConfigurationValiator(GeoServerUserGroupService.class, config.getClassName());

        if (config.getId() == null) {
            config.initBeforeSave();
            validator.validateAddUserGroupService(config);
        } else {
            validator.validateModifiedUserGroupService(config, userGroupServiceHelper.loadConfig(config.getName()));
        }

        userGroupServiceHelper.saveConfig(config);
        // remove from cache
        userGroupServices.remove(config.getName());

    }

    /**
     * Removes a user group service configuration.
     * 
     * @param name The  user group service configuration.
     */
    public void removeUserGroupService(SecurityUserGroupServiceConfig config)
            throws IOException, SecurityConfigException {

        SecurityConfigValidator validator = SecurityConfigValidator
                .getConfigurationValiator(GeoServerUserGroupService.class, config.getClassName());

        validator.validateRemoveUserGroupService(config);

        userGroupServices.remove(config.getName());
        userGroupServiceHelper.removeConfig(config.getName());
    }

    /**
     * Lists all available authentication provider configurations.
     */
    public SortedSet<String> listAuthenticationProviders() throws IOException {
        return listFiles(getAuthRoot());
    }

    /**
     * Loads an authentication provider from a named configuration.
     * 
     * @param name The name of the authentication provider service configuration.
     */
    public GeoServerAuthenticationProvider loadAuthenticationProvider(String name) throws IOException {
        return authProviderHelper.load(name);
    }

    /**
     * Loads an authentication provider config from a named configuration.
     * <code>null</code> if not found
     * 
     * @param name The name of the authentication provider service configuration.
     */
    public SecurityAuthProviderConfig loadAuthenticationProviderConfig(String name) throws IOException {
        return authProviderHelper.loadConfig(name);
    }

    public void saveAuthenticationProvider(SecurityAuthProviderConfig config)
            throws IOException, SecurityConfigException {
        SecurityConfigValidator validator = SecurityConfigValidator
                .getConfigurationValiator(GeoServerAuthenticationProvider.class, config.getClassName());

        if (config.getId() == null) {
            config.initBeforeSave();
            validator.validateAddAuthProvider(config);
        } else {
            validator.validateModifiedAuthProvider(config, authProviderHelper.loadConfig(config.getName()));
        }

        //update the running auth providers
        if (authProviders != null) {
            GeoServerAuthenticationProvider authProvider = null;
            for (GeoServerAuthenticationProvider ap : authProviders) {
                if (config.getName().equals(ap.getName())) {
                    authProvider = ap;
                    break;
                }
            }

            if (authProvider != null) {
                synchronized (authProvider) {
                    authProvider.initializeFromConfig(config);
                }
            }
        }

        authProviderHelper.saveConfig(config);
    }

    /**
     * Checks if the currently authenticated user has the administrator role.
     * <p>
     * This method is shorthand for:
     * <code>
     * <pre>
     *   checkAuthenticationForAdminRole(SecurityContextHolder.getContext().getAuthentication())
     * </pre>
     * </code>
     * </p>
     */
    public boolean checkAuthenticationForAdminRole() {
        if (SecurityContextHolder.getContext() == null)
            return checkAuthenticationForAdminRole(null);
        else
            return checkAuthenticationForAdminRole(SecurityContextHolder.getContext().getAuthentication());
    }

    /**
     * Checks if the specified authentication has the administrator role.
     * <p>
     * This method is shorthand for:
     * <code>
     * <pre>
     *   checkAuthenticationForRole(auth, GeoServerRole.ADMIN_ROLE)
     * </pre>
     * </code>
     * </p>
     * 
     */
    public boolean checkAuthenticationForAdminRole(Authentication auth) {
        return checkAuthenticationForRole(auth, GeoServerRole.ADMIN_ROLE);
    }

    /**
     * Checks if the specified authentication contains the specified role.
     * 
     * If the current {@link HttpServletRequest} has security disabled,
     * this method always returns <code>true</code>.
     * 
     * @return <code>true</code> if the authenticated contains the role, otherwise <code>false</false>
     */
    public boolean checkAuthenticationForRole(Authentication auth, GeoServerRole role) {

        if (GeoServerSecurityFilterChainProxy.isSecurityEnabledForCurrentRequest() == false)
            return true; // No security means any role is granted

        if (auth == null || !auth.isAuthenticated()) {
            return false;
        }
        for (GrantedAuthority authority : auth.getAuthorities()) {
            if (role.getAuthority().equals(authority.getAuthority())) {
                return true;
            }
        }
        return false;
    }

    /**
     * Lists all available filter configurations.
     */
    public SortedSet<String> listFilters() throws IOException {
        return listFiles(getFilterRoot());
    }

    /**
     * Lists all available pre authentication filter configurations whose implentation class 
     * is an instance of the specified class.
     */
    public SortedSet<String> listFilters(Class<?> type) throws IOException {
        SortedSet<String> configs = new TreeSet<String>();
        for (String name : listFilters()) {
            SecurityFilterConfig config = (SecurityFilterConfig) loadFilterConfig(name);
            if (config.getClassName() == null) {
                continue;
            }

            try {
                if (type.isAssignableFrom(Class.forName(config.getClassName()))) {
                    configs.add(config.getName());
                }
            } catch (ClassNotFoundException e) {
                //ignore and continue
                LOGGER.log(Level.WARNING, e.getMessage(), e);
            }
        }
        return configs;
    }

    /**
     * Loads an authentication provider from a named configuration.
     * 
     * @param name The name of the authentication provider service configuration.
     */
    public GeoServerSecurityFilter loadFilter(String name) throws IOException {
        return filterHelper.load(name);
    }

    /**
     * Loads an authentication provider config from a named configuration.
     * <code>null</a> if not found
     * 
     * @param name The name of the authentication provider service configuration.
     */
    public SecurityFilterConfig loadFilterConfig(String name) throws IOException {
        return filterHelper.loadConfig(name);
    }

    public void saveFilter(SecurityNamedServiceConfig config) throws IOException, SecurityConfigException {

        SecurityConfigValidator validator = SecurityConfigValidator
                .getConfigurationValiator(GeoServerSecurityFilter.class, config.getClassName());

        boolean fireChanged = false;
        if (config.getId() == null) {
            config.initBeforeSave();
            validator.validateAddFilter(config);
        } else {
            validator.validateModifiedFilter(config, filterHelper.loadConfig(config.getName()));
            // remove all cached authentications for this filter
            getAuthenticationCache().removeAll(config.getName());
            if (!securityConfig.getFilterChain().patternsForFilter(config.getName(), true).isEmpty()) {
                fireChanged = true;
            }
        }

        filterHelper.saveConfig(config);
        if (fireChanged) {
            fireChanged();
        }
    }

    /**
     * Removes an authentication provider configuration.
     * 
     * @param name The  authentication provider configuration.
     */
    public void removeAuthenticationProvider(SecurityAuthProviderConfig config)
            throws IOException, SecurityConfigException {
        SecurityConfigValidator validator = SecurityConfigValidator
                .getConfigurationValiator(GeoServerAuthenticationProvider.class, config.getClassName());
        validator.validateRemoveAuthProvider(config);
        authProviderHelper.removeConfig(config.getName());
    }

    public void removeFilter(SecurityNamedServiceConfig config) throws IOException, SecurityConfigException {
        SecurityConfigValidator validator = SecurityConfigValidator
                .getConfigurationValiator(GeoServerSecurityFilter.class, config.getClassName());
        validator.validateRemoveFilter(config);
        getAuthenticationCache().removeAll(config.getName());
        filterHelper.removeConfig(config.getName());
    }

    /**
     * Returns the current security configuration.
     * <p>
     * In order to make changes to the security configuration client code may make changes to this 
     * object directly, but must call {@link #saveSecurityConfig(SecurityManagerConfig)} in order
     * to persist changes.
     * </p>
     */
    public SecurityManagerConfig getSecurityConfig() {
        return new SecurityManagerConfig(this.securityConfig);
    }

    public boolean isEncryptingUrlParams() {
        if (this.securityConfig == null)
            return false;
        return this.securityConfig.isEncryptingUrlParams();
    }

    /*
     * saves the global security config
     * TODO: use read/write lock rather than full synchronied
     */
    public synchronized void saveSecurityConfig(SecurityManagerConfig config) throws Exception {

        SecurityManagerConfig oldConfig = new SecurityManagerConfig(this.securityConfig);

        SecurityConfigValidator validator = new SecurityConfigValidator(this);
        validator.validateManagerConfig(config, oldConfig);

        //save the current config to fall back to                

        // The whole try block should run as a transaction, unfortunately
        // this is not possible with files.
        try {
            //set the new configuration
            init(config);
            if (config.getConfigPasswordEncrypterName()
                    .equals(oldConfig.getConfigPasswordEncrypterName()) == false) {
                updateConfigurationFilesWithEncryptedFields();
            }

            //save out new configuration
            xStreamPersist(new File(getSecurityRoot(), CONFIG_FILENAME), config, globalPersister());
        } catch (IOException e) {
            //exception, revert back to known working config
            LOGGER.log(Level.SEVERE, "Error saving security config, reverting back to previous", e);
            init(oldConfig);
            return;
        }

        fireChanged();
    }

    /**
     * Returns the master password configuration.
     */
    public MasterPasswordConfig getMasterPasswordConfig() {
        return new MasterPasswordConfig(masterPasswordConfig);
    }

    /**
     * Saves the master password configuration.
     * 
     * @param config The new configuration.
     * @param currPasswd The current master password.
     * @param newPasswd The new password, may be null depending on strategy used.
     * @param newPasswdConfirm The confirmation password
     * 
     * @throws MasterPasswordChangeException If there is a validation error with the new config 
     * @throws PasswordPolicyException If the new password violates the master password policy
     */
    public synchronized void saveMasterPasswordConfig(MasterPasswordConfig config, char[] currPasswd,
            char[] newPasswd, char[] newPasswdConfirm) throws Exception {

        //load the (possibly new) master password provider
        MasterPasswordProviderConfig mpProviderConfig = loadMasterPassswordProviderConfig(config.getProviderName());
        MasterPasswordProvider mpProvider = loadMasterPasswordProvider(config.getProviderName());

        if (mpProviderConfig.isReadOnly()) {
            //new password comes from the provider
            newPasswd = mpProvider.getMasterPassword();
        }

        //first validate the password change
        MasterPasswordChangeRequest req = new MasterPasswordChangeRequest();
        req.setCurrentPassword(currPasswd);
        req.setNewPassword(newPasswd);
        req.setConfirmPassword(newPasswdConfirm);

        MasterPasswordChangeValidator val = new MasterPasswordChangeValidator(this);
        val.validateChangeRequest(req);

        //validate the new config
        MasterPasswordConfigValidator validator = new MasterPasswordConfigValidator(this);
        validator.validateMasterPasswordConfig(config);

        //save the current config to fall back to                
        MasterPasswordConfig oldConfig = new MasterPasswordConfig(this.masterPasswordConfig);
        String oldMasterPasswdDigest = masterPasswdDigest;

        KeyStoreProvider ksProvider = getKeyStoreProvider();
        synchronized (ksProvider) {
            ksProvider.prepareForMasterPasswordChange(currPasswd, newPasswdConfirm);
            try {
                if (!mpProviderConfig.isReadOnly()) {
                    //write it back first
                    try {
                        mpProvider.setMasterPassword(newPasswd);
                    } catch (Exception e) {
                        throw new IOException(e);
                    }
                }

                //save out the master password config
                saveMasterPasswordConfig(config);

                //redigest 
                masterPasswdDigest = computeAndSaveMasterPasswordDigest(newPasswdConfirm);

                //commit the password change to the keystore
                ksProvider.commitMasterPasswordChange();

                if (!config.getProviderName().equals(oldConfig.getProviderName())) {
                    //TODO: reencrypt the keystore? restart the server?
                    //updateConfigurationFilesWithEncryptedFields();
                }
            } catch (IOException e) {
                //error occured, roll back
                ksProvider.abortMasterPasswordChange();

                //revert to old master password config
                this.masterPasswordConfig = oldConfig;
                this.masterPasswdDigest = oldMasterPasswdDigest;
                saveMasterPasswordDigest(oldMasterPasswdDigest);

                throw e;
            }
        }
    }

    /**
     * Saves master password config out directly, not during a password change.
     */
    public void saveMasterPasswordConfig(MasterPasswordConfig config) throws IOException {
        xStreamPersist(new File(getSecurityRoot(), MASTER_PASSWD_CONFIG_FILENAME), config, globalPersister());
        this.masterPasswordConfig = new MasterPasswordConfig(config);
    }

    /**
     * Checks the specified password against the master password. 
     */
    public boolean checkMasterPassword(String passwd) {
        return checkMasterPassword(passwd.toCharArray());
    }

    /**
     * Checks the specified password against the master password. 
     */
    public boolean checkMasterPassword(char[] passwd) {
        GeoServerDigestPasswordEncoder pwEncoder = loadPasswordEncoder(GeoServerDigestPasswordEncoder.class);
        if (masterPasswdDigest == null) {
            synchronized (this) {
                if (masterPasswdDigest == null) {
                    try {
                        //look for file
                        masterPasswdDigest = loadMasterPasswordDigest();
                    } catch (IOException e) {
                        throw new RuntimeException("Unable to create master password digest", e);
                    }
                }
            }
        }
        return pwEncoder.isPasswordValid(masterPasswdDigest, passwd, null);
    }

    String loadMasterPasswordDigest() throws IOException {
        //look for file
        File pwDigestFile = new File(getSecurityRoot(), MASTER_PASSWD_DIGEST_FILENAME);
        if (pwDigestFile.exists()) {
            FileInputStream fin = new FileInputStream(pwDigestFile);
            try {
                return IOUtils.toString(fin);
            } finally {
                fin.close();
            }
        } else {
            //compute and store
            char[] masterPasswd = getMasterPassword();
            try {
                return computeAndSaveMasterPasswordDigest(masterPasswd);
            } finally {
                disposePassword(masterPasswd);
            }
        }
    }

    void saveMasterPasswordDigest(String masterPasswdDigest) throws IOException {
        FileOutputStream fout = new FileOutputStream(new File(getSecurityRoot(), MASTER_PASSWD_DIGEST_FILENAME));
        try {
            IOUtils.write(masterPasswdDigest, fout);
        } finally {
            fout.close();
        }
    }

    String computeAndSaveMasterPasswordDigest(char[] passwd) throws IOException {
        GeoServerDigestPasswordEncoder pwEncoder = loadPasswordEncoder(GeoServerDigestPasswordEncoder.class);
        String masterPasswdDigest = pwEncoder.encodePassword(passwd, null);
        saveMasterPasswordDigest(masterPasswdDigest);
        return masterPasswdDigest;
    }

    /**
     * Returns the master password in plain text.
     * <p>
     * This method is package protected and only allowed to be called by classes in this package.
     * </p>
     * <p>
     * The password is returned as a char array rather than string to allow for the scrambling of 
     * the password after use. Since strings are immutable they can not be scrambled. All code that 
     * calls this method should follow the following guidelines:
     * <ol>
     *   <li>Never turn the result into a String object</li>
     *   <li>Always call {@link #disposeMasterPassword(char[])} (ideally in a finally block) 
     *   when done with the password.</li>
     * </ol>
     * </p>
     * <p>
     * For example:
     * <code>
     * <pre>
     *   char[] passwd = manager.getMasterPassword();
     *   try {
     *     //do something
     *   }
     *   finally {
     *     manager.disposeMasterPassword(passwd);
     *   }
     * </pre>
     * </code>
     * </p>
     */
    char[] getMasterPassword() {
        try {
            MasterPasswordProvider mpp = loadMasterPasswordProvider(getMasterPasswordConfig().getProviderName());
            return mpp.getMasterPassword();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Disposes the char array containing the plain text password.
     */
    public void disposePassword(char[] passwd) {
        SecurityUtils.scramble(passwd);
    }

    /**
     * Disposes the byte array containing the plain text password.
     */
    public void disposePassword(byte[] passwd) {
        SecurityUtils.scramble(passwd);
    }

    /**
     * Loads a user {@link MasterPasswordProviderConfig} from a named configuration.
     * <p>
     * This method returns <code>null</code> if the provider config is not found.
     * </p>
     * 
     * @param name The name of the master password provider configuration.
     */
    public MasterPasswordProviderConfig loadMasterPassswordProviderConfig(String name) throws IOException {
        return masterPasswordProviderHelper.loadConfig(name);
    }

    /**
     * Loads a user {@link MasterPasswordProvider} from a named configuration.
     * <p>
     * This method returns <code>null</code> if the provider config is not found.
     * </p>
     * 
     * @param name The name of the master password provider configuration.
     */
    protected MasterPasswordProvider loadMasterPasswordProvider(String name) throws IOException {
        return masterPasswordProviderHelper.load(name);
    }

    /**
     * Saves/persists a master password provider configuration.
     */
    public void saveMasterPasswordProviderConfig(MasterPasswordProviderConfig config)
            throws IOException, SecurityConfigException {
        saveMasterPasswordProviderConfig(config, true);
    }

    /**
     * Saves master password provider configuration, optionally skipping validation.
     * <p>
     * Validation only skipped during migration.
     * </p> 
     */
    void saveMasterPasswordProviderConfig(MasterPasswordProviderConfig config, boolean validate)
            throws IOException, SecurityConfigException {

        SecurityConfigValidator validator = SecurityConfigValidator
                .getConfigurationValiator(MasterPasswordProvider.class, config.getClassName());

        if (config.getId() == null) {
            config.initBeforeSave();
            if (validate) {
                validator.validateAddMasterPasswordProvider(config);
            }
        } else {
            if (validate) {
                validator.validateModifiedMasterPasswordProvider(config,
                        masterPasswordProviderHelper.loadConfig(config.getName()));
            }
        }

        masterPasswordProviderHelper.saveConfig(config);
    }

    /**
     * Removes a master password provider configuration.
     */
    public void removeMasterPasswordProvder(MasterPasswordProviderConfig config)
            throws IOException, SecurityConfigException {

        SecurityConfigValidator validator = SecurityConfigValidator
                .getConfigurationValiator(MasterPasswordProvider.class, config.getClassName());

        validator.validateRemoveMasterPasswordProvider(config);
        masterPasswordProviderHelper.removeConfig(config.getName());
    }

    /**
     * Lists all available master password provider configurations.
     */
    public SortedSet<String> listMasterPasswordProviders() throws IOException {
        return listFiles(getMasterPasswordProviderRoot());
    }

    void fireChanged() {
        for (SecurityManagerListener l : listeners) {
            l.handlePostChanged(this);
        }
    }

    /**
     * @return the master password used for the migration
     * @throws Exception
     */
    char[] extractMasterPasswordForMigration(Properties props) throws Exception {

        Map<String, String> candidates = new HashMap<String, String>();
        String defaultPasswordAsString = new String(MASTER_PASSWD_DEFAULT);

        if (props != null) {
            //load user.properties populate the services 

            UserAttributeEditor configAttribEd = new UserAttributeEditor();

            for (Iterator<Object> iter = props.keySet().iterator(); iter.hasNext();) {
                String username = (String) iter.next();

                configAttribEd.setAsText(props.getProperty(username));
                UserAttribute attr = (UserAttribute) configAttribEd.getValue();
                if (attr == null)
                    continue;

                // The master password policy is not yet available, the default is to
                // have a minimum of 8 chars --> all passwords shorter than 8 chars
                // are no candidates
                if (attr.getPassword() == null || attr.getPassword().length() < 8)
                    continue;

                // The default password is not allowed
                if (defaultPasswordAsString.equals(attr.getPassword()))
                    continue;

                // the  user named "admin" having a non default password is the primary candiate                
                if (GeoServerUser.ADMIN_USERNAME.equals(username)) {
                    candidates.put(GeoServerUser.ADMIN_USERNAME, attr.getPassword());
                    continue;
                }

                // other users having the amin role are secondary candidates
                if (attr.getAuthorities().contains(GeoServerRole.ADMIN_ROLE)) {
                    candidates.put(username, attr.getPassword());
                }
            }
        }

        String username = GeoServerUser.ADMIN_USERNAME;
        String masterPW = candidates.get(username);
        if (masterPW == null && candidates.size() > 0) {
            username = candidates.keySet().iterator().next();
            masterPW = candidates.get(username);
        }

        String message = null;
        File info = new File(getSecurityRoot(), MASTER_PASSWD_INFO_FILENAME);
        char[] masterPasswordArray = null;
        if (masterPW != null) {
            message = "Master password is identical to the password of user: " + username;
            masterPasswordArray = masterPW.toCharArray();
            writeMasterPasswordInfo(info, message, null);
        } else {
            message = "The generated master password is: ";
            masterPasswordArray = getRandomPassworddProvider().getRandomPassword(8);
            writeMasterPasswordInfo(info, message, masterPasswordArray);
        }

        LOGGER.info("Information regarding the master password is in: " + info.getCanonicalPath());
        return masterPasswordArray;
    }

    /**
     * Writes a file containing info about the master password.
     * 
     * @param file
     * @param message
     * @param masterPasswordArray
     * @throws IOException
     */
    void writeMasterPasswordInfo(File file, String message, char[] masterPasswordArray) throws IOException {
        BufferedWriter w = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
        DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        w.write("This file was created at " + dateFormat.format(new Date()));
        w.newLine();
        w.newLine();
        w.write(message);
        if (masterPasswordArray != null)
            w.write(masterPasswordArray);
        w.newLine();
        w.newLine();
        w.write("Test the master password by logging in as user \"root\"");
        w.newLine();
        w.newLine();
        w.write("This file should be removed after reading !!!.");
        w.newLine();
        w.close();
    }

    /**
     * Method to dump master password to a file
     * 
     * The file name is the shared secret between the administrator and GeoServer.
     * 
     * The method inspects the stack trace to check for an authorized calling method.
     * The authenticated principal has to be an administrator
     * 
     * If authorization fails, a warning is written in the log and the return
     * code is <code>false</code>. On success, the return code is <code>true</code>. 
     * 
     * @param file
     * @return
     * @throws IOException
     */
    public boolean dumpMasterPassword(File file) throws IOException {

        if (checkAuthenticationForAdminRole() == false) {
            LOGGER.warning("Unautorized user tries to dump master password");
            return false;
        }

        String[][] allowedMethods = new String[][] {
                { "org.geoserver.security.GeoServerSecurityManagerTest", "testMasterPasswordDump" },
                { "org.geoserver.security.web.passwd.MasterPasswordInfoPage", "dumpMasterPassword" } };

        StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();

        boolean isAllowed = false;
        int countMethodsToCheck = 10;
        // since different sdks have a different stack trace the 
        // first 10 elements are checked
        for (int i = 0; i < countMethodsToCheck; i++) {
            StackTraceElement element = stackTraceElements[i];
            for (String[] methodEntry : allowedMethods) {
                if (methodEntry[0].equals(element.getClassName())
                        && methodEntry[1].equals(element.getMethodName())) {
                    isAllowed = true;
                    break;
                }
            }
        }
        if (!isAllowed) {
            LOGGER.warning("Dump master password is called by an unautorized method");
            for (int i = 0; i < countMethodsToCheck; i++) {
                StackTraceElement element = stackTraceElements[i];
                LOGGER.warning(element.getClassName() + " : " + element.getMethodName());
            }
            return false;
        }

        String message = "The current master password is: ";
        writeMasterPasswordInfo(file, message, getMasterPassword());
        return true;
    }

    /**
     * converts an 2.1.x security configuration to 2.2.x
     * 
     * @return <code>true</code> if migration has taken place  
     * @throws Exception
     */
    boolean migrateFrom21() throws Exception {

        if (getRoleRoot(false) != null) {
            File oldUserFile = new File(getSecurityRoot(), "users.properties.old");
            if (oldUserFile.exists()) {
                LOGGER.warning(oldUserFile.getCanonicalPath() + " could be removed manually");
            }
            return false; // already migrated
        }

        LOGGER.info("Start security migration");

        //create required directories
        getRoleRoot();
        getUserGroupRoot();
        getAuthRoot();
        getPasswordPolicyRoot();
        getFilterRoot();
        getMasterPasswordProviderRoot();

        //master password configuration
        MasterPasswordProviderConfig mpProviderConfig = loadMasterPassswordProviderConfig("default");
        if (mpProviderConfig == null) {
            mpProviderConfig = new URLMasterPasswordProviderConfig();
            mpProviderConfig.setName("default");
            mpProviderConfig.setClassName(URLMasterPasswordProvider.class.getCanonicalName());
            mpProviderConfig.setReadOnly(false);

            ((URLMasterPasswordProviderConfig) mpProviderConfig).setURL(new URL("file:passwd"));
            ((URLMasterPasswordProviderConfig) mpProviderConfig).setEncrypting(true);
            saveMasterPasswordProviderConfig(mpProviderConfig, false);

            //save out the default master password
            MasterPasswordProvider mpProvider = loadMasterPasswordProvider(mpProviderConfig.getName());
            File propFile = new File(getSecurityRoot(), "users.properties");
            Properties userprops = null;
            if (propFile.exists())
                userprops = Util.loadPropertyFile(propFile);
            mpProvider.setMasterPassword(extractMasterPasswordForMigration(userprops));
        }

        MasterPasswordConfig mpConfig = new MasterPasswordConfig();
        mpConfig.setProviderName(mpProviderConfig.getName());
        saveMasterPasswordConfig(mpConfig);

        // check for services.properties, create if necessary
        File serviceFile = new File(getSecurityRoot(), "services.properties");
        if (serviceFile.exists() == false) {
            FileUtils.copyURLToFile(Util.class.getResource("serviceTemplate.properties"), serviceFile);
        }

        long checkInterval = 10000; // 10 secs

        //check for the default user group service, create if necessary
        GeoServerUserGroupService userGroupService = loadUserGroupService(XMLUserGroupService.DEFAULT_NAME);

        KeyStoreProvider keyStoreProvider = getKeyStoreProvider();
        keyStoreProvider.reloadKeyStore();
        keyStoreProvider.setUserGroupKey(XMLUserGroupService.DEFAULT_NAME,
                randomPasswdProvider.getRandomPassword(32));
        keyStoreProvider.storeKeyStore();

        PasswordValidator validator = loadPasswordValidator(PasswordValidator.DEFAULT_NAME);
        if (validator == null) {
            // Policy allows any password except null, this is the default
            // at before migration
            PasswordPolicyConfig pwpconfig = new PasswordPolicyConfig();
            pwpconfig.setName(PasswordValidator.DEFAULT_NAME);
            pwpconfig.setClassName(PasswordValidatorImpl.class.getName());
            pwpconfig.setMinLength(0);
            savePasswordPolicy(pwpconfig);
            validator = loadPasswordValidator(PasswordValidator.DEFAULT_NAME);
        }

        validator = loadPasswordValidator(PasswordValidator.MASTERPASSWORD_NAME);
        if (validator == null) {
            // Policy requires a minimum of 8 chars for the master password            
            PasswordPolicyConfig pwpconfig = new PasswordPolicyConfig();
            pwpconfig.setName(PasswordValidator.MASTERPASSWORD_NAME);
            pwpconfig.setClassName(PasswordValidatorImpl.class.getName());
            pwpconfig.setMinLength(8);
            savePasswordPolicy(pwpconfig);
            validator = loadPasswordValidator(PasswordValidator.MASTERPASSWORD_NAME);
        }

        if (userGroupService == null) {
            XMLUserGroupServiceConfig ugConfig = new XMLUserGroupServiceConfig();
            ugConfig.setName(XMLUserGroupService.DEFAULT_NAME);
            ugConfig.setClassName(XMLUserGroupService.class.getName());
            ugConfig.setCheckInterval(checkInterval);
            ugConfig.setFileName(XMLConstants.FILE_UR);
            ugConfig.setValidating(true);
            // start with weak encryption, plain passwords can be restored
            ugConfig.setPasswordEncoderName(
                    loadPasswordEncoder(GeoServerPBEPasswordEncoder.class, null, false).getName());
            ugConfig.setPasswordPolicyName(PasswordValidator.DEFAULT_NAME);
            saveUserGroupService(ugConfig);
            userGroupService = loadUserGroupService(XMLUserGroupService.DEFAULT_NAME);
        }

        //check for the default role service, create if necessary
        GeoServerRoleService roleService = loadRoleService(XMLRoleService.DEFAULT_NAME);

        if (roleService == null) {
            XMLRoleServiceConfig gaConfig = new XMLRoleServiceConfig();
            gaConfig.setName(XMLRoleService.DEFAULT_NAME);
            gaConfig.setClassName(XMLRoleService.class.getName());
            gaConfig.setCheckInterval(checkInterval);
            gaConfig.setFileName(XMLConstants.FILE_RR);
            gaConfig.setValidating(true);
            gaConfig.setAdminRoleName(XMLRoleService.DEFAULT_LOCAL_ADMIN_ROLE);
            gaConfig.setGroupAdminRoleName(XMLRoleService.DEFAULT_LOCAL_GROUP_ADMIN_ROLE);
            saveRoleService(gaConfig);
            roleService = loadRoleService(XMLRoleService.DEFAULT_NAME);
        }

        String filterName = GeoServerSecurityFilterChain.BASIC_AUTH_FILTER;
        GeoServerSecurityFilter filter = loadFilter(filterName);
        if (filter == null) {
            BasicAuthenticationFilterConfig bfConfig = new BasicAuthenticationFilterConfig();
            bfConfig.setName(filterName);
            bfConfig.setClassName(GeoServerBasicAuthenticationFilter.class.getName());
            bfConfig.setUseRememberMe(true);
            saveFilter(bfConfig);
        }
        /*filterName = GeoServerSecurityFilterChain.BASIC_AUTH_NO_REMEMBER_ME_FILTER;
        filter = loadFilter(filterName);                  
        if (filter==null) {
        BasicAuthenticationFilterConfig bfConfig = new BasicAuthenticationFilterConfig();
        bfConfig.setClassName(GeoServerBasicAuthenticationFilter.class.getName());
        bfConfig.setName(filterName);
        bfConfig.setUseRememberMe(false);
        saveFilter(bfConfig);
        }*/
        filterName = GeoServerSecurityFilterChain.FORM_LOGIN_FILTER;
        filter = loadFilter(filterName);
        if (filter == null) {
            UsernamePasswordAuthenticationFilterConfig upConfig = new UsernamePasswordAuthenticationFilterConfig();
            upConfig.setClassName(GeoServerUserNamePasswordAuthenticationFilter.class.getName());
            upConfig.setName(filterName);
            upConfig.setUsernameParameterName(UsernamePasswordAuthenticationFilterConfig.DEFAULT_USERNAME_PARAM);
            upConfig.setPasswordParameterName(UsernamePasswordAuthenticationFilterConfig.DEFAULT_PASSWORD_PARAM);
            saveFilter(upConfig);
        }
        filterName = GeoServerSecurityFilterChain.SECURITY_CONTEXT_ASC_FILTER;
        filter = loadFilter(filterName);
        if (filter == null) {
            SecurityContextPersistenceFilterConfig pConfig = new SecurityContextPersistenceFilterConfig();
            pConfig.setClassName(GeoServerSecurityContextPersistenceFilter.class.getName());
            pConfig.setName(filterName);
            pConfig.setAllowSessionCreation(true);
            saveFilter(pConfig);
        }
        filterName = GeoServerSecurityFilterChain.SECURITY_CONTEXT_NO_ASC_FILTER;
        filter = loadFilter(filterName);
        if (filter == null) {
            SecurityContextPersistenceFilterConfig pConfig = new SecurityContextPersistenceFilterConfig();
            pConfig.setClassName(GeoServerSecurityContextPersistenceFilter.class.getName());
            pConfig.setName(filterName);
            pConfig.setAllowSessionCreation(false);
            saveFilter(pConfig);
        }
        filterName = GeoServerSecurityFilterChain.ANONYMOUS_FILTER;
        filter = loadFilter(filterName);
        if (filter == null) {
            AnonymousAuthenticationFilterConfig aConfig = new AnonymousAuthenticationFilterConfig();
            aConfig.setClassName(GeoServerAnonymousAuthenticationFilter.class.getName());
            aConfig.setName(filterName);
            saveFilter(aConfig);
        }
        filterName = GeoServerSecurityFilterChain.REMEMBER_ME_FILTER;
        filter = loadFilter(filterName);
        if (filter == null) {
            RememberMeAuthenticationFilterConfig rConfig = new RememberMeAuthenticationFilterConfig();
            rConfig.setClassName(GeoServerRememberMeAuthenticationFilter.class.getName());
            rConfig.setName(filterName);
            saveFilter(rConfig);
        }
        filterName = GeoServerSecurityFilterChain.FILTER_SECURITY_INTERCEPTOR;
        filter = loadFilter(filterName);
        if (filter == null) {
            SecurityInterceptorFilterConfig siConfig = new SecurityInterceptorFilterConfig();
            siConfig.setClassName(GeoServerSecurityInterceptorFilter.class.getName());
            siConfig.setName(filterName);
            siConfig.setAllowIfAllAbstainDecisions(false);
            siConfig.setSecurityMetadataSource("geoserverMetadataSource");
            saveFilter(siConfig);
        }
        filterName = GeoServerSecurityFilterChain.FILTER_SECURITY_REST_INTERCEPTOR;
        filter = loadFilter(filterName);
        if (filter == null) {
            SecurityInterceptorFilterConfig siConfig = new SecurityInterceptorFilterConfig();
            siConfig.setClassName(GeoServerSecurityInterceptorFilter.class.getName());
            siConfig.setName(filterName);
            siConfig.setAllowIfAllAbstainDecisions(false);
            siConfig.setSecurityMetadataSource("restFilterDefinitionMap");
            saveFilter(siConfig);
        }
        filterName = GeoServerSecurityFilterChain.FORM_LOGOUT_FILTER;
        filter = loadFilter(filterName);
        if (filter == null) {
            LogoutFilterConfig loConfig = new LogoutFilterConfig();
            loConfig.setClassName(GeoServerLogoutFilter.class.getName());
            loConfig.setName(filterName);
            saveFilter(loConfig);
        }
        filterName = GeoServerSecurityFilterChain.DYNAMIC_EXCEPTION_TRANSLATION_FILTER;
        filter = loadFilter(filterName);
        if (filter == null) {
            ExceptionTranslationFilterConfig bfConfig = new ExceptionTranslationFilterConfig();
            bfConfig.setClassName(GeoServerExceptionTranslationFilter.class.getName());
            bfConfig.setName(filterName);
            bfConfig.setAuthenticationFilterName(null);
            bfConfig.setAccessDeniedErrorPage("/accessDenied.jsp");
            saveFilter(bfConfig);
        }
        filterName = GeoServerSecurityFilterChain.GUI_EXCEPTION_TRANSLATION_FILTER;
        filter = loadFilter(filterName);
        if (filter == null) {
            ExceptionTranslationFilterConfig bfConfig = new ExceptionTranslationFilterConfig();
            bfConfig.setClassName(GeoServerExceptionTranslationFilter.class.getName());
            bfConfig.setName(filterName);
            bfConfig.setAuthenticationFilterName(GeoServerSecurityFilterChain.FORM_LOGIN_FILTER);
            bfConfig.setAccessDeniedErrorPage("/accessDenied.jsp");
            saveFilter(bfConfig);
        }

        //check for the default auth provider, create if necessary
        GeoServerAuthenticationProvider authProvider = (GeoServerAuthenticationProvider) loadAuthenticationProvider(
                GeoServerAuthenticationProvider.DEFAULT_NAME);
        if (authProvider == null) {
            UsernamePasswordAuthenticationProviderConfig upAuthConfig = new UsernamePasswordAuthenticationProviderConfig();
            upAuthConfig.setName(GeoServerAuthenticationProvider.DEFAULT_NAME);
            upAuthConfig.setClassName(UsernamePasswordAuthenticationProvider.class.getName());
            upAuthConfig.setUserGroupServiceName(userGroupService.getName());

            saveAuthenticationProvider(upAuthConfig);
            authProvider = loadAuthenticationProvider(GeoServerAuthenticationProvider.DEFAULT_NAME);
        }

        //save the top level config
        SecurityManagerConfig config = new SecurityManagerConfig();
        config.setRoleServiceName(roleService.getName());
        config.getAuthProviderNames().add(authProvider.getName());
        config.setEncryptingUrlParams(false);

        // start with weak encryption
        config.setConfigPasswordEncrypterName(
                loadPasswordEncoder(GeoServerPBEPasswordEncoder.class, true, false).getName());

        // setup the default remember me service
        RememberMeServicesConfig rememberMeConfig = new RememberMeServicesConfig();
        rememberMeConfig.setClassName(GeoServerTokenBasedRememberMeServices.class.getName());
        config.setRememberMeService(rememberMeConfig);

        config.setFilterChain(GeoServerSecurityFilterChain.createInitialChain());
        saveSecurityConfig(config);

        //TODO: just call initializeFrom
        userGroupService.setSecurityManager(this);
        roleService.setSecurityManager(this);

        //populate the user group and role service
        GeoServerUserGroupStore userGroupStore = userGroupService.createStore();
        GeoServerRoleStore roleStore = roleService.createStore();

        //migrate from users.properties
        File usersFile = new File(getSecurityRoot(), "users.properties");
        if (usersFile.exists()) {
            //load user.properties populate the services 
            Properties props = Util.loadPropertyFile(usersFile);

            UserAttributeEditor configAttribEd = new UserAttributeEditor();

            for (Iterator<Object> iter = props.keySet().iterator(); iter.hasNext();) {
                // the attribute editors parses the list of strings into password, username and enabled
                // flag
                String username = (String) iter.next();
                configAttribEd.setAsText(props.getProperty(username));

                // if the parsing succeeded turn that into a user object
                UserAttribute attr = (UserAttribute) configAttribEd.getValue();
                if (attr != null) {
                    GeoServerUser user = userGroupStore.createUserObject(username, attr.getPassword(),
                            attr.isEnabled());
                    userGroupStore.addUser(user);

                    for (GrantedAuthority auth : attr.getAuthorities()) {
                        String roleName = GeoServerRole.ADMIN_ROLE.getAuthority().equals(auth.getAuthority())
                                ? XMLRoleService.DEFAULT_LOCAL_ADMIN_ROLE
                                : auth.getAuthority();
                        GeoServerRole role = roleStore.getRoleByName(roleName);
                        if (role == null) {
                            role = roleStore.createRoleObject(roleName);
                            roleStore.addRole(role);
                        }
                        roleStore.associateRoleToUser(role, username);
                    }
                }
            }
        } else {
            // no user.properties, populate with default user and roles
            if (userGroupService.getUserByUsername(GeoServerUser.ADMIN_USERNAME) == null) {
                userGroupStore.addUser(GeoServerUser.createDefaultAdmin());
                GeoServerRole localAdminRole = roleStore.createRoleObject(XMLRoleService.DEFAULT_LOCAL_ADMIN_ROLE);
                roleStore.addRole(localAdminRole);
                roleStore.associateRoleToUser(localAdminRole, GeoServerUser.ADMIN_USERNAME);
            }
        }

        //add the local group administrator role
        if (roleStore.getRoleByName(XMLRoleService.DEFAULT_LOCAL_GROUP_ADMIN_ROLE) == null) {
            roleStore.addRole(roleStore.createRoleObject(XMLRoleService.DEFAULT_LOCAL_GROUP_ADMIN_ROLE));
        }

        // replace all occurrences of ROLE_ADMINISTRATOR  in the property files
        // TODO Justin, a little bit brute force, is this ok ?
        for (String filename : new String[] { "services.properties", "layers.properties", "rest.properties" }) {
            File file = new File(getSecurityRoot(), filename);
            if (file.exists() == false)
                continue;
            List<String> lines = new ArrayList<String>();
            BufferedReader reader = new BufferedReader(new FileReader(file));
            String line;
            while ((line = reader.readLine()) != null)
                lines.add(line.replace(GeoServerRole.ADMIN_ROLE.getAuthority(),
                        XMLRoleService.DEFAULT_LOCAL_ADMIN_ROLE));
            reader.close();
            PrintWriter writer = new PrintWriter(new FileWriter(file));
            for (String s : lines) {
                writer.println(s);
            }
            writer.close();
        }

        // check for roles in services.properties but not in user.properties 
        serviceFile = new File(getSecurityRoot(), "services.properties");
        if (serviceFile.exists()) {
            Properties props = Util.loadPropertyFile(serviceFile);
            for (Entry<Object, Object> entry : props.entrySet()) {
                StringTokenizer tokenizer = new StringTokenizer((String) entry.getValue(), ",");
                while (tokenizer.hasMoreTokens()) {
                    String roleName = tokenizer.nextToken().trim();
                    if (roleName.length() > 0) {
                        if (roleStore.getRoleByName(roleName) == null)
                            roleStore.addRole(roleStore.createRoleObject(roleName));
                    }
                }
            }
        }

        // check for  roles in data.properties but not in user.properties
        File dataFile = new File(getSecurityRoot(), "layers.properties");
        if (dataFile.exists()) {
            Properties props = Util.loadPropertyFile(dataFile);
            for (Entry<Object, Object> entry : props.entrySet()) {
                if ("mode".equals(entry.getKey().toString()))
                    continue; // skip mode directive
                StringTokenizer tokenizer = new StringTokenizer((String) entry.getValue(), ",");
                while (tokenizer.hasMoreTokens()) {
                    String roleName = tokenizer.nextToken().trim();
                    if (roleName.length() > 0 && roleName.equals("*") == false) {
                        if (roleStore.getRoleByName(roleName) == null)
                            roleStore.addRole(roleStore.createRoleObject(roleName));
                    }
                }
            }
        }

        //persist the changes
        roleStore.store();
        userGroupStore.store();

        // first part of migration finished, rename old file
        if (usersFile.exists()) {
            File oldUserFile = new File(usersFile.getCanonicalPath() + ".old");
            usersFile.renameTo(oldUserFile);
            LOGGER.info("Renamed " + usersFile.getCanonicalPath() + " to " + oldUserFile.getCanonicalPath());
        }

        LOGGER.info("End security migration");
        return true;
    }

    /**
     * migration from 2.2.x to 2.3.x
     * return <code>true</code> if migration has taken place
     * 
     * @return
     * @throws Exception
     */
    boolean migrateFrom22(boolean migratedFrom21) throws Exception {

        String filterName = GeoServerSecurityFilterChain.ROLE_FILTER;
        GeoServerSecurityFilter filter = loadFilter(filterName);

        File logoutFilterDir = new File(getFilterRoot(), GeoServerSecurityFilterChain.FORM_LOGOUT_FILTER);
        File oldLogoutFilterConfig = new File(logoutFilterDir, "config.xml.2.2.x");
        File oldSecManagerConfig = new File(getSecurityRoot(), "config.xml.2.2.x");

        if (filter != null) {
            if (oldLogoutFilterConfig.exists())
                LOGGER.warning(oldLogoutFilterConfig.getCanonicalPath() + " could be removed manually");
            if (oldSecManagerConfig.exists())
                LOGGER.warning(oldSecManagerConfig.getCanonicalPath() + " could be removed manually");
            return false; // already migrated
        }

        // add role filter
        RoleFilterConfig rfConfig = new RoleFilterConfig();
        rfConfig.setClassName(GeoServerRoleFilter.class.getName());
        rfConfig.setName(filterName);
        rfConfig.setHttpResponseHeaderAttrForIncludedRoles(GeoServerRoleFilter.DEFAULT_HEADER_ATTRIBUTE);
        rfConfig.setRoleConverterName(GeoServerRoleFilter.DEFAULT_ROLE_CONVERTER);
        saveFilter(rfConfig);

        // add ssl filter
        SSLFilterConfig sslConfig = new SSLFilterConfig();
        sslConfig.setClassName(GeoServerSSLFilter.class.getName());
        sslConfig.setName(GeoServerSecurityFilterChain.SSL_FILTER);
        sslConfig.setSslPort(443);
        saveFilter(sslConfig);

        // set redirect url after successful logout
        if (migratedFrom21 == false)
            FileUtils.copyFile(new File(logoutFilterDir, "config.xml"), oldLogoutFilterConfig);
        LogoutFilterConfig loConfig = (LogoutFilterConfig) loadFilterConfig(
                GeoServerSecurityFilterChain.FORM_LOGOUT_FILTER);
        loConfig.setRedirectURL(GeoServerLogoutFilter.URL_AFTER_LOGOUT);
        saveFilter(loConfig);

        if (migratedFrom21 == false)
            FileUtils.copyFile(new File(getSecurityRoot(), "config.xml"), oldSecManagerConfig);
        SecurityManagerConfig config = loadSecurityConfig();
        for (RequestFilterChain chain : config.getFilterChain().getRequestChains()) {
            if (chain.getFilterNames().contains(GeoServerSecurityFilterChain.SECURITY_CONTEXT_ASC_FILTER)) {
                chain.setAllowSessionCreation(true);
                chain.getFilterNames().remove(GeoServerSecurityFilterChain.SECURITY_CONTEXT_ASC_FILTER);
            }
            if (chain.getFilterNames().contains(GeoServerSecurityFilterChain.SECURITY_CONTEXT_NO_ASC_FILTER)) {
                chain.setAllowSessionCreation(false);
                chain.getFilterNames().remove(GeoServerSecurityFilterChain.SECURITY_CONTEXT_NO_ASC_FILTER);
            }
            // prepare web chain
            if (GeoServerSecurityFilterChain.WEB_CHAIN_NAME.equals(chain.getName())) {
                // replace exception translation filter
                int index = chain.getFilterNames()
                        .indexOf(GeoServerSecurityFilterChain.GUI_EXCEPTION_TRANSLATION_FILTER);
                if (index != -1)
                    chain.getFilterNames().set(index,
                            GeoServerSecurityFilterChain.DYNAMIC_EXCEPTION_TRANSLATION_FILTER);
                // inject form login filter if necessary 
                if (chain.getFilterNames().indexOf(GeoServerSecurityFilterChain.FORM_LOGIN_FILTER) == -1) {
                    index = chain.getFilterNames().indexOf(GeoServerSecurityFilterChain.ANONYMOUS_FILTER);
                    if (index == -1)
                        index = chain.getFilterNames()
                                .indexOf(GeoServerSecurityFilterChain.FILTER_SECURITY_INTERCEPTOR);
                    if (index != -1)
                        chain.getFilterNames().add(index, GeoServerSecurityFilterChain.FORM_LOGIN_FILTER);
                }
            }

            // remove dynamic translation filter
            chain.getFilterNames().remove(GeoServerSecurityFilterChain.DYNAMIC_EXCEPTION_TRANSLATION_FILTER);
            chain.getFilterNames().remove(GeoServerSecurityFilterChain.FILTER_SECURITY_INTERCEPTOR);
            chain.getFilterNames().remove(GeoServerSecurityFilterChain.FILTER_SECURITY_REST_INTERCEPTOR);
        }
        // gui filter not needed any more
        removeFilter(loadFilterConfig(GeoServerSecurityFilterChain.GUI_EXCEPTION_TRANSLATION_FILTER));
        saveSecurityConfig(config);

        // load and store all filter configuration
        // some filter configurations may have their class name as top level xml element in config.xml,
        // the alias should be used instead, this was bug fixed during GSIP 82
        if (migratedFrom21 == false) {
            for (String fName : listFilters()) {
                SecurityFilterConfig fConfig = loadFilterConfig(fName);
                if (fConfig != null)
                    saveFilter(fConfig);
            }
        }

        return true;
    }

    /**
     * converts an 2.3.x security configuration to 2.4.x
     * 
     * @return <code>true</code> if migration has taken place  
     * @throws Exception
     */
    boolean migrateFrom23() throws Exception {
        SecurityManagerConfig config = loadSecurityConfig();
        RequestFilterChain webChain = config.getFilterChain()
                .getRequestChainByName(GeoServerSecurityFilterChain.WEB_CHAIN_NAME);

        boolean migrated = false;
        List<String> patterns = webChain.getPatterns();
        if (patterns.contains("/") == false) {
            patterns.add("/");
            saveSecurityConfig(config);
            migrated |= true;
        }
        return migrated;
    }

    /*
     * looks up security plugins
     */
    public List<GeoServerSecurityProvider> lookupSecurityProviders() {
        List<GeoServerSecurityProvider> list = new ArrayList<GeoServerSecurityProvider>();

        for (GeoServerSecurityProvider provider : GeoServerExtensions.extensions(GeoServerSecurityProvider.class,
                appContext)) {
            if (!provider.isAvailable()) {
                continue;
            }
            list.add(provider);
        }

        return list;
    }

    /*
     * list files in a directory.
     */
    SortedSet<String> listFiles(File dir) {
        SortedSet<String> result = new TreeSet<String>();
        File[] dirs = dir.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                return pathname.isDirectory() && new File(pathname, CONFIG_FILENAME).exists();
            }
        });
        for (File d : dirs) {
            result.add(d.getName());
        }
        return result;
    }

    XStreamPersister globalPersister() throws IOException {
        XStreamPersister xp = persister();
        xp.getXStream().alias("security", SecurityManagerConfig.class);
        xp.getXStream().alias("masterPassword", MasterPasswordConfig.class);
        xp.getXStream().registerLocalConverter(SecurityManagerConfig.class, "filterChain",
                new FilterChainConverter(xp.getXStream().getMapper()));

        // The field anonymousAuth is deprecated
        xp.getXStream().omitField(SecurityManagerConfig.class, "anonymousAuth");

        return xp;
    }

    /*
     * creates the persister for security plugin configuration.
     */
    XStreamPersister persister() throws IOException {
        List<GeoServerSecurityProvider> all = lookupSecurityProviders();

        //create and configure an xstream persister to load the configuration files
        XStreamPersister xp = new XStreamPersisterFactory().createXMLPersister();
        xp.getXStream().alias("security", SecurityManagerConfig.class);

        for (GeoServerSecurityProvider roleService : all) {
            roleService.configure(xp);
        }
        return xp;
    }

    /*
     * loads the global security config
     */
    public SecurityManagerConfig loadSecurityConfig() throws IOException {
        return (SecurityManagerConfig) loadConfigFile(getSecurityRoot(), globalPersister());
    }

    /*
     * loads the master password config
     */
    public MasterPasswordConfig loadMasterPasswordConfig() throws IOException {
        return (MasterPasswordConfig) loadConfigFile(getSecurityRoot(), MASTER_PASSWD_CONFIG_FILENAME,
                globalPersister());
    }

    /**
     * reads a config file from the specified directly using the specified xstream persister
     */
    SecurityConfig loadConfigFile(File directory, String filename, XStreamPersister xp) throws IOException {
        FileInputStream fin = new FileInputStream(new File(directory, filename));
        try {
            return xp.load(fin, SecurityConfig.class);
        } finally {
            fin.close();
        }
    }

    /**
     * reads a file named {@value #CONFIG_FILE_NAME} from the specified directly using the specified
     * xstream persister
     */
    SecurityConfig loadConfigFile(File directory, XStreamPersister xp) throws IOException {
        return loadConfigFile(directory, CONFIG_FILENAME, xp);
    }

    /**
     * saves a config file to the specified directly using the specified xstream persister
     */
    void saveConfigFile(SecurityConfig config, File directory, String filename, XStreamPersister xp)
            throws IOException {
        xStreamPersist(new File(directory, filename), config, xp);
    }

    /**
     * saves a file named {@value #CONFIG_FILE_NAME} from the specified directly using the specified xstream 
     * persister
     */
    void saveConfigFile(SecurityConfig config, File directory, XStreamPersister xp) throws IOException {

        saveConfigFile(config, directory, CONFIG_FILENAME, xp);
    }

    abstract class HelperBase<T, C extends SecurityNamedServiceConfig> {
        /*
         * list of file watchers
         * TODO: we should probably manage these better rather than just throwing them in a 
         * list, repeated loads will cause this list to fill up with threads
         */
        protected List<FileWatcher> fileWatchers = new ArrayList<FileWatcher>();

        public abstract T load(String name) throws IOException;

        /**
         * loads the named entity config from persistence
         */
        public C loadConfig(String name) throws IOException {
            File dir = new File(getRoot(), name);
            if (!dir.exists()) {
                return null;
            }

            XStreamPersister xp = persister();
            return (C) loadConfigFile(dir, xp);
        }

        /**
         * saves the user group service config to persistence
         */
        public void saveConfig(SecurityNamedServiceConfig config) throws IOException {
            File dir = new File(getRoot(), config.getName());
            dir.mkdir();

            boolean isNew = config.getId() == null;
            if (isNew) {
                config.setId(newId());
            }
            try {
                saveConfigFile(config, dir, persister());
            } catch (Exception e) {
                //catch exception, if the config was new, clear out the id since it was not added
                if (isNew) {
                    config.setId(null);
                }
                if (e instanceof IOException) {
                    throw (IOException) e;
                }
                throw new IOException(e);
            }
        }

        String newId() {
            return new UID().toString();
        }

        /**
         * removes the user group service config from persistence
         */
        public void removeConfig(String name) throws IOException {
            FileUtils.deleteDirectory(new File(getRoot(), name));
        }

        public void destroy() {
            for (FileWatcher fw : fileWatchers) {
                fw.setTerminate(true);
            }
        }

        /**
         * config root
         */
        protected abstract File getRoot() throws IOException;
    }

    class UserGroupServiceHelper extends HelperBase<GeoServerUserGroupService, SecurityUserGroupServiceConfig> {
        public GeoServerUserGroupService load(String name) throws IOException {

            SecurityNamedServiceConfig config = loadConfig(name);
            if (config == null) {
                //no such config
                return null;
            }

            //look up the service for this config
            GeoServerUserGroupService service = null;

            for (GeoServerSecurityProvider p : lookupSecurityProviders()) {
                if (p.getUserGroupServiceClass() == null) {
                    continue;
                }
                if (p.getUserGroupServiceClass().getName().equals(config.getClassName())) {
                    service = p.createUserGroupService(config);
                    break;
                }
            }

            if (service == null) {
                throw new IOException("No user group service matching config: " + config);
            }

            service.setSecurityManager(GeoServerSecurityManager.this);
            if (config instanceof SecurityUserGroupServiceConfig) {
                boolean needsLockProtection = GeoServerSecurityProvider
                        .getProvider(GeoServerUserGroupService.class, config.getClassName())
                        .roleServiceNeedsLockProtection();
                if (needsLockProtection)
                    service = new LockingUserGroupService(service);
            }
            service.setName(name);
            service.initializeFromConfig(config);

            if (config instanceof FileBasedSecurityServiceConfig) {
                FileBasedSecurityServiceConfig fileConfig = (FileBasedSecurityServiceConfig) config;
                if (fileConfig.getCheckInterval() > 0) {
                    File file = new File(fileConfig.getFileName());
                    if (file.isAbsolute() == false)
                        file = new File(new File(getUserGroupRoot(), name), file.getPath());
                    if (file.canRead() == false) {
                        throw new IOException("Cannot read file: " + file.getCanonicalPath());
                    }
                    UserGroupFileWatcher watcher = new UserGroupFileWatcher(file.getCanonicalPath(), service,
                            file.lastModified());
                    watcher.setDelay(fileConfig.getCheckInterval());
                    service.registerUserGroupLoadedListener(watcher);
                    watcher.start();

                    //register the watcher so we can kill it later on disposale
                    fileWatchers.add(watcher);
                }
            }

            return service;
        }

        @Override
        protected File getRoot() throws IOException {
            return getUserGroupRoot();
        }
    }

    class RoleServiceHelper extends HelperBase<GeoServerRoleService, SecurityRoleServiceConfig> {

        /**
        * Loads the role service for the named config from persistence.
        */
        public GeoServerRoleService load(String name) throws IOException {

            SecurityNamedServiceConfig config = loadConfig(name);
            if (config == null) {
                //no such config
                return null;
            }

            //look up the service for this config
            GeoServerRoleService service = null;

            for (GeoServerSecurityProvider p : lookupSecurityProviders()) {
                if (p.getRoleServiceClass() == null) {
                    continue;
                }
                if (p.getRoleServiceClass().getName().equals(config.getClassName())) {
                    service = p.createRoleService(config);
                    break;
                }
            }

            if (service == null) {
                throw new IOException("No authority service matching config: " + config);
            }
            service.setSecurityManager(GeoServerSecurityManager.this);

            if (config instanceof SecurityRoleServiceConfig) {
                boolean needsLockProtection = GeoServerSecurityProvider
                        .getProvider(GeoServerRoleService.class, config.getClassName())
                        .roleServiceNeedsLockProtection();
                if (needsLockProtection) {
                    service = new LockingRoleService(service);
                }
            }

            service.setName(name);

            //TODO: do we need this anymore?
            service.initializeFromConfig(config);

            if (config instanceof FileBasedSecurityServiceConfig) {
                FileBasedSecurityServiceConfig fileConfig = (FileBasedSecurityServiceConfig) config;
                if (fileConfig.getCheckInterval() > 0) {
                    File file = new File(fileConfig.getFileName());
                    if (file.isAbsolute() == false)
                        file = new File(new File(getRoleRoot(), name), file.getPath());
                    if (file.canRead() == false) {
                        throw new IOException("Cannot read file: " + file.getCanonicalPath());
                    }
                    RoleFileWatcher watcher = new RoleFileWatcher(file.getCanonicalPath(), service,
                            file.lastModified());
                    watcher.setDelay(fileConfig.getCheckInterval());
                    service.registerRoleLoadedListener(watcher);
                    watcher.start();

                    //register the watcher so we can kill it later
                    fileWatchers.add(watcher);
                }
            }

            return service;
        }

        @Override
        protected File getRoot() throws IOException {
            return getRoleRoot();
        }
    }

    class PasswordValidatorHelper extends HelperBase<PasswordValidator, PasswordPolicyConfig> {

        /**
        * Loads the password policy for the named config from persistence.
        */
        public PasswordValidator load(String name) throws IOException {

            PasswordPolicyConfig config = loadConfig(name);
            if (config == null) {
                //no such config
                return null;
            }

            //look up the validator for this config
            PasswordValidator validator = null;

            for (GeoServerSecurityProvider p : lookupSecurityProviders()) {
                if (p.getPasswordValidatorClass() == null) {
                    continue;
                }
                if (p.getPasswordValidatorClass().getName().equals(config.getClassName())) {
                    validator = p.createPasswordValidator(config, GeoServerSecurityManager.this);
                    break;
                }
            }
            if (validator == null) {
                throw new IOException("No password policy matching config: " + config);
            }

            validator.setConfig(config);
            return validator;
        }

        @Override
        protected File getRoot() throws IOException {
            return getPasswordPolicyRoot();
        }
    }

    class MasterPasswordProviderHelper extends HelperBase<MasterPasswordProvider, MasterPasswordProviderConfig> {

        @Override
        public MasterPasswordProvider load(String name) throws IOException {
            MasterPasswordProviderConfig config = loadConfig(name);
            if (config == null) {
                return null;
            }

            //look up the provider for this config
            MasterPasswordProvider provider = null;

            for (GeoServerSecurityProvider p : lookupSecurityProviders()) {
                if (p.getMasterPasswordProviderClass() == null) {
                    continue;
                }
                if (p.getMasterPasswordProviderClass().getName().equals(config.getClassName())) {
                    provider = p.createMasterPasswordProvider(config);
                    break;
                }
            }
            if (provider == null) {
                throw new IOException("No master password provider matching config: " + config);
            }

            //ensure that the provider is a final class
            if (!Modifier.isFinal(provider.getClass().getModifiers())) {
                throw new RuntimeException("Master password provider class: "
                        + provider.getClass().getCanonicalName() + " is not final");
            }

            provider.setName(config.getName());
            provider.setSecurityManager(GeoServerSecurityManager.this);
            provider.initializeFromConfig(config);
            return provider;
        }

        @Override
        protected File getRoot() throws IOException {
            return getMasterPasswordProviderRoot();
        }

    }

    /**
     *
     * @return the active {@link GeoServerRoleService}
     */
    public GeoServerRoleService getActiveRoleService() {
        try {
            return wrapRoleService(activeRoleService);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * set the active {@link GeoServerRoleService}
     * @param activeRoleService
     */
    public void setActiveRoleService(GeoServerRoleService activeRoleService) {
        this.activeRoleService = activeRoleService;
    }

    /**
     * rewrites configuration files with encrypted fields. 
     * Candidates:
     * {@link StoreInfo} from the {@link Catalog}
     * {@link SecurityNamedServiceConfig} objects from the security directory
     * @param catalog
     */
    public void updateConfigurationFilesWithEncryptedFields() throws IOException {
        // rewrite stores in catalog
        LOGGER.info("Start encrypting configuration passwords using "
                + getSecurityConfig().getConfigPasswordEncrypterName());

        Catalog catalog = getCatalog();
        List<StoreInfo> stores = catalog.getStores(StoreInfo.class);
        for (StoreInfo info : stores) {
            if (!configPasswordEncryptionHelper.getEncryptedFields(info).isEmpty()) {
                catalog.save(info);
            }
        }

        Set<Class<?>> configClasses = new HashSet<Class<?>>();

        // filter the interesting classes ones
        for (GeoServerSecurityProvider prov : lookupSecurityProviders()) {
            configClasses.addAll(prov.getFieldsForEncryption().keySet());
        }

        for (String name : listPasswordValidators()) {
            PasswordPolicyConfig config = passwordValidatorHelper.loadConfig(name);
            for (Class<?> classWithEncryption : configClasses) {
                if (config.getClass().isAssignableFrom(classWithEncryption)) {
                    passwordValidatorHelper.saveConfig(config);
                    break;
                }
            }
        }
        for (String name : listRoleServices()) {
            SecurityNamedServiceConfig config = roleServiceHelper.loadConfig(name);
            for (Class<?> classWithEncryption : configClasses) {
                if (config.getClass().isAssignableFrom(classWithEncryption)) {
                    roleServiceHelper.saveConfig(config);
                    break;
                }
            }
        }
        for (String name : listUserGroupServices()) {
            SecurityNamedServiceConfig config = userGroupServiceHelper.loadConfig(name);
            for (Class<?> classWithEncryption : configClasses) {
                if (config.getClass().isAssignableFrom(classWithEncryption)) {
                    userGroupServiceHelper.saveConfig(config);
                    break;
                }
            }
        }

        for (String name : listAuthenticationProviders()) {
            SecurityNamedServiceConfig config = authProviderHelper.loadConfig(name);
            for (Class<?> classWithEncryption : configClasses) {
                if (config.getClass().isAssignableFrom(classWithEncryption)) {
                    authProviderHelper.saveConfig(config);
                    break;
                }
            }
        }

        for (String name : listFilters()) {
            SecurityNamedServiceConfig config = filterHelper.loadConfig(name);
            for (Class<?> classWithEncryption : configClasses) {
                if (config.getClass().isAssignableFrom(classWithEncryption)) {
                    filterHelper.saveConfig(config);
                    break;
                }
            }
        }
        LOGGER.info("End encrypting configuration passwords");
    }

    class AuthProviderHelper extends HelperBase<GeoServerAuthenticationProvider, SecurityAuthProviderConfig> {

        /**
         * Loads the auth provider for the named config from persistence.
         */
        public GeoServerAuthenticationProvider load(String name) throws IOException {

            SecurityNamedServiceConfig config = loadConfig(name);
            if (config == null) {
                //no such config
                return null;
            }

            //look up the service for this config
            GeoServerAuthenticationProvider authProvider = null;

            for (GeoServerSecurityProvider p : lookupSecurityProviders()) {
                if (p.getAuthenticationProviderClass() == null) {
                    continue;
                }
                if (p.getAuthenticationProviderClass().getName().equals(config.getClassName())) {
                    authProvider = p.createAuthenticationProvider(config);
                    break;
                }
            }

            if (authProvider == null) {
                throw new IOException("No authentication provider matching config: " + config);
            }

            authProvider.setName(name);
            authProvider.setSecurityManager(GeoServerSecurityManager.this);
            authProvider.initializeFromConfig(config);

            return authProvider;
        }

        @Override
        protected File getRoot() throws IOException {
            return getAuthRoot();
        }
    }

    class FilterHelper extends HelperBase<GeoServerSecurityFilter, SecurityFilterConfig> {
        /**
         * Loads the filter for the named config from persistence.
         */
        public GeoServerSecurityFilter load(String name) throws IOException {

            SecurityNamedServiceConfig config = loadConfig(name);
            if (config == null) {
                //no such config
                return null;
            }

            //look up the service for this config
            GeoServerSecurityFilter filter = null;

            for (GeoServerSecurityProvider p : lookupSecurityProviders()) {
                if (p.getFilterClass() == null) {
                    continue;
                }
                if (p.getFilterClass().getName().equals(config.getClassName())) {
                    filter = p.createFilter(config);
                    break;
                }
            }

            if (filter == null) {
                throw new IOException("No authentication provider matching config: " + config);
            }

            filter.setName(name);
            filter.setSecurityManager(GeoServerSecurityManager.this);
            filter.initializeFromConfig(config);

            return filter;
        }

        @Override
        protected File getRoot() throws IOException {
            return getFilterRoot();
        }
    }

    /**
     * custom converter for filter chain
     */
    class FilterChainConverter extends AbstractCollectionConverter {

        public FilterChainConverter(Mapper mapper) {
            super(mapper);
        }

        @Override
        public boolean canConvert(Class type) {
            return GeoServerSecurityFilterChain.class.isAssignableFrom(type);
        }

        @Override
        public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {

            GeoServerSecurityFilterChain filterChain = (GeoServerSecurityFilterChain) source;
            for (RequestFilterChain requestChain : filterChain.getRequestChains()) {
                //<filterChain>
                //  <filters path="..." index="...">
                //    <filter>name1</filter>
                //    <filter>name2</filter>
                //    ...
                writer.startNode("filters");

                StringBuilder sb = new StringBuilder();
                for (String s : requestChain.getPatterns()) {
                    sb.append(s).append(",");
                }
                if (sb.length() > 0) {
                    sb.setLength(sb.length() - 1);
                }

                if (requestChain.getName() != null) {
                    writer.addAttribute("name", requestChain.getName());
                }
                writer.addAttribute("class", requestChain.getClass().getName());
                if (StringUtils.hasLength(requestChain.getRoleFilterName()))
                    writer.addAttribute("roleFilterName", requestChain.getRoleFilterName());

                if (requestChain instanceof VariableFilterChain) {
                    if (StringUtils.hasLength(((VariableFilterChain) requestChain).getInterceptorName()))
                        writer.addAttribute("interceptorName",
                                ((VariableFilterChain) requestChain).getInterceptorName());
                    if (StringUtils.hasLength(((VariableFilterChain) requestChain).getExceptionTranslationName()))
                        writer.addAttribute("exceptionTranslationName",
                                ((VariableFilterChain) requestChain).getExceptionTranslationName());

                }

                writer.addAttribute("path", sb.toString());
                writer.addAttribute("disabled", Boolean.toString(requestChain.isDisabled()));
                writer.addAttribute("allowSessionCreation",
                        Boolean.toString(requestChain.isAllowSessionCreation()));
                writer.addAttribute("ssl", Boolean.toString(requestChain.isRequireSSL()));
                writer.addAttribute("matchHTTPMethod", Boolean.toString(requestChain.isMatchHTTPMethod()));
                if (requestChain.getHttpMethods() != null && requestChain.getHttpMethods().size() > 0) {
                    writer.addAttribute("httpMethods",
                            StringUtils.collectionToCommaDelimitedString(requestChain.getHttpMethods()));
                }

                for (String filterName : requestChain.getFilterNames()) {
                    writer.startNode("filter");
                    writer.setValue(filterName);
                    writer.endNode();
                }

                writer.endNode();
            }
        }

        @Override
        public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {

            GeoServerSecurityFilterChain filterChain = new GeoServerSecurityFilterChain();

            while (reader.hasMoreChildren()) {

                //<filters name="..." path="..."
                reader.moveDown();

                String path = reader.getAttribute("path");
                String name = reader.getAttribute("name");
                String classname = reader.getAttribute("class");
                String roleFilterName = reader.getAttribute("roleFilterName");
                String disabledString = reader.getAttribute("disabled");
                String allowSessionCreationString = reader.getAttribute("allowSessionCreation");
                String interceptorName = reader.getAttribute("interceptorName");
                String exceptionTranslationName = reader.getAttribute("exceptionTranslationName");
                String sslString = reader.getAttribute("ssl");
                String matchHTTPMethodString = reader.getAttribute("matchHTTPMethod");
                String httpMethodsString = reader.getAttribute("httpMethods");

                if (name == null) {
                    //first version of the serialization did not contain name attribute, if not 
                    // available try to look up well known chain, if not found just use the path
                    // as the name
                    RequestFilterChain requestChain = GeoServerSecurityFilterChain.lookupRequestChainByPattern(path,
                            GeoServerSecurityManager.this);
                    if (requestChain != null) {
                        name = requestChain.getName();
                    } else {
                        name = path;
                    }
                }

                // this is nasty but no other chance to migrate from GeoServer 2.2.0
                if (classname == null) {
                    if (GeoServerSecurityFilterChain.WEB_CHAIN_NAME.equals(name)) {
                        classname = HtmlLoginFilterChain.class.getName();
                        allowSessionCreationString = "true";
                        interceptorName = GeoServerSecurityFilterChain.FILTER_SECURITY_INTERCEPTOR;
                    }
                    if (GeoServerSecurityFilterChain.WEB_LOGIN_CHAIN_NAME.equals(name)) {
                        classname = ConstantFilterChain.class.getName();
                        allowSessionCreationString = "true";
                        interceptorName = GeoServerSecurityFilterChain.FILTER_SECURITY_INTERCEPTOR;
                    }
                    if (GeoServerSecurityFilterChain.WEB_LOGOUT_CHAIN_NAME.equals(name)) {
                        classname = LogoutFilterChain.class.getName();
                        allowSessionCreationString = "true";
                        interceptorName = GeoServerSecurityFilterChain.FILTER_SECURITY_INTERCEPTOR;
                    }
                    if (GeoServerSecurityFilterChain.REST_CHAIN_NAME.equals(name)) {
                        classname = ServiceLoginFilterChain.class.getName();
                        allowSessionCreationString = "false";
                        interceptorName = GeoServerSecurityFilterChain.FILTER_SECURITY_REST_INTERCEPTOR;
                    }
                    if (GeoServerSecurityFilterChain.GWC_CHAIN_NAME.equals(name)) {
                        classname = ServiceLoginFilterChain.class.getName();
                        allowSessionCreationString = "false";
                        interceptorName = GeoServerSecurityFilterChain.FILTER_SECURITY_REST_INTERCEPTOR;
                    }
                    if (GeoServerSecurityFilterChain.DEFAULT_CHAIN_NAME.equals(name)) {
                        classname = ServiceLoginFilterChain.class.getName();
                        allowSessionCreationString = "false";
                        interceptorName = GeoServerSecurityFilterChain.FILTER_SECURITY_INTERCEPTOR;
                    }
                }

                //<filter
                ArrayList<String> filterNames = new ArrayList<String>();
                while (reader.hasMoreChildren()) {
                    reader.moveDown();
                    filterNames.add(reader.getValue());
                    reader.moveUp();
                }

                RequestFilterChain requestChain = null;
                try {
                    Class<?> chainClass = Class.forName(classname);
                    Constructor<?> cons = chainClass.getConstructor(new Class[] { String[].class });
                    String[] args = path.split(",");
                    requestChain = (RequestFilterChain) cons.newInstance(new Object[] { args });
                } catch (Exception ex) {
                    throw new RuntimeException(ex);
                }
                requestChain.setName(name);
                if (StringUtils.hasLength(disabledString)) {
                    requestChain.setDisabled(Boolean.parseBoolean(disabledString));
                }
                if (StringUtils.hasLength(allowSessionCreationString)) {
                    requestChain.setAllowSessionCreation(Boolean.parseBoolean(allowSessionCreationString));
                }
                if (StringUtils.hasLength(sslString)) {
                    requestChain.setRequireSSL(Boolean.parseBoolean(sslString));
                }
                if (StringUtils.hasLength(matchHTTPMethodString)) {
                    requestChain.setMatchHTTPMethod(Boolean.parseBoolean(matchHTTPMethodString));
                }
                if (StringUtils.hasLength(httpMethodsString)) {
                    for (String method : httpMethodsString.split(",")) {
                        requestChain.getHttpMethods().add(HTTPMethod.fromString(method));
                    }
                }

                requestChain.setRoleFilterName(roleFilterName);

                if (requestChain instanceof VariableFilterChain) {
                    ((VariableFilterChain) requestChain).setInterceptorName(interceptorName);
                    if (StringUtils.hasLength(exceptionTranslationName))
                        ((VariableFilterChain) requestChain).setExceptionTranslationName(exceptionTranslationName);
                    else
                        ((VariableFilterChain) requestChain).setExceptionTranslationName(
                                GeoServerSecurityFilterChain.DYNAMIC_EXCEPTION_TRANSLATION_FILTER);
                }
                requestChain.setFilterNames(filterNames);
                filterChain.getRequestChains().add(requestChain);

                reader.moveUp();
            }

            // no good idea, how to split them from the gui ???
            //filterChain.simplify();
            return filterChain;
        }

    }

    /**
     * Calculates the union of roles from all role services and
     * adds {@link GeoServerRole#ANONYMOUS_ROLE} and {@link GeoServerRole#AUTHENTICATED_ROLE}
     * 
     * @throws IOException
     */
    public SortedSet<GeoServerRole> getRolesForAccessControl() throws IOException {

        SortedSet<GeoServerRole> allRoles = new TreeSet<GeoServerRole>();
        for (String serviceName : listRoleServices()) {
            // catch the IOException for each role service.
            // As an example, it does not make sense to throw an IOException if 
            // a jdbc connection cannot be established.
            try {
                allRoles.addAll(loadRoleService(serviceName).getRoles());
            } catch (IOException ex) {
                LOGGER.log(Level.WARNING, ex.getMessage(), ex);
            }
        }
        allRoles.add(GeoServerRole.AUTHENTICATED_ROLE);
        allRoles.add(GeoServerRole.ANONYMOUS_ROLE);
        return allRoles;
    }
}