/* (c) 2014 Open Source Geospatial Foundation - all rights reserved
* (c) 2001 - 2013 OpenPlans
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.security;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.geoserver.config.util.XStreamPersister;
import org.geoserver.platform.GeoServerExtensions;
import org.geoserver.security.config.PasswordPolicyConfig;
import org.geoserver.security.config.SecurityNamedServiceConfig;
import org.geoserver.security.filter.GeoServerSecurityFilter;
import org.geoserver.security.password.MasterPasswordProviderConfig;
import org.geoserver.security.password.PasswordValidator;
import org.geoserver.security.validation.SecurityConfigValidator;
import com.thoughtworks.xstream.converters.SingleValueConverter;
/**
* Extension point for backend authentication and authorization services.
* <p>
* Subclasses of this class should be registered in the spring context.
* </p>
*
* @author Justin Deoliveira, OpenGeo
*/
public abstract class GeoServerSecurityProvider {
/**
* Find the provider for a service type
* and a concrete class name.
* May return <code>null</code>
*
* @param serviceClass
* @param className
*
*/
static public GeoServerSecurityProvider getProvider (Class<?> serviceClass, String className) {
for (GeoServerSecurityProvider prov :
GeoServerExtensions.extensions(GeoServerSecurityProvider.class)) {
if (GeoServerAuthenticationProvider.class==serviceClass &&
prov.getAuthenticationProviderClass()!=null) {
if (prov.getAuthenticationProviderClass().getName().equals(className))
return prov;
}
if (GeoServerUserGroupService.class==serviceClass &&
prov.getUserGroupServiceClass()!=null) {
if (prov.getUserGroupServiceClass().getName().equals(className))
return prov;
}
if (GeoServerRoleService.class==serviceClass &&
prov.getRoleServiceClass()!=null) {
if (prov.getRoleServiceClass().getName().equals(className))
return prov;
}
if (PasswordValidator.class==serviceClass &&
prov.getPasswordValidatorClass()!=null ) {
if (prov.getPasswordValidatorClass().getName().equals(className))
return prov;
}
if (GeoServerSecurityFilter.class==serviceClass &&
prov.getFilterClass()!=null) {
if (prov.getFilterClass().getName().equals(className))
return prov;
}
if (MasterPasswordProvider.class == serviceClass &&
prov.getMasterPasswordProviderClass() != null) {
if (prov.getMasterPasswordProviderClass().getName().equals(className)) {
return prov;
}
}
}
return null;
}
/**
* An implementation of {@link SingleValueConverter} for encryption and
* decryption of configuration passwords.
*
* Register the fields in {@link #configure(XStreamPersister)}
*
* <code>
* xp.getXStream().registerLocalConverter(class, fieldName, encrypter);
* </code>
* TODO: remove the GeoServerExtensions looksups in this class
*/
public SingleValueConverter encrypter = new SingleValueConverter() {
@Override
public boolean canConvert(Class type) {
return type.equals(String.class);
}
@Override
public String toString(Object obj) {
String source = obj == null ? "" : (String) obj;
GeoServerSecurityManager manager =
GeoServerExtensions.bean(GeoServerSecurityManager.class);
return manager.getConfigPasswordEncryptionHelper().encode(source);
};
@Override
public Object fromString(String str) {
GeoServerSecurityManager manager =
GeoServerExtensions.bean(GeoServerSecurityManager.class);
return manager.getConfigPasswordEncryptionHelper().decode(str);
}
};
/**
* Flag determining if this provider is available.
* <p>
* This default implementation returns <code>true</code>, subclasses should override in cases
* where a meaningful check can be made... for instance checking for a jdbc driver, etc...
* </p>
*/
public boolean isAvailable() {
return true;
}
/**
* Configures the xstream instance used to serialize/deserialize provider configuration.
*/
public void configure(XStreamPersister xp) {
// register converter for fields to be encrypted
for (Entry<Class<?>, Set<String>> entry: getFieldsForEncryption().entrySet()) {
for (String fieldName: entry.getValue()) {
xp.getXStream().registerLocalConverter(entry.getKey(), fieldName, encrypter);
}
}
}
/**
* Returns the concrete class of authentication provider created by
* {@link #createAuthenticationProvider(SecurityNamedServiceConfig)}.
* <p>
* If the extension does not provide an authentication provider this method should simply return
* <code>null</code>.
* </p>
* TODO: change this interface to GeoServerAuthenticationProvider
*/
public Class<? extends GeoServerAuthenticationProvider> getAuthenticationProviderClass() {
return null;
}
/**
* Creates an authentication provider.
* <p>
* If the extension does not provide an authentication provider this method should simply return
* <code>null</code>.
* </p>
*/
public GeoServerAuthenticationProvider createAuthenticationProvider(SecurityNamedServiceConfig config) {
return null;
}
/**
* Returns the concrete class of security filter created by
* {@link #createFilter(SecurityNamedServiceConfig)}.
* <p>
* If the extension does not provide an filter this method should simply return <code>null</code>.
* </p>
*/
public Class<? extends GeoServerSecurityFilter> getFilterClass() {
return null;
}
/**
* Creates a security filter.
* <p>
* If the extension does not provide an filter this method should simply return <code>null</code>.
* </p>
*/
public GeoServerSecurityFilter createFilter(SecurityNamedServiceConfig config) {
return null;
}
/**
* Returns the specific class of the user group service created by
* {@link #createUserGroupService(SecurityNamedServiceConfig)}.
* <p>
* If the extension does not provide a user group service this method should simply return
* <code>null</code>.
* </p>
*/
public Class<? extends GeoServerUserGroupService> getUserGroupServiceClass() {
return null;
}
/**
* Creates a new user group service.
* <p>
* If the extension does not provide a user group service this method should simply return
* <code>null</code>.
* </p>
*/
public GeoServerUserGroupService createUserGroupService(SecurityNamedServiceConfig config)
throws IOException {
return null;
}
/**
* Returns the specific class of the role service created by
* {@link #createRoleService(SecurityNamedServiceConfig)}
* <p>
* If the extension does not provide a role service this method should simply return
* <code>null</code>.
* </p>
*/
public Class<? extends GeoServerRoleService> getRoleServiceClass() {
return null;
}
/**
* Creates a new role group service.
* <p>
* If the extension does not provide a role service this method should simply return
* <code>null</code>.
* </p>
*/
public GeoServerRoleService createRoleService(SecurityNamedServiceConfig config)
throws IOException {
return null;
}
/**
* Returns the specific class of the master password provider created by
* {@link #createMasterPasswordProvider(MasterPasswordProviderConfig)}
* <p>
* If the extension does not provide a master password provider e this method should simply
* return <code>null</code>.
* </p>
*/
public Class<? extends MasterPasswordProvider> getMasterPasswordProviderClass() {
return null;
}
/**
* Creates a new role group service.
* <p>
* If the extension does not provide a master password provider this method should simply return
* <code>null</code>.
* </p>
*/
public MasterPasswordProvider createMasterPasswordProvider(MasterPasswordProviderConfig config)
throws IOException {
return null;
}
/**
* Returns the specific class of the password validator created by
* {@link #createPasswordValidator(PasswordPolicyConfig)}.
* <p>
* If the extension does not provide a validator this method should simply return
* <code>null</code>.
* </p>
*/
public Class<? extends PasswordValidator> getPasswordValidatorClass() {
return null;
}
/**
* Create the standard password validator or
* return <code>null</code>
*
* @param config
*
*/
public PasswordValidator createPasswordValidator(PasswordPolicyConfig config,
GeoServerSecurityManager securityManager) {
return null;
}
/**
* Returns a map containing the field names
* which should be encrypted. (backend store
* passwords as an example)
*
*
*/
public Map<Class<?>, Set<String>> getFieldsForEncryption() {
return Collections.emptyMap();
}
/**
* Return true if the {@link GeoServerRoleService} implementation
* is not thread safe.
*
*/
public boolean roleServiceNeedsLockProtection() {
return false;
}
/**
* Return true if the {@link GeoServerUserGroupService} implementation
* is not thread safe.
*
*/
public boolean userGroupServiceNeedsLockProtection() {
return false;
}
/**
* Return a configuration validator, subclass of {@link SecurityConfigValidator}
*
*/
public SecurityConfigValidator createConfigurationValidator(GeoServerSecurityManager securityManager) {
return new SecurityConfigValidator(securityManager);
}
/**
* Configures the security filter chain.
*/
public void configureFilterChain(GeoServerSecurityFilterChain filterChain) {
}
/**
* Startup hook - this will be executed after loading the security
* configuration, allowing plugins to apply custom modifications to the
* security settings.
*/
public void init(GeoServerSecurityManager manager) {
}
/**
* Shutdown hook - this will be executed before unloading the security configuration.
*/
public void destroy(GeoServerSecurityManager manager) {
}
}