Package org.geoserver.security

Source Code of org.geoserver.security.GeoServerSecurityManager$HelperBase

/* (c) 2014 Open Source Geospatial Foundation - all rights reserved
* (c) 2001 - 2014 OpenPlans
* 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.InputStream;
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.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.Properties;
import java.util.Set;
import java.util.SortedSet;
import java.util.StringTokenizer;
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.platform.ContextLoadedEvent;
import org.geoserver.platform.GeoServerExtensions;
import org.geoserver.platform.GeoServerResourceLoader;
import org.geoserver.platform.resource.Files;
import org.geoserver.platform.resource.Paths;
import org.geoserver.platform.resource.Resource;
import org.geoserver.platform.resource.Resource.Type;
import org.geoserver.platform.resource.ResourceStore;
import org.geoserver.platform.resource.Resources;
import org.geoserver.security.GeoServerSecurityManager.FilterHelper;
import org.geoserver.security.auth.AuthenticationCache;
import org.geoserver.security.auth.GeoServerRootAuthenticationProvider;
import org.geoserver.security.auth.GuavaAuthenticationCacheImpl;
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.J2eeAuthenticationBaseFilterConfig;
import org.geoserver.security.config.J2eeAuthenticationBaseFilterConfig.J2EERoleSource;
import org.geoserver.security.config.LogoutFilterConfig;
import org.geoserver.security.config.PasswordPolicyConfig;
import org.geoserver.security.config.PreAuthenticatedUserNameFilterConfig;
import org.geoserver.security.config.PreAuthenticatedUserNameFilterConfig.PreAuthenticatedUserNameRoleSource;
import org.geoserver.security.config.RememberMeAuthenticationFilterConfig;
import org.geoserver.security.config.RoleFilterConfig;
import org.geoserver.security.config.RoleSource;
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.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.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.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.RememberMeAuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
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.util.StringUtils;

import com.thoughtworks.xstream.converters.Converter;
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, ResourceStore {

    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 
         */
        Resource masterpw = security().get( MASTER_PASSWD_CONFIG_FILENAME);       
        if (masterpw.getType() == Type.RESOURCE) {
            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();
                removeErroneousAccessDeniedPage();
                migrateFrom22(migratedFrom21);
                migrateFrom23();
                migrateFrom24();               
            } 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);
            }
        }
    }

    void migrateFrom24() throws SecurityConfigException, IOException {
        // allows migration of RoleSource from PreAuthenticatedUserNameFilterConfig
        MigrationHelper mh = new MigrationHelper() {
            @Override
            public void migrationPersister(XStreamPersister xp) {
                xp.getXStream().registerConverter(new Converter() {
   
                    @Override
                    public boolean canConvert(Class cls) {
                        return cls.isAssignableFrom(RoleSource.class);
                    }
   
                    @Override
                    public void marshal(Object rs, HierarchicalStreamWriter writer,
                            MarshallingContext ctx) {
                        if (rs != null) {
                            writer.setValue(rs.toString());
                        }
   
                    }
   
                    @Override
                    public Object unmarshal(HierarchicalStreamReader reader,
                            UnmarshallingContext ctx) {
                        if (reader.getValue() != null) {
                            return J2EERoleSource.valueOf(reader.getValue());
                        }
                        return null;
                    }
   
                });
            }
   
        };
        for (String fName : listFilters()) {
            SecurityFilterConfig fConfig = loadFilterConfig(fName, mh);
            if (fConfig != null) {
                if (fConfig instanceof J2eeAuthenticationBaseFilterConfig) {
                    J2eeAuthenticationBaseFilterConfig j2eeConfig = (J2eeAuthenticationBaseFilterConfig) fConfig;
                    // add default J2EE RoleSource that was the only one possible
                    // before 2.5
                    if (j2eeConfig.getRoleSource() == null) {
                        j2eeConfig.setRoleSource(J2EERoleSource.J2EE);
                    }
                } else if (fConfig instanceof PreAuthenticatedUserNameFilterConfig) {
                    PreAuthenticatedUserNameFilterConfig userNameConfig = (PreAuthenticatedUserNameFilterConfig) fConfig;
                    RoleSource rs = userNameConfig.getRoleSource();
                    if (rs != null) {
                        // use the right RoleSource enum
                        userNameConfig
                                .setRoleSource(PreAuthenticatedUserNameRoleSource
                                        .valueOf(rs.toString()));
                    }
                }
                saveFilter(fConfig, mh);
            }
        }
    }

    @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 GuavaAuthenticationCacheImpl(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;
    }
   
    @Override
    public Resource get(String path) {
        return dataDir.get(path);
    }

    @Override
    public boolean remove(String path) {
        return dataDir.remove(path);
    }

    @Override
    public boolean move(String path, String target) {
        return dataDir.move(path, target);
    }

    /**
     * Security configuration root directory.
     */
    public Resource security() {
        return get("security");
    }
   
    /**
     * Security configuration root directory.
     *
     * @deprecated Use {@link #secuirtyRoot()}
     */
    public File getSecurityRoot() throws IOException {
        Resource directory = get("security");
        return directory.dir();
    }

    /**
     * Role configuration root directory.
     */
    public Resource role() {
        return get("security/role");
    }

    /**
     * Role configuration root directory.
     *
     * @deprecated Use {@link #role()}
     */
    public File getRoleRoot() throws IOException {
        Resource directory = get("security/role");
        return directory.dir();
    }

    /**
     * Role configuration root directory.
     *
     * @deprecated Use {@link #role()}
     */
    public File getRoleRoot(boolean create) throws IOException {
        Resource directory = get("security/role");
        if (create) {
            return directory.dir();
        } else {
            return Resources.directory(directory);
        }
    }

    /**
     * Password policy configuration root directory
     */
    public Resource passwordPolicy(){
        return get("security/pwpolicy");
    }
    /**
     * Password policy configuration root directory
     * @deprecated Use {@link #passwordPolicy()}
     */
    public File getPasswordPolicyRoot() throws IOException {
        return get("security/pwpolicy").dir();
    }

    /**
     * User/group configuration root directory.
     */
    public Resource userGroup() throws IOException {
        return get("security/usergroup");
    }

    /**
     * User/group configuration root directory.
     * @deprecated Use {@link #userGroup()}
     */
    public File getUserGroupRoot() throws IOException {
        Resource directory = get("security/usergroup");
        return directory.dir();
    }

    /**
     * Authentication configuration root directory.
     */
    public Resource auth() throws IOException {
        return get("security/auth");
    }
   
    /**
     * Authentication configuration root directory.
     * @deprecated use {@link #auth()}
     */
    public File getAuthRoot() throws IOException {
        Resource directory = get("security/auth");
        return directory.dir();
    }

    /**
     * Authentication filter root directory.
     */
    public Resource filterRoot() throws IOException {
        return get("security/filter");
    }
   
    /**
     * Authentication filter root directory.
     * @deprecated Use {@link #auth()}
     */
    public File getFilterRoot() throws IOException {
        Resource directory = get("security/filter");
        return directory.dir();
    }

    /**
     * Master password provider root
     */
    public Resource masterPasswordProvider() throws IOException {
        return get("security/masterpw");
    }
    /**
     * Master password provider root
     * @deprecated Use {@link #masterPasswordProvider()}
     */
    public File getMasterPasswordProviderRoot() throws IOException {
        Resource resource = get("security/masterpw");
        return resource.dir();
    }

    /**
     * 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 cryptography is available");
        } catch (InvalidKeyException e) {
            strongEncryptionAvaialble = false;
            LOGGER.warning("Strong cryptography is NOT available"+
            "\nDownload and installation the of unlimted length policy files is recommended"
            );
        } catch (Exception ex) {
            LOGGER.log(Level.WARNING, "Strong cryptography 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;
    }

    /**
     * Returns true if the default password for the {@Link GeoServerUser#ADMIN_USERNAME} has not
     * been changed.
     */
    public boolean checkForDefaultAdminPassword() {
        Authentication token = new UsernamePasswordAuthenticationToken(GeoServerUser.ADMIN_USERNAME,
            GeoServerUser.DEFAULT_ADMIN_PASSWD);

        try {
            token = authenticate(token);
        }
        catch(Exception e) {
            //ok
        }

        return token.isAuthenticated();
    }

    /**
     * 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.
     * @param migrationHelper Optional helper used for migration purposes
     */
    public SecurityFilterConfig loadFilterConfig(String name, MigrationHelper migrationHelper) throws IOException {
        return filterHelper.loadConfig(name, migrationHelper);
    }
   
    /**
     * 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 {
        saveFilter(config, null);
    }
   
    public void saveFilter(SecurityNamedServiceConfig config, MigrationHelper migrationHelper)
            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(), migrationHelper));
            // 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"}
        };
               
        String result = checkStackTrace(10, allowedMethods);
       
        if (result!=null) {
            LOGGER.warning("Dump master password is called by an unautorized method\n"+result);
            return false;
        }
       
        String message = "The current master password is: ";
        writeMasterPasswordInfo(file, message, getMasterPassword());
        return true;
    }
   
    /**
     * Get master password for REST configuraton
     *
     * The method inspects the stack trace to check for an authorized calling method.
     * The authenticated principal has to be an administrator
     *
     * If authorization fails, an IOException is thrown
     *
     * @return
     * @throws IOException
     */
    public char[] getMasterPasswordForREST() throws IOException {
       
       
        if (checkAuthenticationForAdminRole()==false) {
            throw new IOException("Unauthorized user tries to read master password");
        }
       
       
        String[][] allowedMethods = new String [][]{
                {"org.geoserver.security.rest.MasterPasswordResource","getMap"}               
        };
       
        String result = checkStackTrace(10, allowedMethods);
        if (result!=null) {
            throw new IOException ("Unauthorized method wants to read master password\n"+result);
        }
       
        return getMasterPassword();
    }


    /**
     * Checks if the stack trace contains allowed methods.
     * It it contains allowed methods, return <code>null</code>,
     * if not return a String listing the methods.
     *
     * @param countMethodsToCheck
     * @param allowedMethods
     * @return
     */
    String checkStackTrace(int countMethodsToCheck,String[][] allowedMethods) {
       
        StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
       
        boolean isAllowed=false;
       
        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) {
            return null;
        }
        else {
            StringBuffer buff = new StringBuffer();
            for (int i = 0; i< countMethodsToCheck;i++) {
                StackTraceElement element = stackTraceElements[i];
                buff.append(element.getClassName()).append(" : ").
                    append(element.getMethodName()).append("\n");
            }
            return buff.toString();
        }
       
    }
   
    /**
     * 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;
    }
   
    /**
     * Remove erroneous access denied page (HTTP) 403 (see GEOS-4943)
     * The page /accessDeniedPage does not exist and would not work
     * if it exists.
     *
     * @throws Exception
     */
    void removeErroneousAccessDeniedPage() throws Exception {
        
        ExceptionTranslationFilterConfig config =
                (ExceptionTranslationFilterConfig) loadFilterConfig(GeoServerSecurityFilterChain.DYNAMIC_EXCEPTION_TRANSLATION_FILTER);
        if (config!=null && "/accessDenied.jsp".equals(config.getAccessDeniedErrorPage())) {
            config.setAccessDeniedErrorPage(null);
            saveFilter(config);
        }
       
         config =
                (ExceptionTranslationFilterConfig) loadFilterConfig(GeoServerSecurityFilterChain.GUI_EXCEPTION_TRANSLATION_FILTER);
        if (config!=null && "/accessDenied.jsp".equals(config.getAccessDeniedErrorPage())) {
            config.setAccessDeniedErrorPage(null);
            saveFilter(config);
        }
                   
    }


    /*
     * 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 {
        Resource resource = security().get(MASTER_PASSWD_CONFIG_FILENAME);
        return loadConfig( MasterPasswordConfig.class, resource, globalPersister() );
    }
   
    /**
     * reads a config file from the specified directly using the specified xstream persister
     */
    <T extends SecurityConfig> T loadConfig( Class<T> config, Resource resource, XStreamPersister xp ) throws IOException {
        InputStream in = resource.in();
        try {
            Object loaded = xp.load(in, SecurityConfig.class);
            return config.cast( loaded );
        }
        finally {
            in.close();
        }       
    }
    /**
     * 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, MigrationHelper migrationHelper) throws IOException {
            File dir = new File(getRoot(), name);
            if (!dir.exists()) {
                return null;
            }

            XStreamPersister xp = persister();
            if(migrationHelper != null) {
                migrationHelper.migrationPersister(xp);
            }
            return (C) loadConfigFile(dir, xp);
        }
       
        /**
         * loads the named entity config from persistence
         */
        public C loadConfig(String name) throws IOException {
            return loadConfig(name, null);
        }


        /**
         * 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) {
                    Resource resource = getConfigFile( fileConfig.getFileName());
                    if( resource == null ){
                        String path = Paths.path("security/usergroup", name, fileConfig.getFileName());
                        resource = get(path);                       
                    }
                   
                    UserGroupFileWatcher watcher = new UserGroupFileWatcher(resource,service);
                    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) {
                    Resource resource = getConfigFile( fileConfig.getFileName());
                    if( resource == null ){
                        String path = Paths.path("security/role", name, fileConfig.getFileName());
                        resource = get(path);
                    }

                    RoleFileWatcher watcher = new RoleFileWatcher(resource, service, resource.lastmodified());
                    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();
        }
    }

    /**
     * Alternative to {@link GeoServerResourceLoader#find(String)} that supports absolute paths
     * for use in test cases.
     * <p>
     * If an absolute path is used the Resource implementation is provided by {@link Files#asResource(File)}.
     *
     * @param fileLocation
     * @return resource
     */
    Resource getConfigFile(String configFileLocation) throws IOException {
        File file = new File(configFileLocation);
        if (file.isAbsolute()) {
            if (file.canRead()) {
                return Files.asResource(file); // used by test cases
            } else {
                throw new IOException("Cannot read file: " + file.getCanonicalPath());
            }
        }
        return null;
    }

    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");
    }
   
    /**
     * Interface that can be used to assist migration phases, adding XStream behaviours to be used
     * only during migration of configurations from previous versions.
     * A specific implementation can be passed to {@link FilterHelper.loadConfig} and/or
     * {@link FilterHelper.saveConfig} to change XStream mappings and conversions to allow loading
     * of old (incompatible) configuration files that need to be updated to a new format.
     * The implementation should implement the migrationPersister method to add
     * aliases, converters or other XStream behaviours needed only when migrating old
     * configurations.
     * 
     * @author Mauro Bartolomeoli (mauro.bartolomeoli@geo-solutions.it)
     *
     */
    interface MigrationHelper {
        /**
         * Implement here XStream mappings and conversion behaviours needed to read incompatible
         * configurations during migration.
         *
         * @param xp
         */
        public void migrationPersister(XStreamPersister xp);
    }
   
    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;
    }
}
TOP

Related Classes of org.geoserver.security.GeoServerSecurityManager$HelperBase

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.