SEC-1124: Refactored LDAP code into separate module
This commit is contained in:
@@ -1,205 +0,0 @@
|
||||
package org.springframework.security.config;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.ldap.core.ContextSource;
|
||||
import org.springframework.util.Assert;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.directory.server.configuration.MutableServerStartupConfiguration;
|
||||
import org.apache.directory.server.jndi.ServerContextFactory;
|
||||
import org.apache.directory.server.protocol.shared.store.LdifFileLoader;
|
||||
import org.apache.directory.server.core.configuration.ShutdownConfiguration;
|
||||
import org.apache.directory.server.core.DirectoryService;
|
||||
|
||||
import javax.naming.Context;
|
||||
import javax.naming.InitialContext;
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.directory.DirContext;
|
||||
import javax.naming.directory.InitialDirContext;
|
||||
import java.util.Properties;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Provides lifecycle services for the embedded apacheDS server defined by the supplied configuration.
|
||||
* Used by {@link LdapServerBeanDefinitionParser}. An instance will be stored in the application context for
|
||||
* each embedded server instance. It will start the server when the context is initialized and shut it down when
|
||||
* it is closed. It is intended for temporary embedded use and will not retain changes across start/stop boundaries. The
|
||||
* working directory is deleted on shutdown.
|
||||
*
|
||||
* <p>
|
||||
* If used repeatedly in a single JVM process with the same configuration (for example, when
|
||||
* repeatedly loading an application context during testing), it's important that the
|
||||
* application context is closed to allow the bean to be disposed of and the server shutdown
|
||||
* prior to attempting to start it again.
|
||||
* </p>
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
class ApacheDSContainer implements InitializingBean, DisposableBean, Lifecycle, ApplicationContextAware {
|
||||
private Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private MutableServerStartupConfiguration configuration;
|
||||
private ApplicationContext ctxt;
|
||||
private File workingDir;
|
||||
|
||||
private ContextSource contextSource;
|
||||
private boolean running;
|
||||
private String ldifResources;
|
||||
|
||||
public ApacheDSContainer(MutableServerStartupConfiguration config, ContextSource contextSource, String ldifs) {
|
||||
this.configuration = config;
|
||||
this.contextSource = contextSource;
|
||||
this.ldifResources = ldifs;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (workingDir == null) {
|
||||
String apacheWorkDir = System.getProperty("apacheDSWorkDir");
|
||||
|
||||
if (apacheWorkDir == null) {
|
||||
apacheWorkDir = System.getProperty("java.io.tmpdir") + File.separator + "apacheds-spring-security";
|
||||
}
|
||||
|
||||
setWorkingDirectory(new File(apacheWorkDir));
|
||||
}
|
||||
start();
|
||||
}
|
||||
|
||||
public void destroy() throws Exception {
|
||||
stop();
|
||||
}
|
||||
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
ctxt = applicationContext;
|
||||
}
|
||||
|
||||
private boolean deleteDir(File dir) {
|
||||
if (dir.isDirectory()) {
|
||||
String[] children = dir.list();
|
||||
for (int i=0; i < children.length; i++) {
|
||||
boolean success = deleteDir(new File(dir, children[i]));
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dir.delete();
|
||||
}
|
||||
|
||||
public void setWorkingDirectory(File workingDir) {
|
||||
Assert.notNull(workingDir);
|
||||
|
||||
logger.info("Setting working directory for LDAP_PROVIDER: " + workingDir.getAbsolutePath());
|
||||
|
||||
if (workingDir.exists()) {
|
||||
throw new IllegalArgumentException("The specified working directory '" + workingDir.getAbsolutePath() +
|
||||
"' already exists. Another directory service instance may be using it or it may be from a " +
|
||||
" previous unclean shutdown. Please confirm and delete it or configure a different " +
|
||||
"working directory");
|
||||
}
|
||||
|
||||
this.workingDir = workingDir;
|
||||
|
||||
configuration.setWorkingDirectory(workingDir);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void start() {
|
||||
if (isRunning()) {
|
||||
return;
|
||||
}
|
||||
|
||||
DirectoryService ds = DirectoryService.getInstance(configuration.getInstanceId());
|
||||
|
||||
if (ds.isStarted()) {
|
||||
throw new IllegalStateException("A DirectoryService with Id '" + configuration.getInstanceId() + "' is already running.");
|
||||
}
|
||||
|
||||
logger.info("Starting directory server with Id '" + configuration.getInstanceId() + "'");
|
||||
Properties env = new Properties();
|
||||
|
||||
env.setProperty(Context.INITIAL_CONTEXT_FACTORY, ServerContextFactory.class.getName());
|
||||
env.setProperty(Context.SECURITY_AUTHENTICATION, "simple");
|
||||
env.setProperty(Context.SECURITY_PRINCIPAL, "uid=admin,ou=system");
|
||||
env.setProperty(Context.SECURITY_CREDENTIALS, "secret");
|
||||
env.putAll(configuration.toJndiEnvironment());
|
||||
|
||||
try {
|
||||
new InitialDirContext(env);
|
||||
} catch (NamingException e) {
|
||||
logger.error("Failed to start directory service", e);
|
||||
return;
|
||||
}
|
||||
|
||||
running = true;
|
||||
|
||||
try {
|
||||
importLdifs();
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to import LDIF file(s)", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void importLdifs() throws IOException, NamingException {
|
||||
// Import any ldif files
|
||||
Resource[] ldifs = ctxt.getResources(ldifResources);
|
||||
|
||||
// Note that we can't just import using the ServerContext returned
|
||||
// from starting Apace DS, apparently because of the long-running issue DIRSERVER-169.
|
||||
// We need a standard context.
|
||||
DirContext dirContext = contextSource.getReadWriteContext();
|
||||
|
||||
if(ldifs != null && ldifs.length > 0) {
|
||||
try {
|
||||
String ldifFile = ldifs[0].getFile().getAbsolutePath();
|
||||
logger.info("Loading LDIF file: " + ldifFile);
|
||||
LdifFileLoader loader = new LdifFileLoader(dirContext, ldifFile);
|
||||
loader.execute();
|
||||
} finally {
|
||||
dirContext.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void stop() {
|
||||
Properties env = new Properties();
|
||||
env.setProperty(Context.INITIAL_CONTEXT_FACTORY, ServerContextFactory.class.getName());
|
||||
env.setProperty(Context.SECURITY_AUTHENTICATION, "simple");
|
||||
env.setProperty(Context.SECURITY_PRINCIPAL, "uid=admin,ou=system");
|
||||
env.setProperty(Context.SECURITY_CREDENTIALS, "secret");
|
||||
|
||||
ShutdownConfiguration shutdown = new ShutdownConfiguration(configuration.getInstanceId());
|
||||
env.putAll(shutdown.toJndiEnvironment());
|
||||
|
||||
logger.info("Shutting down directory server with Id '" + configuration.getInstanceId() + "'");
|
||||
|
||||
try {
|
||||
new InitialContext(env);
|
||||
} catch (NamingException e) {
|
||||
logger.error("Failed to shutdown directory server", e);
|
||||
return;
|
||||
}
|
||||
|
||||
running = false;
|
||||
|
||||
if (workingDir.exists()) {
|
||||
logger.info("Deleting working directory " + workingDir.getAbsolutePath());
|
||||
deleteDir(workingDir);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return running;
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ package org.springframework.security.config;
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
abstract class Elements {
|
||||
public abstract class Elements {
|
||||
|
||||
public static final String AUTHENTICATION_MANAGER = "authentication-manager";
|
||||
public static final String USER_SERVICE = "user-service";
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
package org.springframework.security.config;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 2.0
|
||||
*/
|
||||
class LdapConfigUtils {
|
||||
|
||||
/**
|
||||
* Checks for the presence of a ContextSource instance. Also supplies the standard reference to any
|
||||
* unconfigured <ldap-authentication-provider> or <ldap-user-service> beans. This is
|
||||
* necessary in cases where the user has given the server a specific Id, but hasn't used
|
||||
* the server-ref attribute to link this to the other ldap definitions. See SEC-799.
|
||||
*/
|
||||
private static class ContextSourceSettingPostProcessor implements BeanFactoryPostProcessor, Ordered {
|
||||
/** If set to true, a bean parser has indicated that the default context source name needs to be set */
|
||||
private boolean defaultNameRequired;
|
||||
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory bf) throws BeansException {
|
||||
String[] sources = bf.getBeanNamesForType(BaseLdapPathContextSource.class);
|
||||
|
||||
if (sources.length == 0) {
|
||||
throw new SecurityConfigurationException("No BaseLdapPathContextSource instances found. Have you " +
|
||||
"added an <" + Elements.LDAP_SERVER + " /> element to your application context?");
|
||||
}
|
||||
|
||||
if (!bf.containsBean(BeanIds.CONTEXT_SOURCE) && defaultNameRequired) {
|
||||
if (sources.length > 1) {
|
||||
throw new SecurityConfigurationException("More than one BaseLdapPathContextSource instance found. " +
|
||||
"Please specify a specific server id using the 'server-ref' attribute when configuring your <" +
|
||||
Elements.LDAP_PROVIDER + "> " + "or <" + Elements.LDAP_USER_SERVICE + ">.");
|
||||
}
|
||||
|
||||
bf.registerAlias(sources[0], BeanIds.CONTEXT_SOURCE);
|
||||
}
|
||||
}
|
||||
|
||||
public void setDefaultNameRequired(boolean defaultNameRequired) {
|
||||
this.defaultNameRequired = defaultNameRequired;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return LOWEST_PRECEDENCE;
|
||||
}
|
||||
}
|
||||
|
||||
static void registerPostProcessorIfNecessary(BeanDefinitionRegistry registry, boolean defaultNameRequired) {
|
||||
if (registry.containsBeanDefinition(BeanIds.CONTEXT_SOURCE_SETTING_POST_PROCESSOR)) {
|
||||
if (defaultNameRequired) {
|
||||
BeanDefinition bd = registry.getBeanDefinition(BeanIds.CONTEXT_SOURCE_SETTING_POST_PROCESSOR);
|
||||
bd.getPropertyValues().addPropertyValue("defaultNameRequired", Boolean.valueOf(defaultNameRequired));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
BeanDefinition bd = new RootBeanDefinition(ContextSourceSettingPostProcessor.class);
|
||||
registry.registerBeanDefinition(BeanIds.CONTEXT_SOURCE_SETTING_POST_PROCESSOR, bd);
|
||||
bd.getPropertyValues().addPropertyValue("defaultNameRequired", Boolean.valueOf(defaultNameRequired));
|
||||
}
|
||||
|
||||
}
|
||||
+10
-13
@@ -1,17 +1,14 @@
|
||||
package org.springframework.security.config;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.security.providers.encoding.PasswordEncoder;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
@@ -83,9 +80,9 @@ public class LdapProviderBeanDefinitionParser implements BeanDefinitionParser {
|
||||
parserContext.getReaderContext().warning("Salt source information isn't valid when used with LDAP",
|
||||
passwordEncoderElement);
|
||||
}
|
||||
} else if (StringUtils.hasText(hash)) {
|
||||
Class<? extends PasswordEncoder> encoderClass = PasswordEncoderParser.ENCODER_CLASSES.get(hash);
|
||||
authenticatorBuilder.addPropertyValue("passwordEncoder", new RootBeanDefinition(encoderClass));
|
||||
} else if (StringUtils.hasText(hash)) {;
|
||||
authenticatorBuilder.addPropertyValue("passwordEncoder",
|
||||
PasswordEncoderParser.createPasswordEncoderBeanDefinition(hash, false));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -148,7 +148,7 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
|
||||
contextSource.addPropertyValue("userDn", "uid=admin,ou=system");
|
||||
contextSource.addPropertyValue("password", "secret");
|
||||
|
||||
RootBeanDefinition apacheContainer = new RootBeanDefinition("org.springframework.security.config.ApacheDSContainer", null, null);
|
||||
RootBeanDefinition apacheContainer = new RootBeanDefinition("org.springframework.security.config.ldap.ApacheDSContainer", null, null);
|
||||
apacheContainer.setSource(source);
|
||||
apacheContainer.getConstructorArgumentValues().addGenericArgumentValue(configuration.getBeanDefinition());
|
||||
apacheContainer.getConstructorArgumentValues().addGenericArgumentValue(contextSource.getBeanDefinition());
|
||||
|
||||
+17
-1
@@ -2,7 +2,9 @@ package org.springframework.security.config;
|
||||
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -88,11 +90,25 @@ public class LdapUserServiceBeanDefinitionParser extends AbstractUserDetailsServ
|
||||
|
||||
RuntimeBeanReference contextSource = new RuntimeBeanReference(server);
|
||||
contextSource.setSource(parserContext.extractSource(elt));
|
||||
LdapConfigUtils.registerPostProcessorIfNecessary(parserContext.getRegistry(), requiresDefaultName);
|
||||
registerPostProcessorIfNecessary(parserContext.getRegistry(), requiresDefaultName);
|
||||
|
||||
return contextSource;
|
||||
}
|
||||
|
||||
private static void registerPostProcessorIfNecessary(BeanDefinitionRegistry registry, boolean defaultNameRequired) {
|
||||
if (registry.containsBeanDefinition(BeanIds.CONTEXT_SOURCE_SETTING_POST_PROCESSOR)) {
|
||||
if (defaultNameRequired) {
|
||||
BeanDefinition bd = registry.getBeanDefinition(BeanIds.CONTEXT_SOURCE_SETTING_POST_PROCESSOR);
|
||||
bd.getPropertyValues().addPropertyValue("defaultNameRequired", Boolean.valueOf(defaultNameRequired));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
BeanDefinitionBuilder bdb = BeanDefinitionBuilder.rootBeanDefinition("org.springframework.security.config.ldap.ContextSourceSettingPostProcessor");
|
||||
bdb.addPropertyValue("defaultNameRequired", Boolean.valueOf(defaultNameRequired));
|
||||
registry.registerBeanDefinition(BeanIds.CONTEXT_SOURCE_SETTING_POST_PROCESSOR, bdb.getBeanDefinition());
|
||||
}
|
||||
|
||||
static RootBeanDefinition parseUserDetailsClass(Element elt, ParserContext parserContext) {
|
||||
String userDetailsClass = elt.getAttribute(ATT_USER_CLASS);
|
||||
|
||||
|
||||
@@ -6,16 +6,18 @@ import java.util.Map;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.security.providers.encoding.BaseDigestPasswordEncoder;
|
||||
import org.springframework.security.providers.encoding.LdapShaPasswordEncoder;
|
||||
import org.springframework.security.providers.encoding.Md4PasswordEncoder;
|
||||
import org.springframework.security.providers.encoding.Md5PasswordEncoder;
|
||||
import org.springframework.security.providers.encoding.PasswordEncoder;
|
||||
import org.springframework.security.providers.encoding.PlaintextPasswordEncoder;
|
||||
import org.springframework.security.providers.encoding.ShaPasswordEncoder;
|
||||
import org.springframework.security.providers.ldap.authenticator.LdapShaPasswordEncoder;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
import org.w3c.dom.Element;
|
||||
@@ -39,10 +41,10 @@ class PasswordEncoderParser {
|
||||
static final String OPT_HASH_MD5 = "md5";
|
||||
static final String OPT_HASH_LDAP_SHA = "{sha}";
|
||||
|
||||
static final Map<String, Class<? extends PasswordEncoder>> ENCODER_CLASSES;
|
||||
private static final Map<String, Class<? extends PasswordEncoder>> ENCODER_CLASSES;
|
||||
|
||||
static {
|
||||
ENCODER_CLASSES = new HashMap<String, Class<? extends PasswordEncoder>>(6);
|
||||
ENCODER_CLASSES = new HashMap<String, Class<? extends PasswordEncoder>>();
|
||||
ENCODER_CLASSES.put(OPT_HASH_PLAINTEXT, PlaintextPasswordEncoder.class);
|
||||
ENCODER_CLASSES.put(OPT_HASH_SHA, ShaPasswordEncoder.class);
|
||||
ENCODER_CLASSES.put(OPT_HASH_SHA256, ShaPasswordEncoder.class);
|
||||
@@ -51,7 +53,7 @@ class PasswordEncoderParser {
|
||||
ENCODER_CLASSES.put(OPT_HASH_LDAP_SHA, LdapShaPasswordEncoder.class);
|
||||
}
|
||||
|
||||
private Log logger = LogFactory.getLog(getClass());
|
||||
private static Log logger = LogFactory.getLog(PasswordEncoderParser.class);
|
||||
|
||||
private BeanMetadataElement passwordEncoder;
|
||||
private BeanMetadataElement saltSource;
|
||||
@@ -73,22 +75,8 @@ class PasswordEncoderParser {
|
||||
if (StringUtils.hasText(ref)) {
|
||||
passwordEncoder = new RuntimeBeanReference(ref);
|
||||
} else {
|
||||
Class<? extends PasswordEncoder> beanClass = ENCODER_CLASSES.get(hash);
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
|
||||
|
||||
if (OPT_HASH_SHA256.equals(hash)) {
|
||||
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, new Integer(256));
|
||||
}
|
||||
|
||||
beanDefinition.setSource(parserContext.extractSource(element));
|
||||
if (useBase64) {
|
||||
if (BaseDigestPasswordEncoder.class.isAssignableFrom(beanClass)) {
|
||||
beanDefinition.getPropertyValues().addPropertyValue("encodeHashAsBase64", "true");
|
||||
} else {
|
||||
logger.warn(ATT_BASE_64 + " isn't compatible with " + hash + " and will be ignored");
|
||||
}
|
||||
}
|
||||
passwordEncoder = beanDefinition;
|
||||
passwordEncoder = createPasswordEncoderBeanDefinition(hash, useBase64);
|
||||
((RootBeanDefinition)passwordEncoder).setSource(parserContext.extractSource(element));
|
||||
}
|
||||
|
||||
Element saltSourceElt = DomUtils.getChildElementByTagName(element, Elements.SALT_SOURCE);
|
||||
@@ -98,6 +86,24 @@ class PasswordEncoderParser {
|
||||
}
|
||||
}
|
||||
|
||||
static BeanDefinition createPasswordEncoderBeanDefinition(String hash, boolean useBase64) {
|
||||
Class<? extends PasswordEncoder> beanClass = ENCODER_CLASSES.get(hash);
|
||||
BeanDefinitionBuilder beanBldr = BeanDefinitionBuilder.rootBeanDefinition(beanClass);
|
||||
|
||||
if (OPT_HASH_SHA256.equals(hash)) {
|
||||
beanBldr.addConstructorArgValue(new Integer(256));
|
||||
}
|
||||
|
||||
if (useBase64) {
|
||||
if (BaseDigestPasswordEncoder.class.isAssignableFrom(beanClass)) {
|
||||
beanBldr.addPropertyValue("encodeHashAsBase64", "true");
|
||||
} else {
|
||||
logger.warn(ATT_BASE_64 + " isn't compatible with " + hash + " and will be ignored");
|
||||
}
|
||||
}
|
||||
return beanBldr.getBeanDefinition();
|
||||
}
|
||||
|
||||
public BeanMetadataElement getPasswordEncoder() {
|
||||
return passwordEncoder;
|
||||
}
|
||||
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
package org.springframework.security.ldap;
|
||||
|
||||
import org.springframework.ldap.core.DistinguishedName;
|
||||
|
||||
/**
|
||||
* This implementation appends a name component to the <tt>userDnBase</tt> context using the
|
||||
* <tt>usernameAttributeName</tt> property. So if the <tt>uid</tt> attribute is used to store the username, and the
|
||||
* base DN is <tt>cn=users</tt> and we are creating a new user called "sam", then the DN will be
|
||||
* <tt>uid=sam,cn=users</tt>.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public class DefaultLdapUsernameToDnMapper implements LdapUsernameToDnMapper {
|
||||
private String userDnBase;
|
||||
private String usernameAttribute;
|
||||
|
||||
/**
|
||||
* @param userDnBase the base name of the DN
|
||||
* @param usernameAttribute the attribute to append for the username component.
|
||||
*/
|
||||
public DefaultLdapUsernameToDnMapper(String userDnBase, String usernameAttribute) {
|
||||
this.userDnBase = userDnBase;
|
||||
this.usernameAttribute = usernameAttribute;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assembles the Distinguished Name that should be used the given username.
|
||||
*/
|
||||
public DistinguishedName buildDn(String username) {
|
||||
DistinguishedName dn = new DistinguishedName(userDnBase);
|
||||
|
||||
dn.add(usernameAttribute, username);
|
||||
|
||||
return dn;
|
||||
}
|
||||
}
|
||||
-80
@@ -1,80 +0,0 @@
|
||||
package org.springframework.security.ldap;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Hashtable;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.ldap.core.support.DirContextAuthenticationStrategy;
|
||||
import org.springframework.ldap.core.support.LdapContextSource;
|
||||
import org.springframework.ldap.core.support.SimpleDirContextAuthenticationStrategy;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* ContextSource implementation which uses Spring LDAP's <tt>LdapContextSource</tt> as a base
|
||||
* class. Used internally by the Spring Security LDAP namespace configuration.
|
||||
* <p>
|
||||
* From Spring Security 2.5, Spring LDAP 1.3 is used and the <tt>ContextSource</tt> interface
|
||||
* provides support for binding with a username and password. As a result, Spring LDAP <tt>ContextSource</tt>
|
||||
* implementations such as <tt>LdapContextSource</tt> may be used directly with Spring Security.
|
||||
* <p>
|
||||
* Spring LDAP 1.3 doesn't have JVM-level LDAP connection pooling enabled by default. This class sets the
|
||||
* <tt>pooled</tt> property to true, but customizes the {@link DirContextAuthenticationStrategy} used to disable
|
||||
* pooling when the <tt>DN</tt> doesn't match the <tt>userDn</tt> property. This prevents pooling for calls
|
||||
* to {@link #getContext(String, String)} to authenticate as specific users.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 2.0
|
||||
*/
|
||||
public class DefaultSpringSecurityContextSource extends LdapContextSource {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(DefaultSpringSecurityContextSource.class);
|
||||
private String rootDn;
|
||||
|
||||
/**
|
||||
* Create and initialize an instance which will connect to the supplied LDAP URL.
|
||||
*
|
||||
* @param providerUrl an LDAP URL of the form <code>ldap://localhost:389/base_dn<code>
|
||||
*/
|
||||
public DefaultSpringSecurityContextSource(String providerUrl) {
|
||||
Assert.hasLength(providerUrl, "An LDAP connection URL must be supplied.");
|
||||
|
||||
StringTokenizer st = new StringTokenizer(providerUrl);
|
||||
|
||||
ArrayList<String> urls = new ArrayList<String>();
|
||||
|
||||
// Work out rootDn from the first URL and check that the other URLs (if any) match
|
||||
while (st.hasMoreTokens()) {
|
||||
String url = st.nextToken();
|
||||
String urlRootDn = LdapUtils.parseRootDnFromUrl(url);
|
||||
|
||||
urls.add(url.substring(0, url.lastIndexOf(urlRootDn)));
|
||||
|
||||
logger.info(" URL '" + url + "', root DN is '" + urlRootDn + "'");
|
||||
|
||||
if (rootDn == null) {
|
||||
rootDn = urlRootDn;
|
||||
} else if (!rootDn.equals(urlRootDn)) {
|
||||
throw new IllegalArgumentException("Root DNs must be the same when using multiple URLs");
|
||||
}
|
||||
}
|
||||
|
||||
setUrls(urls.toArray(new String[urls.size()]));
|
||||
setBase(rootDn);
|
||||
setPooled(true);
|
||||
setAuthenticationStrategy(new SimpleDirContextAuthenticationStrategy() {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setupEnvironment(Hashtable env, String dn, String password) {
|
||||
super.setupEnvironment(env, dn, password);
|
||||
// Remove the pooling flag unless we are authenticating as the 'manager' user.
|
||||
if (!userDn.equals(dn) && env.containsKey(SUN_LDAP_POOLING_FLAG)) {
|
||||
logger.debug("Removing pooling flag for user " + dn);
|
||||
env.remove(SUN_LDAP_POOLING_FLAG);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.ldap;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.security.GrantedAuthority;
|
||||
|
||||
import org.springframework.ldap.core.DirContextOperations;
|
||||
|
||||
|
||||
/**
|
||||
* Obtains a list of granted authorities for an Ldap user.
|
||||
* <p>
|
||||
* Used by the <tt>LdapAuthenticationProvider</tt> once a user has been
|
||||
* authenticated to create the final user details object.
|
||||
* </p>
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface LdapAuthoritiesPopulator {
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
/**
|
||||
* Get the list of authorities for the user.
|
||||
*
|
||||
* @param userData the context object which was returned by the LDAP authenticator.
|
||||
*
|
||||
* @return the granted authorities for the given user.
|
||||
*
|
||||
*/
|
||||
List<GrantedAuthority> getGrantedAuthorities(DirContextOperations userData, String username);
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.ldap;
|
||||
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.directory.DirContext;
|
||||
|
||||
|
||||
/**
|
||||
* Callback object for use with SpringSecurityLdapTemplate.
|
||||
*
|
||||
* @deprecated use spring-ldap ContextExecutor instead.
|
||||
* @TODO: Delete before 2.0 release
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
public interface LdapCallback {
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
Object doInDirContext(DirContext dirContext)
|
||||
throws NamingException;
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.ldap;
|
||||
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.directory.Attributes;
|
||||
|
||||
|
||||
/**
|
||||
* A mapper for use with {@link SpringSecurityLdapTemplate}. Creates a customized object from
|
||||
* a set of attributes retrieved from a directory entry.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @deprecated in favour of Spring LDAP ContextMapper
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface LdapEntryMapper {
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
Object mapAttributes(String dn, Attributes attributes)
|
||||
throws NamingException;
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.ldap;
|
||||
|
||||
import org.springframework.ldap.core.DirContextOperations;
|
||||
import org.springframework.security.userdetails.UsernameNotFoundException;
|
||||
|
||||
|
||||
/**
|
||||
* Obtains a user's information from the LDAP directory given a login name.
|
||||
* <p>
|
||||
* May be optionally used to configure the LDAP authentication implementation when
|
||||
* a more sophisticated approach is required than just using a simple username->DN
|
||||
* mapping.
|
||||
* </p>
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface LdapUserSearch {
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
/**
|
||||
* Locates a single user in the directory and returns the LDAP information for that user.
|
||||
*
|
||||
* @param username the login name supplied to the authentication service.
|
||||
*
|
||||
* @return a DirContextOperations object containing the user's full DN and requested attributes.
|
||||
* @throws UsernameNotFoundException if no user with the supplied name could be located by the search.
|
||||
*/
|
||||
DirContextOperations searchForUser(String username) throws UsernameNotFoundException;
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package org.springframework.security.ldap;
|
||||
|
||||
import org.springframework.ldap.core.DistinguishedName;
|
||||
|
||||
/**
|
||||
* Constructs an Ldap Distinguished Name from a username.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface LdapUsernameToDnMapper {
|
||||
DistinguishedName buildDn(String username);
|
||||
}
|
||||
@@ -1,192 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.ldap;
|
||||
|
||||
import org.springframework.ldap.core.DirContextAdapter;
|
||||
import org.springframework.ldap.core.DistinguishedName;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import javax.naming.Context;
|
||||
import javax.naming.NamingException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
|
||||
/**
|
||||
* LDAP Utility methods.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public final class LdapUtils {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
|
||||
private static final Log logger = LogFactory.getLog(LdapUtils.class);
|
||||
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
private LdapUtils() {
|
||||
}
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public static void closeContext(Context ctx) {
|
||||
if(ctx instanceof DirContextAdapter) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (ctx != null) {
|
||||
ctx.close();
|
||||
}
|
||||
} catch (NamingException e) {
|
||||
logger.error("Failed to close context.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the part of a DN relative to a supplied base context.
|
||||
* <p>If the DN is "cn=bob,ou=people,dc=springframework,dc=org" and the base context name is
|
||||
* "ou=people,dc=springframework,dc=org" it would return "cn=bob".
|
||||
* </p>
|
||||
*
|
||||
* @param fullDn the DN
|
||||
* @param baseCtx the context to work out the name relative to.
|
||||
*
|
||||
* @return the
|
||||
*
|
||||
* @throws NamingException any exceptions thrown by the context are propagated.
|
||||
*/
|
||||
public static String getRelativeName(String fullDn, Context baseCtx) throws NamingException {
|
||||
|
||||
String baseDn = baseCtx.getNameInNamespace();
|
||||
|
||||
if (baseDn.length() == 0) {
|
||||
return fullDn;
|
||||
}
|
||||
|
||||
DistinguishedName base = new DistinguishedName(baseDn);
|
||||
DistinguishedName full = new DistinguishedName(fullDn);
|
||||
|
||||
if(base.equals(full)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
Assert.isTrue(full.startsWith(base), "Full DN does not start with base DN");
|
||||
|
||||
full.removeFirst(base);
|
||||
|
||||
return full.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the full dn of a name by prepending the name of the context it is relative to.
|
||||
* If the name already contains the base name, it is returned unaltered.
|
||||
*/
|
||||
public static DistinguishedName getFullDn(DistinguishedName dn, Context baseCtx)
|
||||
throws NamingException {
|
||||
DistinguishedName baseDn = new DistinguishedName(baseCtx.getNameInNamespace());
|
||||
|
||||
if(dn.contains(baseDn)) {
|
||||
return dn;
|
||||
}
|
||||
|
||||
baseDn.append(dn);
|
||||
|
||||
return baseDn;
|
||||
}
|
||||
|
||||
public static byte[] getUtf8Bytes(String s) {
|
||||
try {
|
||||
return s.getBytes("UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// Should be impossible since UTF-8 is required by all implementations
|
||||
throw new IllegalStateException("Failed to convert string to UTF-8 bytes. Shouldn't be possible");
|
||||
}
|
||||
}
|
||||
|
||||
public static String getUtf8BytesAsString(byte[] utf8) {
|
||||
try {
|
||||
return new String(utf8, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// Should be impossible since UTF-8 is required by all implementations
|
||||
throw new IllegalStateException("Failed to convert string to UTF-8 bytes. Shouldn't be possible");
|
||||
}
|
||||
}
|
||||
|
||||
public static String convertPasswordToString(Object passObj) {
|
||||
Assert.notNull(passObj, "Password object to convert must not be null");
|
||||
|
||||
if(passObj instanceof byte[]) {
|
||||
return getUtf8BytesAsString((byte[])passObj);
|
||||
} else if (passObj instanceof String) {
|
||||
return (String)passObj;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Password object was not a String or byte array.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Works out the root DN for an LDAP URL.<p>For example, the URL
|
||||
* <tt>ldap://monkeymachine:11389/dc=springframework,dc=org</tt> has the root DN "dc=springframework,dc=org".</p>
|
||||
*
|
||||
* @param url the LDAP URL
|
||||
*
|
||||
* @return the root DN
|
||||
*/
|
||||
public static String parseRootDnFromUrl(String url) {
|
||||
Assert.hasLength(url);
|
||||
|
||||
String urlRootDn = "";
|
||||
|
||||
if (url.startsWith("ldap:") || url.startsWith("ldaps:")) {
|
||||
URI uri = parseLdapUrl(url);
|
||||
urlRootDn = uri.getRawPath();
|
||||
} else {
|
||||
// Assume it's an embedded server
|
||||
urlRootDn = url;
|
||||
}
|
||||
|
||||
if (urlRootDn.startsWith("/")) {
|
||||
urlRootDn = urlRootDn.substring(1);
|
||||
}
|
||||
|
||||
return urlRootDn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the supplied LDAP URL.
|
||||
* @param url the URL (e.g. <tt>ldap://monkeymachine:11389/dc=springframework,dc=org</tt>).
|
||||
* @return the URI object created from the URL
|
||||
* @throws IllegalArgumentException if the URL is null, empty or the URI syntax is invalid.
|
||||
*/
|
||||
|
||||
private static URI parseLdapUrl(String url) {
|
||||
Assert.hasLength(url);
|
||||
|
||||
try {
|
||||
return new URI(url);
|
||||
} catch (URISyntaxException e) {
|
||||
IllegalArgumentException iae = new IllegalArgumentException("Unable to parse url: " + url);
|
||||
iae.initCause(e);
|
||||
throw iae;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.ldap;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
|
||||
import javax.naming.NamingException;
|
||||
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
* @deprecated Spring ldap is used instead.
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface NamingExceptionTranslator {
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
DataAccessException translate(String task, NamingException e);
|
||||
}
|
||||
-69
@@ -1,69 +0,0 @@
|
||||
package org.springframework.security.ldap;
|
||||
|
||||
import org.springframework.security.Authentication;
|
||||
import org.springframework.security.context.SecurityContextHolder;
|
||||
import org.springframework.security.providers.anonymous.AnonymousAuthenticationToken;
|
||||
import org.springframework.security.userdetails.ldap.LdapUserDetails;
|
||||
import org.springframework.ldap.core.AuthenticationSource;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* An AuthenticationSource to retrieve authentication information stored in Spring Security's
|
||||
* {@link SecurityContextHolder}.
|
||||
* <p>
|
||||
* This is a copy of Spring LDAP's AcegiAuthenticationSource, updated for use with Spring Security 2.0.
|
||||
*
|
||||
* @author Mattias Arthursson
|
||||
* @author Luke Taylor
|
||||
* @since 2.0
|
||||
* @version $Id$
|
||||
*/
|
||||
public class SpringSecurityAuthenticationSource implements AuthenticationSource {
|
||||
private static final Log log = LogFactory.getLog(SpringSecurityAuthenticationSource.class);
|
||||
|
||||
/**
|
||||
* Get the principals of the logged in user, in this case the distinguished
|
||||
* name.
|
||||
*
|
||||
* @return the distinguished name of the logged in user.
|
||||
*/
|
||||
public String getPrincipal() {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
|
||||
if (authentication == null) {
|
||||
log.warn("No Authentication object set in SecurityContext - returning empty String as Principal");
|
||||
return "";
|
||||
}
|
||||
|
||||
Object principal = authentication.getPrincipal();
|
||||
|
||||
if (principal instanceof LdapUserDetails) {
|
||||
LdapUserDetails details = (LdapUserDetails) principal;
|
||||
return details.getDn();
|
||||
} else if (authentication instanceof AnonymousAuthenticationToken) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Anonymous Authentication, returning empty String as Principal");
|
||||
}
|
||||
return "";
|
||||
} else {
|
||||
throw new IllegalArgumentException("The principal property of the authentication object"
|
||||
+ "needs to be an LdapUserDetails.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.springframework.ldap.core.AuthenticationSource#getCredentials()
|
||||
*/
|
||||
public String getCredentials() {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
|
||||
if (authentication == null) {
|
||||
log.warn("No Authentication object set in SecurityContext - returning empty String as Credentials");
|
||||
return "";
|
||||
}
|
||||
|
||||
return (String) authentication.getCredentials();
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package org.springframework.security.ldap;
|
||||
|
||||
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
|
||||
import org.springframework.ldap.core.ContextSource;
|
||||
|
||||
import javax.naming.directory.DirContext;
|
||||
|
||||
/**
|
||||
* Extension of {@link ContextSource} which allows binding explicitly as a particular user.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 2.0
|
||||
*
|
||||
* @deprecated As of Spring LDAP 1.3, ContextSource provides this method itself.
|
||||
*/
|
||||
public interface SpringSecurityContextSource extends BaseLdapPathContextSource {
|
||||
|
||||
/**
|
||||
* Obtains a context using the supplied distinguished name and credentials.
|
||||
*
|
||||
* @param userDn the distinguished name of the user to authenticate as
|
||||
* @param credentials the user's password
|
||||
* @return a context authenticated as the supplied user
|
||||
*/
|
||||
DirContext getReadWriteContext(String userDn, Object credentials);
|
||||
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.ldap;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.naming.NamingEnumeration;
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.PartialResultException;
|
||||
import javax.naming.directory.Attributes;
|
||||
import javax.naming.directory.DirContext;
|
||||
import javax.naming.directory.SearchControls;
|
||||
import javax.naming.directory.SearchResult;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
import org.springframework.ldap.core.ContextExecutor;
|
||||
import org.springframework.ldap.core.ContextMapper;
|
||||
import org.springframework.ldap.core.ContextSource;
|
||||
import org.springframework.ldap.core.DirContextAdapter;
|
||||
import org.springframework.ldap.core.DirContextOperations;
|
||||
import org.springframework.ldap.core.DistinguishedName;
|
||||
import org.springframework.ldap.core.LdapEncoder;
|
||||
import org.springframework.ldap.core.LdapTemplate;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* Extension of Spring LDAP's LdapTemplate class which adds extra functionality required by Spring Security.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Luke Taylor
|
||||
* @since 2.0
|
||||
*/
|
||||
public class SpringSecurityLdapTemplate extends LdapTemplate {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
private static final Log logger = LogFactory.getLog(SpringSecurityLdapTemplate.class);
|
||||
|
||||
public static final String[] NO_ATTRS = new String[0];
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
/** Default search controls */
|
||||
private SearchControls searchControls = new SearchControls();
|
||||
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
public SpringSecurityLdapTemplate(ContextSource contextSource) {
|
||||
Assert.notNull(contextSource, "ContextSource cannot be null");
|
||||
setContextSource(contextSource);
|
||||
|
||||
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
|
||||
}
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
/**
|
||||
* Performs an LDAP compare operation of the value of an attribute for a particular directory entry.
|
||||
*
|
||||
* @param dn the entry who's attribute is to be used
|
||||
* @param attributeName the attribute who's value we want to compare
|
||||
* @param value the value to be checked against the directory value
|
||||
*
|
||||
* @return true if the supplied value matches that in the directory
|
||||
*/
|
||||
public boolean compare(final String dn, final String attributeName, final Object value) {
|
||||
final String comparisonFilter = "(" + attributeName + "={0})";
|
||||
|
||||
class LdapCompareCallback implements ContextExecutor {
|
||||
|
||||
public Object executeWithContext(DirContext ctx) throws NamingException {
|
||||
SearchControls ctls = new SearchControls();
|
||||
ctls.setReturningAttributes(NO_ATTRS);
|
||||
ctls.setSearchScope(SearchControls.OBJECT_SCOPE);
|
||||
|
||||
NamingEnumeration<SearchResult> results = ctx.search(dn, comparisonFilter, new Object[] {value}, ctls);
|
||||
|
||||
return Boolean.valueOf(results.hasMore());
|
||||
}
|
||||
}
|
||||
|
||||
Boolean matches = (Boolean) executeReadOnly(new LdapCompareCallback());
|
||||
|
||||
return matches.booleanValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Composes an object from the attributes of the given DN.
|
||||
*
|
||||
* @param dn the directory entry which will be read
|
||||
* @param attributesToRetrieve the named attributes which will be retrieved from the directory entry.
|
||||
*
|
||||
* @return the object created by the mapper
|
||||
*/
|
||||
public DirContextOperations retrieveEntry(final String dn, final String[] attributesToRetrieve) {
|
||||
|
||||
return (DirContextOperations) executeReadOnly(new ContextExecutor() {
|
||||
public Object executeWithContext(DirContext ctx) throws NamingException {
|
||||
Attributes attrs = ctx.getAttributes(dn, attributesToRetrieve);
|
||||
|
||||
// Object object = ctx.lookup(LdapUtils.getRelativeName(dn, ctx));
|
||||
|
||||
return new DirContextAdapter(attrs, new DistinguishedName(dn),
|
||||
new DistinguishedName(ctx.getNameInNamespace()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a search using the supplied filter and returns the union of the values of the named attribute
|
||||
* found in all entries matched by the search. Note that one directory entry may have several values for the
|
||||
* attribute. Intended for role searches and similar scenarios.
|
||||
*
|
||||
* @param base the DN to search in
|
||||
* @param filter search filter to use
|
||||
* @param params the parameters to substitute in the search filter
|
||||
* @param attributeName the attribute who's values are to be retrieved.
|
||||
*
|
||||
* @return the set of String values for the attribute as a union of the values found in all the matching entries.
|
||||
*/
|
||||
public Set<String> searchForSingleAttributeValues(final String base, final String filter, final Object[] params,
|
||||
final String attributeName) {
|
||||
// Escape the params acording to RFC2254
|
||||
Object[] encodedParams = new String[params.length];
|
||||
|
||||
for (int i=0; i < params.length; i++) {
|
||||
encodedParams[i] = LdapEncoder.filterEncode(params[i].toString());
|
||||
}
|
||||
|
||||
String formattedFilter = MessageFormat.format(filter, encodedParams);
|
||||
logger.debug("Using filter: " + formattedFilter);
|
||||
|
||||
final HashSet<String> set = new HashSet<String>();
|
||||
|
||||
ContextMapper roleMapper = new ContextMapper() {
|
||||
public Object mapFromContext(Object ctx) {
|
||||
DirContextAdapter adapter = (DirContextAdapter) ctx;
|
||||
String[] values = adapter.getStringAttributes(attributeName);
|
||||
if (values == null || values.length == 0) {
|
||||
logger.debug("No attribute value found for '" + attributeName + "'");
|
||||
} else {
|
||||
set.addAll(Arrays.asList(values));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
SearchControls ctls = new SearchControls();
|
||||
ctls.setSearchScope(searchControls.getSearchScope());
|
||||
ctls.setReturningAttributes(new String[] {attributeName});
|
||||
|
||||
search(base, formattedFilter, ctls, roleMapper);
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a search, with the requirement that the search shall return a single directory entry, and uses
|
||||
* the supplied mapper to create the object from that entry.
|
||||
* <p>
|
||||
* Ignores <tt>PartialResultException</tt> if thrown, for compatibility with Active Directory
|
||||
* (see {@link LdapTemplate#setIgnorePartialResultException(boolean)}).
|
||||
*
|
||||
* @param base the search base, relative to the base context supplied by the context source.
|
||||
* @param filter the LDAP search filter
|
||||
* @param params parameters to be substituted in the search.
|
||||
*
|
||||
* @return a DirContextOperations instance created from the matching entry.
|
||||
*
|
||||
* @throws IncorrectResultSizeDataAccessException if no results are found or the search returns more than one
|
||||
* result.
|
||||
*/
|
||||
public DirContextOperations searchForSingleEntry(final String base, final String filter, final Object[] params) {
|
||||
|
||||
return (DirContextOperations) executeReadOnly(new ContextExecutor() {
|
||||
public Object executeWithContext(DirContext ctx) throws NamingException {
|
||||
DistinguishedName ctxBaseDn = new DistinguishedName(ctx.getNameInNamespace());
|
||||
NamingEnumeration<SearchResult> resultsEnum = ctx.search(base, filter, params, searchControls);
|
||||
Set<DirContextOperations> results = new HashSet<DirContextOperations>();
|
||||
try {
|
||||
while (resultsEnum.hasMore()) {
|
||||
|
||||
SearchResult searchResult = resultsEnum.next();
|
||||
// Work out the DN of the matched entry
|
||||
StringBuffer dn = new StringBuffer(searchResult.getName());
|
||||
|
||||
if (base.length() > 0) {
|
||||
dn.append(",");
|
||||
dn.append(base);
|
||||
}
|
||||
|
||||
results.add(new DirContextAdapter(searchResult.getAttributes(),
|
||||
new DistinguishedName(dn.toString()), ctxBaseDn));
|
||||
}
|
||||
} catch (PartialResultException e) {
|
||||
logger.info("Ignoring PartialResultException");
|
||||
}
|
||||
|
||||
if (results.size() == 0) {
|
||||
throw new IncorrectResultSizeDataAccessException(1, 0);
|
||||
}
|
||||
|
||||
if (results.size() > 1) {
|
||||
throw new IncorrectResultSizeDataAccessException(1, results.size());
|
||||
}
|
||||
|
||||
return results.toArray()[0];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the search controls which will be used for search operations by the template.
|
||||
*
|
||||
* @param searchControls the SearchControls instance which will be cached in the template.
|
||||
*/
|
||||
public void setSearchControls(SearchControls searchControls) {
|
||||
this.searchControls = searchControls;
|
||||
}
|
||||
}
|
||||
-295
@@ -1,295 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.ldap.populator;
|
||||
|
||||
import org.springframework.security.GrantedAuthority;
|
||||
import org.springframework.security.GrantedAuthorityImpl;
|
||||
import org.springframework.security.ldap.SpringSecurityLdapTemplate;
|
||||
import org.springframework.security.ldap.LdapAuthoritiesPopulator;
|
||||
import org.springframework.ldap.core.ContextSource;
|
||||
import org.springframework.ldap.core.DirContextOperations;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import javax.naming.directory.SearchControls;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
/**
|
||||
* The default strategy for obtaining user role information from the directory.
|
||||
* <p>
|
||||
* It obtains roles by performing a search for "groups" the user is a member of.
|
||||
* <p>
|
||||
* A typical group search scenario would be where each group/role is specified using the <tt>groupOfNames</tt>
|
||||
* (or <tt>groupOfUniqueNames</tt>) LDAP objectClass and the user's DN is listed in the <tt>member</tt> (or
|
||||
* <tt>uniqueMember</tt>) attribute to indicate that they should be assigned that role. The following LDIF sample has
|
||||
* the groups stored under the DN <tt>ou=groups,dc=springframework,dc=org</tt> and a group called "developers" with
|
||||
* "ben" and "luke" as members:
|
||||
* <pre>
|
||||
* dn: ou=groups,dc=springframework,dc=org
|
||||
* objectClass: top
|
||||
* objectClass: organizationalUnit
|
||||
* ou: groups
|
||||
*
|
||||
* dn: cn=developers,ou=groups,dc=springframework,dc=org
|
||||
* objectClass: groupOfNames
|
||||
* objectClass: top
|
||||
* cn: developers
|
||||
* description: Spring Security Developers
|
||||
* member: uid=ben,ou=people,dc=springframework,dc=org
|
||||
* member: uid=luke,ou=people,dc=springframework,dc=org
|
||||
* ou: developer
|
||||
* </pre>
|
||||
* <p>
|
||||
* The group search is performed within a DN specified by the <tt>groupSearchBase</tt> property, which should
|
||||
* be relative to the root DN of its <tt>InitialDirContextFactory</tt>. If the search base is null, group searching is
|
||||
* disabled. The filter used in the search is defined by the <tt>groupSearchFilter</tt> property, with the filter
|
||||
* argument {0} being the full DN of the user. You can also optionally use the parameter {1}, which will be substituted
|
||||
* with the username. You can also specify which attribute defines the role name by setting
|
||||
* the <tt>groupRoleAttribute</tt> property (the default is "cn").
|
||||
* <p>
|
||||
* The configuration below shows how the group search might be performed with the above schema.
|
||||
* <pre>
|
||||
* <bean id="ldapAuthoritiesPopulator"
|
||||
* class="org.springframework.security.providers.ldap.populator.DefaultLdapAuthoritiesPopulator">
|
||||
* <constructor-arg ref="contextSource"/>
|
||||
* <constructor-arg value="ou=groups"/>
|
||||
* <property name="groupRoleAttribute" value="ou"/>
|
||||
* <!-- the following properties are shown with their default values -->
|
||||
* <property name="searchSubTree" value="false"/>
|
||||
* <property name="rolePrefix" value="ROLE_"/>
|
||||
* <property name="convertToUpperCase" value="true"/>
|
||||
* </bean>
|
||||
* </pre>
|
||||
* A search for roles for user "uid=ben,ou=people,dc=springframework,dc=org" would return the single granted authority
|
||||
* "ROLE_DEVELOPER".
|
||||
* <p>
|
||||
* The single-level search is performed by default. Setting the <tt>searchSubTree</tt> property to true will enable
|
||||
* a search of the entire subtree under <tt>groupSearchBase</tt>.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
|
||||
private static final Log logger = LogFactory.getLog(DefaultLdapAuthoritiesPopulator.class);
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
/**
|
||||
* A default role which will be assigned to all authenticated users if set
|
||||
*/
|
||||
private GrantedAuthority defaultRole;
|
||||
|
||||
private SpringSecurityLdapTemplate ldapTemplate;
|
||||
|
||||
/**
|
||||
* Controls used to determine whether group searches should be performed over the full sub-tree from the
|
||||
* base DN. Modified by searchSubTree property
|
||||
*/
|
||||
private SearchControls searchControls = new SearchControls();
|
||||
|
||||
/**
|
||||
* The ID of the attribute which contains the role name for a group
|
||||
*/
|
||||
private String groupRoleAttribute = "cn";
|
||||
|
||||
/**
|
||||
* The base DN from which the search for group membership should be performed
|
||||
*/
|
||||
private String groupSearchBase;
|
||||
|
||||
/**
|
||||
* The pattern to be used for the user search. {0} is the user's DN
|
||||
*/
|
||||
private String groupSearchFilter = "(member={0})";
|
||||
|
||||
/**
|
||||
* Attributes of the User's LDAP Object that contain role name information.
|
||||
*/
|
||||
|
||||
// private String[] userRoleAttributes = null;
|
||||
private String rolePrefix = "ROLE_";
|
||||
private boolean convertToUpperCase = true;
|
||||
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
/**
|
||||
* Constructor for group search scenarios. <tt>userRoleAttributes</tt> may still be
|
||||
* set as a property.
|
||||
*
|
||||
* @param contextSource supplies the contexts used to search for user roles.
|
||||
* @param groupSearchBase if this is an empty string the search will be performed from the root DN of the
|
||||
* context factory.
|
||||
*/
|
||||
public DefaultLdapAuthoritiesPopulator(ContextSource contextSource, String groupSearchBase) {
|
||||
Assert.notNull(contextSource, "contextSource must not be null");
|
||||
ldapTemplate = new SpringSecurityLdapTemplate(contextSource);
|
||||
ldapTemplate.setSearchControls(searchControls);
|
||||
setGroupSearchBase(groupSearchBase);
|
||||
}
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
/**
|
||||
* This method should be overridden if required to obtain any additional
|
||||
* roles for the given user (on top of those obtained from the standard
|
||||
* search implemented by this class).
|
||||
*
|
||||
* @param user the context representing the user who's roles are required
|
||||
* @return the extra roles which will be merged with those returned by the group search
|
||||
*/
|
||||
|
||||
protected Set<GrantedAuthority> getAdditionalRoles(DirContextOperations user, String username) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the authorities for the user who's directory entry is represented by
|
||||
* the supplied LdapUserDetails object.
|
||||
*
|
||||
* @param user the user who's authorities are required
|
||||
* @return the set of roles granted to the user.
|
||||
*/
|
||||
public final List<GrantedAuthority> getGrantedAuthorities(DirContextOperations user, String username) {
|
||||
String userDn = user.getNameInNamespace();
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Getting authorities for user " + userDn);
|
||||
}
|
||||
|
||||
Set<GrantedAuthority> roles = getGroupMembershipRoles(userDn, username);
|
||||
|
||||
Set<GrantedAuthority> extraRoles = getAdditionalRoles(user, username);
|
||||
|
||||
if (extraRoles != null) {
|
||||
roles.addAll(extraRoles);
|
||||
}
|
||||
|
||||
if (defaultRole != null) {
|
||||
roles.add(defaultRole);
|
||||
}
|
||||
|
||||
List<GrantedAuthority> result = new ArrayList<GrantedAuthority>(roles.size());
|
||||
result.addAll(roles);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Set<GrantedAuthority> getGroupMembershipRoles(String userDn, String username) {
|
||||
if (getGroupSearchBase() == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Searching for roles for user '" + username + "', DN = " + "'" + userDn + "', with filter "
|
||||
+ groupSearchFilter + " in search base '" + getGroupSearchBase() + "'");
|
||||
}
|
||||
|
||||
Set<String> userRoles = ldapTemplate.searchForSingleAttributeValues(getGroupSearchBase(), groupSearchFilter,
|
||||
new String[]{userDn, username}, groupRoleAttribute);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Roles from search: " + userRoles);
|
||||
}
|
||||
|
||||
for (String role : userRoles) {
|
||||
|
||||
if (convertToUpperCase) {
|
||||
role = role.toUpperCase();
|
||||
}
|
||||
|
||||
authorities.add(new GrantedAuthorityImpl(rolePrefix + role));
|
||||
}
|
||||
|
||||
return authorities;
|
||||
}
|
||||
|
||||
protected ContextSource getContextSource() {
|
||||
return ldapTemplate.getContextSource();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the group search base (name to search under)
|
||||
*
|
||||
* @param groupSearchBase if this is an empty string the search will be performed from the root DN of the context
|
||||
* factory.
|
||||
*/
|
||||
private void setGroupSearchBase(String groupSearchBase) {
|
||||
Assert.notNull(groupSearchBase, "The groupSearchBase (name to search under), must not be null.");
|
||||
this.groupSearchBase = groupSearchBase;
|
||||
if (groupSearchBase.length() == 0) {
|
||||
logger.info("groupSearchBase is empty. Searches will be performed from the context source base");
|
||||
}
|
||||
}
|
||||
|
||||
protected String getGroupSearchBase() {
|
||||
return groupSearchBase;
|
||||
}
|
||||
|
||||
public void setConvertToUpperCase(boolean convertToUpperCase) {
|
||||
this.convertToUpperCase = convertToUpperCase;
|
||||
}
|
||||
|
||||
/**
|
||||
* The default role which will be assigned to all users.
|
||||
*
|
||||
* @param defaultRole the role name, including any desired prefix.
|
||||
*/
|
||||
public void setDefaultRole(String defaultRole) {
|
||||
Assert.notNull(defaultRole, "The defaultRole property cannot be set to null");
|
||||
this.defaultRole = new GrantedAuthorityImpl(defaultRole);
|
||||
}
|
||||
|
||||
public void setGroupRoleAttribute(String groupRoleAttribute) {
|
||||
Assert.notNull(groupRoleAttribute, "groupRoleAttribute must not be null");
|
||||
this.groupRoleAttribute = groupRoleAttribute;
|
||||
}
|
||||
|
||||
public void setGroupSearchFilter(String groupSearchFilter) {
|
||||
Assert.notNull(groupSearchFilter, "groupSearchFilter must not be null");
|
||||
this.groupSearchFilter = groupSearchFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the prefix which will be prepended to the values loaded from the directory.
|
||||
* Defaults to "ROLE_" for compatibility with <tt>RoleVoter/tt>.
|
||||
*/
|
||||
public void setRolePrefix(String rolePrefix) {
|
||||
Assert.notNull(rolePrefix, "rolePrefix must not be null");
|
||||
this.rolePrefix = rolePrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to true, a subtree scope search will be performed. If false a single-level search is used.
|
||||
*
|
||||
* @param searchSubtree set to true to enable searching of the entire tree below the <tt>groupSearchBase</tt>.
|
||||
*/
|
||||
public void setSearchSubtree(boolean searchSubtree) {
|
||||
int searchScope = searchSubtree ? SearchControls.SUBTREE_SCOPE : SearchControls.ONELEVEL_SCOPE;
|
||||
searchControls.setSearchScope(searchScope);
|
||||
}
|
||||
}
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
package org.springframework.security.ldap.populator;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.security.ldap.LdapAuthoritiesPopulator;
|
||||
import org.springframework.security.GrantedAuthority;
|
||||
import org.springframework.security.userdetails.UserDetailsService;
|
||||
import org.springframework.ldap.core.DirContextOperations;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Simple LdapAuthoritiesPopulator which delegates to a UserDetailsService, using the name which
|
||||
* was supplied at login as the username.
|
||||
*
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 2.0
|
||||
*/
|
||||
public class UserDetailsServiceLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator {
|
||||
private UserDetailsService userDetailsService;
|
||||
|
||||
public UserDetailsServiceLdapAuthoritiesPopulator(UserDetailsService userService) {
|
||||
Assert.notNull(userService, "userDetailsService cannot be null");
|
||||
this.userDetailsService = userService;
|
||||
}
|
||||
|
||||
public List<GrantedAuthority> getGrantedAuthorities(DirContextOperations userData, String username) {
|
||||
return userDetailsService.loadUserByUsername(username).getAuthorities();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
<html>
|
||||
<body>
|
||||
LdapAuthoritiesPopulator implementations.
|
||||
</body>
|
||||
</html>
|
||||
-182
@@ -1,182 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.ldap.search;
|
||||
|
||||
import org.springframework.security.ldap.SpringSecurityLdapTemplate;
|
||||
import org.springframework.security.ldap.LdapUserSearch;
|
||||
|
||||
import org.springframework.security.userdetails.UsernameNotFoundException;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import org.springframework.ldap.core.ContextSource;
|
||||
import org.springframework.ldap.core.DirContextOperations;
|
||||
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
|
||||
|
||||
import javax.naming.directory.SearchControls;
|
||||
|
||||
|
||||
/**
|
||||
* LdapUserSearch implementation which uses an Ldap filter to locate the user.
|
||||
*
|
||||
* @author Robert Sanders
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*
|
||||
* @see SearchControls
|
||||
*/
|
||||
public class FilterBasedLdapUserSearch implements LdapUserSearch {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
|
||||
private static final Log logger = LogFactory.getLog(FilterBasedLdapUserSearch.class);
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private ContextSource contextSource;
|
||||
|
||||
/**
|
||||
* The LDAP SearchControls object used for the search. Shared between searches so shouldn't be modified
|
||||
* once the bean has been configured.
|
||||
*/
|
||||
private SearchControls searchControls = new SearchControls();
|
||||
|
||||
/** Context name to search in, relative to the base of the configured ContextSource. */
|
||||
private String searchBase = "";
|
||||
|
||||
/**
|
||||
* The filter expression used in the user search. This is an LDAP search filter (as defined in 'RFC 2254')
|
||||
* with optional arguments. See the documentation for the <tt>search</tt> methods in {@link
|
||||
* javax.naming.directory.DirContext DirContext} for more information.
|
||||
*
|
||||
* <p>In this case, the username is the only parameter.</p>
|
||||
* Possible examples are:
|
||||
* <ul>
|
||||
* <li>(uid={0}) - this would search for a username match on the uid attribute.</li>
|
||||
* </ul>
|
||||
*/
|
||||
private String searchFilter;
|
||||
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
public FilterBasedLdapUserSearch(String searchBase, String searchFilter, BaseLdapPathContextSource contextSource) {
|
||||
Assert.notNull(contextSource, "contextSource must not be null");
|
||||
Assert.notNull(searchFilter, "searchFilter must not be null.");
|
||||
Assert.notNull(searchBase, "searchBase must not be null (an empty string is acceptable).");
|
||||
|
||||
this.searchFilter = searchFilter;
|
||||
this.contextSource = contextSource;
|
||||
this.searchBase = searchBase;
|
||||
|
||||
setSearchSubtree(true);
|
||||
|
||||
if (searchBase.length() == 0) {
|
||||
logger.info("SearchBase not set. Searches will be performed from the root: "
|
||||
+ contextSource.getBaseLdapPath());
|
||||
}
|
||||
}
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
/**
|
||||
* Return the LdapUserDetails containing the user's information
|
||||
*
|
||||
* @param username the username to search for.
|
||||
*
|
||||
* @return An LdapUserDetails object containing the details of the located user's directory entry
|
||||
*
|
||||
* @throws UsernameNotFoundException if no matching entry is found.
|
||||
*/
|
||||
public DirContextOperations searchForUser(String username) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Searching for user '" + username + "', with user search " + this);
|
||||
}
|
||||
|
||||
SpringSecurityLdapTemplate template = new SpringSecurityLdapTemplate(contextSource);
|
||||
|
||||
template.setSearchControls(searchControls);
|
||||
|
||||
try {
|
||||
|
||||
return template.searchForSingleEntry(searchBase, searchFilter, new String[] {username});
|
||||
|
||||
} catch (IncorrectResultSizeDataAccessException notFound) {
|
||||
if (notFound.getActualSize() == 0) {
|
||||
throw new UsernameNotFoundException("User " + username + " not found in directory.", username);
|
||||
}
|
||||
// Search should never return multiple results if properly configured, so just rethrow
|
||||
throw notFound;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the corresponding property on the {@link SearchControls} instance used in the search.
|
||||
*
|
||||
* @param deref the derefLinkFlag value as defined in SearchControls..
|
||||
*/
|
||||
public void setDerefLinkFlag(boolean deref) {
|
||||
searchControls.setDerefLinkFlag(deref);
|
||||
}
|
||||
|
||||
/**
|
||||
* If true then searches the entire subtree as identified by context, if false (the default) then only
|
||||
* searches the level identified by the context.
|
||||
*
|
||||
* @param searchSubtree true the underlying search controls should be set to SearchControls.SUBTREE_SCOPE
|
||||
* rather than SearchControls.ONELEVEL_SCOPE.
|
||||
*/
|
||||
public void setSearchSubtree(boolean searchSubtree) {
|
||||
searchControls.setSearchScope(searchSubtree ? SearchControls.SUBTREE_SCOPE : SearchControls.ONELEVEL_SCOPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* The time to wait before the search fails; the default is zero, meaning forever.
|
||||
*
|
||||
* @param searchTimeLimit the time limit for the search (in milliseconds).
|
||||
*/
|
||||
public void setSearchTimeLimit(int searchTimeLimit) {
|
||||
searchControls.setTimeLimit(searchTimeLimit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the attributes that will be returned as part of the search.
|
||||
*<p>
|
||||
* null indicates that all attributes will be returned.
|
||||
* An empty array indicates no attributes are returned.
|
||||
*
|
||||
* @param attrs An array of attribute names identifying the attributes that
|
||||
* will be returned. Can be null.
|
||||
*/
|
||||
public void setReturningAttributes(String[] attrs) {
|
||||
searchControls.setReturningAttributes(attrs);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
|
||||
sb.append("[ searchFilter: '").append(searchFilter).append("', ");
|
||||
sb.append("searchBase: '").append(searchBase).append("'");
|
||||
sb.append(", scope: ")
|
||||
.append(searchControls.getSearchScope() == SearchControls.SUBTREE_SCOPE ? "subtree" : "single-level, ");
|
||||
sb.append(", searchTimeLimit: ").append(searchControls.getTimeLimit());
|
||||
sb.append(", derefLinkFlag: ").append(searchControls.getDerefLinkFlag()).append(" ]");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
<html>
|
||||
<body>
|
||||
<tt>LdapUserSearch</tt> implementations. These may be used to locate the user in the directory.
|
||||
</body>
|
||||
</html>
|
||||
+1
-3
@@ -13,10 +13,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.providers.ldap.authenticator;
|
||||
package org.springframework.security.providers.encoding;
|
||||
|
||||
import org.springframework.security.providers.encoding.PasswordEncoder;
|
||||
import org.springframework.security.providers.encoding.ShaPasswordEncoder;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
-304
@@ -1,304 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.providers.ldap;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.security.Authentication;
|
||||
import org.springframework.security.AuthenticationException;
|
||||
import org.springframework.security.AuthenticationServiceException;
|
||||
import org.springframework.security.BadCredentialsException;
|
||||
import org.springframework.security.GrantedAuthority;
|
||||
import org.springframework.security.SpringSecurityMessageSource;
|
||||
import org.springframework.security.ldap.LdapAuthoritiesPopulator;
|
||||
import org.springframework.security.ldap.populator.DefaultLdapAuthoritiesPopulator;
|
||||
import org.springframework.security.providers.AuthenticationProvider;
|
||||
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.userdetails.UserDetails;
|
||||
import org.springframework.security.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.userdetails.ldap.LdapUserDetailsMapper;
|
||||
import org.springframework.security.userdetails.ldap.UserDetailsContextMapper;
|
||||
import org.springframework.security.util.AuthorityUtils;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.MessageSourceAware;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.ldap.NamingException;
|
||||
import org.springframework.ldap.core.DirContextOperations;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
|
||||
/**
|
||||
* An {@link org.springframework.security.providers.AuthenticationProvider} implementation that authenticates
|
||||
* against an LDAP server.
|
||||
* <p>
|
||||
* There are many ways in which an LDAP directory can be configured so this class delegates most of
|
||||
* its responsibilites to two separate strategy interfaces, {@link LdapAuthenticator}
|
||||
* and {@link LdapAuthoritiesPopulator}.
|
||||
*
|
||||
* <h3>LdapAuthenticator</h3>
|
||||
* This interface is responsible for performing the user authentication and retrieving
|
||||
* the user's information from the directory. Example implementations are {@link
|
||||
* org.springframework.security.providers.ldap.authenticator.BindAuthenticator BindAuthenticator} which authenticates
|
||||
* the user by "binding" as that user, and
|
||||
* {@link org.springframework.security.providers.ldap.authenticator.PasswordComparisonAuthenticator PasswordComparisonAuthenticator}
|
||||
* which compares the supplied password with the value stored in the directory, using an LDAP "compare"
|
||||
* operation.
|
||||
* <p>
|
||||
* The task of retrieving the user attributes is delegated to the authenticator because the permissions on the
|
||||
* attributes may depend on the type of authentication being used; for example, if binding as the user, it may be
|
||||
* necessary to read them with the user's own permissions (using the same context used for the bind operation).
|
||||
*
|
||||
* <h3>LdapAuthoritiesPopulator</h3>
|
||||
* Once the user has been authenticated, this interface is called to obtain the set of granted authorities for the
|
||||
* user.
|
||||
* The {@link DefaultLdapAuthoritiesPopulator DefaultLdapAuthoritiesPopulator}
|
||||
* can be configured to obtain user role information from the user's attributes and/or to perform a search for
|
||||
* "groups" that the user is a member of and map these to roles.
|
||||
*
|
||||
* <p>
|
||||
* A custom implementation could obtain the roles from a completely different source, for example from a database.
|
||||
*
|
||||
* <h3>Configuration</h3>
|
||||
*
|
||||
* A simple configuration might be as follows:
|
||||
* <pre>
|
||||
* <bean id="contextSource"
|
||||
* class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
|
||||
* <constructor-arg value="ldap://monkeymachine:389/dc=springframework,dc=org"/>
|
||||
* <property name="userDn" value="cn=manager,dc=springframework,dc=org"/>
|
||||
* <property name="password" value="password"/>
|
||||
* </bean>
|
||||
*
|
||||
* <bean id="ldapAuthProvider"
|
||||
* class="org.springframework.security.providers.ldap.LdapAuthenticationProvider">
|
||||
* <constructor-arg>
|
||||
* <bean class="org.springframework.security.providers.ldap.authenticator.BindAuthenticator">
|
||||
* <constructor-arg ref="contextSource"/>
|
||||
* <property name="userDnPatterns"><list><value>uid={0},ou=people</value></list></property>
|
||||
* </bean>
|
||||
* </constructor-arg>
|
||||
* <constructor-arg>
|
||||
* <bean class="org.springframework.security.ldap.populator.DefaultLdapAuthoritiesPopulator">
|
||||
* <constructor-arg ref="contextSource"/>
|
||||
* <constructor-arg value="ou=groups"/>
|
||||
* <property name="groupRoleAttribute" value="ou"/>
|
||||
* </bean>
|
||||
* </constructor-arg>
|
||||
* </bean>
|
||||
*</pre>
|
||||
*
|
||||
* <p>
|
||||
* This would set up the provider to access an LDAP server with URL
|
||||
* <tt>ldap://monkeymachine:389/dc=springframework,dc=org</tt>. Authentication will be performed by attempting to bind
|
||||
* with the DN <tt>uid=<user-login-name>,ou=people,dc=springframework,dc=org</tt>. After successful
|
||||
* authentication, roles will be assigned to the user by searching under the DN
|
||||
* <tt>ou=groups,dc=springframework,dc=org</tt> with the default filter <tt>(member=<user's-DN>)</tt>. The role
|
||||
* name will be taken from the "ou" attribute of each match.
|
||||
* <p>
|
||||
* The authenticate method will reject empty passwords outright. LDAP servers may allow an anonymous
|
||||
* bind operation with an empty password, even if a DN is supplied. In practice this means that if
|
||||
* the LDAP directory is configured to allow unauthenticated access, it might be possible to
|
||||
* authenticate as <i>any</i> user just by supplying an empty password.
|
||||
* More information on the misuse of unauthenticated access can be found in
|
||||
* <a href="http://www.ietf.org/internet-drafts/draft-ietf-ldapbis-authmeth-19.txt">
|
||||
* draft-ietf-ldapbis-authmeth-19.txt</a>.
|
||||
*
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*
|
||||
* @see org.springframework.security.providers.ldap.authenticator.BindAuthenticator
|
||||
* @see DefaultLdapAuthoritiesPopulator
|
||||
*/
|
||||
public class LdapAuthenticationProvider implements AuthenticationProvider, MessageSourceAware {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
|
||||
private static final Log logger = LogFactory.getLog(LdapAuthenticationProvider.class);
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
|
||||
|
||||
private LdapAuthenticator authenticator;
|
||||
private LdapAuthoritiesPopulator authoritiesPopulator;
|
||||
private UserDetailsContextMapper userDetailsContextMapper = new LdapUserDetailsMapper();
|
||||
private boolean useAuthenticationRequestCredentials = true;
|
||||
private boolean hideUserNotFoundExceptions = true;
|
||||
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
/**
|
||||
* Create an instance with the supplied authenticator and authorities populator implementations.
|
||||
*
|
||||
* @param authenticator the authentication strategy (bind, password comparison, etc)
|
||||
* to be used by this provider for authenticating users.
|
||||
* @param authoritiesPopulator the strategy for obtaining the authorities for a given user after they've been
|
||||
* authenticated.
|
||||
*/
|
||||
public LdapAuthenticationProvider(LdapAuthenticator authenticator, LdapAuthoritiesPopulator authoritiesPopulator) {
|
||||
this.setAuthenticator(authenticator);
|
||||
this.setAuthoritiesPopulator(authoritiesPopulator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance with the supplied authenticator and a null authorities populator.
|
||||
* In this case, the authorities must be mapped from the user context.
|
||||
*
|
||||
* @param authenticator the authenticator strategy.
|
||||
*/
|
||||
public LdapAuthenticationProvider(LdapAuthenticator authenticator) {
|
||||
this.setAuthenticator(authenticator);
|
||||
this.setAuthoritiesPopulator(new NullAuthoritiesPopulator());
|
||||
}
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
private void setAuthenticator(LdapAuthenticator authenticator) {
|
||||
Assert.notNull(authenticator, "An LdapAuthenticator must be supplied");
|
||||
this.authenticator = authenticator;
|
||||
}
|
||||
|
||||
private LdapAuthenticator getAuthenticator() {
|
||||
return authenticator;
|
||||
}
|
||||
|
||||
private void setAuthoritiesPopulator(LdapAuthoritiesPopulator authoritiesPopulator) {
|
||||
Assert.notNull(authoritiesPopulator, "An LdapAuthoritiesPopulator must be supplied");
|
||||
this.authoritiesPopulator = authoritiesPopulator;
|
||||
}
|
||||
|
||||
protected LdapAuthoritiesPopulator getAuthoritiesPopulator() {
|
||||
return authoritiesPopulator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows a custom strategy to be used for creating the <tt>UserDetails</tt> which will be stored as the principal
|
||||
* in the <tt>Authentication</tt> returned by the
|
||||
* {@link #createSuccessfulAuthentication(UsernamePasswordAuthenticationToken, UserDetails)} method.
|
||||
*
|
||||
* @param userDetailsContextMapper the strategy instance. If not set, defaults to a simple
|
||||
* <tt>LdapUserDetailsMapper</tt>.
|
||||
*/
|
||||
public void setUserDetailsContextMapper(UserDetailsContextMapper userDetailsContextMapper) {
|
||||
Assert.notNull(userDetailsContextMapper, "UserDetailsContextMapper must not be null");
|
||||
this.userDetailsContextMapper = userDetailsContextMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides access to the injected <tt>UserDetailsContextMapper</tt> strategy for use by subclasses.
|
||||
*/
|
||||
protected UserDetailsContextMapper getUserDetailsContextMapper() {
|
||||
return userDetailsContextMapper;
|
||||
}
|
||||
|
||||
public void setHideUserNotFoundExceptions(boolean hideUserNotFoundExceptions) {
|
||||
this.hideUserNotFoundExceptions = hideUserNotFoundExceptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the supplied password will be used as the credentials in the successful authentication
|
||||
* token. If set to false, then the password will be obtained from the UserDetails object
|
||||
* created by the configured <tt>UserDetailsContextMapper</tt>.
|
||||
* Often it will not be possible to read the password from the directory, so defaults to true.
|
||||
*
|
||||
* @param useAuthenticationRequestCredentials
|
||||
*/
|
||||
public void setUseAuthenticationRequestCredentials(boolean useAuthenticationRequestCredentials) {
|
||||
this.useAuthenticationRequestCredentials = useAuthenticationRequestCredentials;
|
||||
}
|
||||
|
||||
public void setMessageSource(MessageSource messageSource) {
|
||||
this.messages = new MessageSourceAccessor(messageSource);
|
||||
}
|
||||
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
|
||||
messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports",
|
||||
"Only UsernamePasswordAuthenticationToken is supported"));
|
||||
|
||||
UsernamePasswordAuthenticationToken userToken = (UsernamePasswordAuthenticationToken)authentication;
|
||||
|
||||
String username = userToken.getName();
|
||||
|
||||
if (!StringUtils.hasLength(username)) {
|
||||
throw new BadCredentialsException(messages.getMessage("LdapAuthenticationProvider.emptyUsername",
|
||||
"Empty Username"));
|
||||
}
|
||||
|
||||
String password = (String) authentication.getCredentials();
|
||||
Assert.notNull(password, "Null password was supplied in authentication token");
|
||||
|
||||
if (password.length() == 0) {
|
||||
logger.debug("Rejecting empty password for user " + username);
|
||||
throw new BadCredentialsException(messages.getMessage("LdapAuthenticationProvider.emptyPassword",
|
||||
"Empty Password"));
|
||||
}
|
||||
|
||||
try {
|
||||
DirContextOperations userData = getAuthenticator().authenticate(authentication);
|
||||
|
||||
List<GrantedAuthority> extraAuthorities = loadUserAuthorities(userData, username, password);
|
||||
|
||||
UserDetails user = userDetailsContextMapper.mapUserFromContext(userData, username, extraAuthorities);
|
||||
|
||||
return createSuccessfulAuthentication(userToken, user);
|
||||
} catch (UsernameNotFoundException notFound) {
|
||||
if (hideUserNotFoundExceptions) {
|
||||
throw new BadCredentialsException(messages.getMessage(
|
||||
"LdapAuthenticationProvider.badCredentials", "Bad credentials"));
|
||||
} else {
|
||||
throw notFound;
|
||||
}
|
||||
} catch (NamingException ldapAccessFailure) {
|
||||
throw new AuthenticationServiceException(ldapAccessFailure.getMessage(), ldapAccessFailure);
|
||||
}
|
||||
}
|
||||
|
||||
protected List<GrantedAuthority> loadUserAuthorities(DirContextOperations userData, String username, String password) {
|
||||
return getAuthoritiesPopulator().getGrantedAuthorities(userData, username);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the final <tt>Authentication</tt> object which will be returned from the <tt>authenticate</tt> method.
|
||||
*
|
||||
* @param authentication the original authentication request token
|
||||
* @param user the <tt>UserDetails</tt> instance returned by the configured <tt>UserDetailsContextMapper</tt>.
|
||||
* @return the Authentication object for the fully authenticated user.
|
||||
*/
|
||||
protected Authentication createSuccessfulAuthentication(UsernamePasswordAuthenticationToken authentication,
|
||||
UserDetails user) {
|
||||
Object password = useAuthenticationRequestCredentials ? authentication.getCredentials() : user.getPassword();
|
||||
|
||||
return new UsernamePasswordAuthenticationToken(user, password, user.getAuthorities());
|
||||
}
|
||||
|
||||
public boolean supports(Class<? extends Object> authentication) {
|
||||
return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
|
||||
}
|
||||
|
||||
//~ Inner Classes ==================================================================================================
|
||||
|
||||
private static class NullAuthoritiesPopulator implements LdapAuthoritiesPopulator {
|
||||
public List<GrantedAuthority> getGrantedAuthorities(DirContextOperations userDetails, String username) {
|
||||
return AuthorityUtils.NO_AUTHORITIES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.providers.ldap;
|
||||
|
||||
import org.springframework.security.Authentication;
|
||||
import org.springframework.ldap.core.DirContextOperations;
|
||||
|
||||
|
||||
/**
|
||||
* The strategy interface for locating and authenticating an Ldap user.
|
||||
* <p>
|
||||
* The LdapAuthenticationProvider calls this interface to authenticate a user
|
||||
* and obtain the information for that user from the directory.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*
|
||||
* @see org.springframework.security.ldap.populator.DefaultLdapAuthoritiesPopulator
|
||||
* @see org.springframework.security.ldap.populator.UserDetailsServiceLdapAuthoritiesPopulator
|
||||
*/
|
||||
public interface LdapAuthenticator {
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
/**
|
||||
* Authenticates as a user and obtains additional user information from the directory.
|
||||
*
|
||||
* @param authentication
|
||||
* @return the details of the successfully authenticated user.
|
||||
*/
|
||||
DirContextOperations authenticate(Authentication authentication);
|
||||
}
|
||||
-146
@@ -1,146 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.providers.ldap.authenticator;
|
||||
|
||||
import org.springframework.security.SpringSecurityMessageSource;
|
||||
import org.springframework.security.ldap.LdapUserSearch;
|
||||
import org.springframework.security.providers.ldap.LdapAuthenticator;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.MessageSourceAware;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.ldap.core.ContextSource;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Base class for the authenticator implementations.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public abstract class AbstractLdapAuthenticator implements LdapAuthenticator, InitializingBean, MessageSourceAware {
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private ContextSource contextSource;
|
||||
|
||||
/** Optional search object which can be used to locate a user when a simple DN match isn't sufficient */
|
||||
private LdapUserSearch userSearch;
|
||||
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
|
||||
|
||||
/** The attributes which will be retrieved from the directory. Null means all attributes */
|
||||
private String[] userAttributes = null;
|
||||
|
||||
//private String[] userDnPattern = null;
|
||||
/** Stores the patterns which are used as potential DN matches */
|
||||
private MessageFormat[] userDnFormat = null;
|
||||
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
/**
|
||||
* Create an initialized instance with the {@link ContextSource} provided.
|
||||
*
|
||||
* @param contextSource
|
||||
*/
|
||||
public AbstractLdapAuthenticator(ContextSource contextSource) {
|
||||
Assert.notNull(contextSource, "contextSource must not be null.");
|
||||
this.contextSource = contextSource;
|
||||
}
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.isTrue((userDnFormat != null) || (userSearch != null),
|
||||
"Either an LdapUserSearch or DN pattern (or both) must be supplied.");
|
||||
}
|
||||
|
||||
protected ContextSource getContextSource() {
|
||||
return contextSource;
|
||||
}
|
||||
|
||||
public String[] getUserAttributes() {
|
||||
return userAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds list of possible DNs for the user, worked out from the <tt>userDnPatterns</tt> property.
|
||||
*
|
||||
* @param username the user's login name
|
||||
*
|
||||
* @return the list of possible DN matches, empty if <tt>userDnPatterns</tt> wasn't set.
|
||||
*/
|
||||
protected List<String> getUserDns(String username) {
|
||||
if (userDnFormat == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<String> userDns = new ArrayList<String>(userDnFormat.length);
|
||||
String[] args = new String[] {username};
|
||||
|
||||
synchronized (userDnFormat) {
|
||||
for (int i = 0; i < userDnFormat.length; i++) {
|
||||
userDns.add(userDnFormat[i].format(args));
|
||||
}
|
||||
}
|
||||
|
||||
return userDns;
|
||||
}
|
||||
|
||||
protected LdapUserSearch getUserSearch() {
|
||||
return userSearch;
|
||||
}
|
||||
|
||||
public void setMessageSource(MessageSource messageSource) {
|
||||
Assert.notNull("Message source must not be null");
|
||||
this.messages = new MessageSourceAccessor(messageSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the user attributes which will be retrieved from the directory.
|
||||
*
|
||||
* @param userAttributes
|
||||
*/
|
||||
public void setUserAttributes(String[] userAttributes) {
|
||||
Assert.notNull(userAttributes, "The userAttributes property cannot be set to null");
|
||||
this.userAttributes = userAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the pattern which will be used to supply a DN for the user. The pattern should be the name relative
|
||||
* to the root DN. The pattern argument {0} will contain the username. An example would be "cn={0},ou=people".
|
||||
*
|
||||
* @param dnPattern the array of patterns which will be tried when converting a username to a DN.
|
||||
*/
|
||||
public void setUserDnPatterns(String[] dnPattern) {
|
||||
Assert.notNull(dnPattern, "The array of DN patterns cannot be set to null");
|
||||
// this.userDnPattern = dnPattern;
|
||||
userDnFormat = new MessageFormat[dnPattern.length];
|
||||
|
||||
for (int i = 0; i < dnPattern.length; i++) {
|
||||
userDnFormat[i] = new MessageFormat(dnPattern[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void setUserSearch(LdapUserSearch userSearch) {
|
||||
Assert.notNull(userSearch, "The userSearch cannot be set to null");
|
||||
this.userSearch = userSearch;
|
||||
}
|
||||
}
|
||||
-128
@@ -1,128 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.providers.ldap.authenticator;
|
||||
|
||||
import javax.naming.directory.Attributes;
|
||||
import javax.naming.directory.DirContext;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.ldap.NamingException;
|
||||
import org.springframework.ldap.core.DirContextAdapter;
|
||||
import org.springframework.ldap.core.DirContextOperations;
|
||||
import org.springframework.ldap.core.DistinguishedName;
|
||||
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
|
||||
import org.springframework.ldap.support.LdapUtils;
|
||||
import org.springframework.security.Authentication;
|
||||
import org.springframework.security.BadCredentialsException;
|
||||
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* An authenticator which binds as a user.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*
|
||||
* @see AbstractLdapAuthenticator
|
||||
*/
|
||||
public class BindAuthenticator extends AbstractLdapAuthenticator {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
|
||||
private static final Log logger = LogFactory.getLog(BindAuthenticator.class);
|
||||
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
/**
|
||||
* Create an initialized instance using the {@link BaseLdapPathContextSource} provided.
|
||||
*
|
||||
* @param contextSource the BaseLdapPathContextSource instance against which bind operations will be
|
||||
* performed.
|
||||
*
|
||||
*/
|
||||
public BindAuthenticator(BaseLdapPathContextSource contextSource) {
|
||||
super(contextSource);
|
||||
}
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public DirContextOperations authenticate(Authentication authentication) {
|
||||
DirContextOperations user = null;
|
||||
Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
|
||||
"Can only process UsernamePasswordAuthenticationToken objects");
|
||||
|
||||
String username = authentication.getName();
|
||||
String password = (String)authentication.getCredentials();
|
||||
|
||||
// If DN patterns are configured, try authenticating with them directly
|
||||
for (String dn : getUserDns(username)) {
|
||||
user = bindWithDn(dn, username, password);
|
||||
}
|
||||
|
||||
// Otherwise use the configured search object to find the user and authenticate with the returned DN.
|
||||
if (user == null && getUserSearch() != null) {
|
||||
DirContextOperations userFromSearch = getUserSearch().searchForUser(username);
|
||||
user = bindWithDn(userFromSearch.getDn().toString(), username, password);
|
||||
}
|
||||
|
||||
if (user == null) {
|
||||
throw new BadCredentialsException(
|
||||
messages.getMessage("BindAuthenticator.badCredentials", "Bad credentials"));
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
private DirContextOperations bindWithDn(String userDn, String username, String password) {
|
||||
BaseLdapPathContextSource ctxSource = (BaseLdapPathContextSource) getContextSource();
|
||||
DistinguishedName fullDn = new DistinguishedName(userDn);
|
||||
fullDn.prepend(ctxSource.getBaseLdapPath());
|
||||
|
||||
logger.debug("Attempting to bind as " + fullDn);
|
||||
|
||||
try {
|
||||
DirContext ctx = getContextSource().getContext(fullDn.toString(), password);
|
||||
Attributes attrs = ctx.getAttributes(userDn, getUserAttributes());
|
||||
|
||||
return new DirContextAdapter(attrs, new DistinguishedName(userDn), ctxSource.getBaseLdapPath());
|
||||
} catch (NamingException e) {
|
||||
// This will be thrown if an invalid user name is used and the method may
|
||||
// be called multiple times to try different names, so we trap the exception
|
||||
// unless a subclass wishes to implement more specialized behaviour.
|
||||
if ((e instanceof org.springframework.ldap.AuthenticationException)
|
||||
|| (e instanceof org.springframework.ldap.OperationNotSupportedException)) {
|
||||
handleBindException(userDn, username, e);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
} catch (javax.naming.NamingException e) {
|
||||
throw LdapUtils.convertLdapException(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows subclasses to inspect the exception thrown by an attempt to bind with a particular DN.
|
||||
* The default implementation just reports the failure to the debug log.
|
||||
*/
|
||||
protected void handleBindException(String userDn, String username, Throwable cause) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Failed to bind as " + userDn + ": " + cause);
|
||||
}
|
||||
}
|
||||
}
|
||||
-116
@@ -1,116 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.providers.ldap.authenticator;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.ldap.NameNotFoundException;
|
||||
import org.springframework.ldap.core.DirContextOperations;
|
||||
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
|
||||
import org.springframework.security.Authentication;
|
||||
import org.springframework.security.BadCredentialsException;
|
||||
import org.springframework.security.ldap.LdapUtils;
|
||||
import org.springframework.security.ldap.SpringSecurityLdapTemplate;
|
||||
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.providers.encoding.PasswordEncoder;
|
||||
import org.springframework.security.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* An {@link org.springframework.security.providers.ldap.LdapAuthenticator LdapAuthenticator} which compares the login
|
||||
* password with the value stored in the directory using a remote LDAP "compare" operation.
|
||||
*
|
||||
* <p>
|
||||
* If passwords are stored in digest form in the repository, then a suitable {@link PasswordEncoder}
|
||||
* implementation must be supplied. By default, passwords are encoded using the {@link LdapShaPasswordEncoder}.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public final class PasswordComparisonAuthenticator extends AbstractLdapAuthenticator {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
|
||||
private static final Log logger = LogFactory.getLog(PasswordComparisonAuthenticator.class);
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private PasswordEncoder passwordEncoder = new LdapShaPasswordEncoder();
|
||||
private String passwordAttributeName = "userPassword";
|
||||
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
public PasswordComparisonAuthenticator(BaseLdapPathContextSource contextSource) {
|
||||
super(contextSource);
|
||||
}
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public DirContextOperations authenticate(final Authentication authentication) {
|
||||
Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
|
||||
"Can only process UsernamePasswordAuthenticationToken objects");
|
||||
// locate the user and check the password
|
||||
|
||||
DirContextOperations user = null;
|
||||
String username = authentication.getName();
|
||||
String password = (String)authentication.getCredentials();
|
||||
|
||||
SpringSecurityLdapTemplate ldapTemplate = new SpringSecurityLdapTemplate(getContextSource());
|
||||
|
||||
for (String userDn : getUserDns(username)) {
|
||||
try {
|
||||
user = ldapTemplate.retrieveEntry(userDn, getUserAttributes());
|
||||
} catch (NameNotFoundException ignore) {
|
||||
}
|
||||
if (user != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (user == null && getUserSearch() != null) {
|
||||
user = getUserSearch().searchForUser(username);
|
||||
}
|
||||
|
||||
if (user == null) {
|
||||
throw new UsernameNotFoundException("User not found: " + username, username);
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Performing LDAP compare of password attribute '" + passwordAttributeName + "' for user '" +
|
||||
user.getDn() +"'");
|
||||
}
|
||||
|
||||
String encodedPassword = passwordEncoder.encodePassword(password, null);
|
||||
byte[] passwordBytes = LdapUtils.getUtf8Bytes(encodedPassword);
|
||||
|
||||
if (!ldapTemplate.compare(user.getDn().toString(), passwordAttributeName, passwordBytes)) {
|
||||
throw new BadCredentialsException(messages.getMessage("PasswordComparisonAuthenticator.badCredentials",
|
||||
"Bad credentials"));
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setPasswordAttributeName(String passwordAttribute) {
|
||||
Assert.hasLength(passwordAttribute, "passwordAttributeName must not be empty or null");
|
||||
this.passwordAttributeName = passwordAttribute;
|
||||
}
|
||||
|
||||
public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
|
||||
Assert.notNull(passwordEncoder, "passwordEncoder must not be null.");
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
}
|
||||
}
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
<html>
|
||||
<body>
|
||||
LDAP authenticator implementations.
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,15 +0,0 @@
|
||||
<html>
|
||||
<body>
|
||||
<p>
|
||||
The LDAP authentication provider package. Interfaces are provided for
|
||||
both authentication and retrieval of user roles from an LDAP server.
|
||||
</p>
|
||||
<p>
|
||||
The main provider class is <tt>LdapAuthenticationProvider</tt>.
|
||||
This is configured with an <tt>LdapAuthenticator</tt> instance and
|
||||
an <tt>LdapAuthoritiesPopulator</tt>. The latter is used to obtain the
|
||||
list of roles for the user.
|
||||
</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,277 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.userdetails.ldap;
|
||||
|
||||
import org.springframework.ldap.core.DirContextAdapter;
|
||||
import org.springframework.ldap.core.DirContextOperations;
|
||||
|
||||
|
||||
/**
|
||||
* UserDetails implementation whose properties are based on a subset of the
|
||||
* LDAP schema for <tt>inetOrgPerson</tt>.
|
||||
*
|
||||
* <p>
|
||||
* The username will be mapped from the <tt>uid</tt> attribute by default.
|
||||
*
|
||||
* @author Luke
|
||||
* @version $Id$
|
||||
*/
|
||||
public class InetOrgPerson extends Person {
|
||||
private String carLicense;
|
||||
// Person.cn
|
||||
private String destinationIndicator;
|
||||
private String departmentNumber;
|
||||
// Person.description
|
||||
private String displayName;
|
||||
private String employeeNumber;
|
||||
private String homePhone;
|
||||
private String homePostalAddress;
|
||||
private String initials;
|
||||
private String mail;
|
||||
private String mobile;
|
||||
private String o;
|
||||
private String ou;
|
||||
private String postalAddress;
|
||||
private String postalCode;
|
||||
private String roomNumber;
|
||||
private String street;
|
||||
// Person.sn
|
||||
// Person.telephoneNumber
|
||||
private String title;
|
||||
private String uid;
|
||||
|
||||
public String getUid() {
|
||||
return uid;
|
||||
}
|
||||
|
||||
public String getMail() {
|
||||
return mail;
|
||||
}
|
||||
|
||||
public String getEmployeeNumber() {
|
||||
return employeeNumber;
|
||||
}
|
||||
|
||||
public String getInitials() {
|
||||
return initials;
|
||||
}
|
||||
|
||||
public String getDestinationIndicator() {
|
||||
return destinationIndicator;
|
||||
}
|
||||
|
||||
public String getO() {
|
||||
return o;
|
||||
}
|
||||
|
||||
public String getOu() {
|
||||
return ou;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public String getCarLicense() {
|
||||
return carLicense;
|
||||
}
|
||||
|
||||
public String getDepartmentNumber() {
|
||||
return departmentNumber;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public String getHomePhone() {
|
||||
return homePhone;
|
||||
}
|
||||
|
||||
public String getRoomNumber() {
|
||||
return roomNumber;
|
||||
}
|
||||
|
||||
public String getHomePostalAddress() {
|
||||
return homePostalAddress;
|
||||
}
|
||||
|
||||
public String getMobile() {
|
||||
return mobile;
|
||||
}
|
||||
|
||||
public String getPostalAddress() {
|
||||
return postalAddress;
|
||||
}
|
||||
|
||||
public String getPostalCode() {
|
||||
return postalCode;
|
||||
}
|
||||
|
||||
public String getStreet() {
|
||||
return street;
|
||||
}
|
||||
|
||||
protected void populateContext(DirContextAdapter adapter) {
|
||||
super.populateContext(adapter);
|
||||
adapter.setAttributeValue("carLicense", carLicense);
|
||||
adapter.setAttributeValue("departmentNumber", departmentNumber);
|
||||
adapter.setAttributeValue("destinationIndicator", destinationIndicator);
|
||||
adapter.setAttributeValue("displayName", displayName);
|
||||
adapter.setAttributeValue("employeeNumber", employeeNumber);
|
||||
adapter.setAttributeValue("homePhone", homePhone);
|
||||
adapter.setAttributeValue("homePostalAddress", homePostalAddress);
|
||||
adapter.setAttributeValue("initials", initials);
|
||||
adapter.setAttributeValue("mail", mail);
|
||||
adapter.setAttributeValue("mobile", mobile);
|
||||
adapter.setAttributeValue("postalAddress", postalAddress);
|
||||
adapter.setAttributeValue("postalCode", postalCode);
|
||||
adapter.setAttributeValue("ou", ou);
|
||||
adapter.setAttributeValue("o", o);
|
||||
adapter.setAttributeValue("roomNumber", roomNumber);
|
||||
adapter.setAttributeValue("street", street);
|
||||
adapter.setAttributeValue("uid", uid);
|
||||
adapter.setAttributeValues("objectclass", new String[] {"top", "person", "organizationalPerson", "inetOrgPerson"});
|
||||
}
|
||||
|
||||
public static class Essence extends Person.Essence {
|
||||
public Essence() {
|
||||
}
|
||||
|
||||
public Essence(InetOrgPerson copyMe) {
|
||||
super(copyMe);
|
||||
setCarLicense(copyMe.getCarLicense());
|
||||
setDepartmentNumber(copyMe.getDepartmentNumber());
|
||||
setDestinationIndicator(copyMe.getDestinationIndicator());
|
||||
setDisplayName(copyMe.getDisplayName());
|
||||
setEmployeeNumber(copyMe.getEmployeeNumber());
|
||||
setHomePhone(copyMe.getHomePhone());
|
||||
setHomePostalAddress(copyMe.getHomePostalAddress());
|
||||
setInitials(copyMe.getInitials());
|
||||
setMail(copyMe.getMail());
|
||||
setMobile(copyMe.getMobile());
|
||||
setO(copyMe.getO());
|
||||
setOu(copyMe.getOu());
|
||||
setPostalAddress(copyMe.getPostalAddress());
|
||||
setPostalCode(copyMe.getPostalCode());
|
||||
setRoomNumber(copyMe.getRoomNumber());
|
||||
setStreet(copyMe.getStreet());
|
||||
setTitle(copyMe.getTitle());
|
||||
setUid(copyMe.getUid());
|
||||
}
|
||||
|
||||
public Essence(DirContextOperations ctx) {
|
||||
super(ctx);
|
||||
setCarLicense(ctx.getStringAttribute("carLicense"));
|
||||
setDepartmentNumber(ctx.getStringAttribute("departmentNumber"));
|
||||
setDestinationIndicator(ctx.getStringAttribute("destinationIndicator"));
|
||||
setDisplayName(ctx.getStringAttribute("displayName"));
|
||||
setEmployeeNumber(ctx.getStringAttribute("employeeNumber"));
|
||||
setHomePhone(ctx.getStringAttribute("homePhone"));
|
||||
setHomePostalAddress(ctx.getStringAttribute("homePostalAddress"));
|
||||
setInitials(ctx.getStringAttribute("initials"));
|
||||
setMail(ctx.getStringAttribute("mail"));
|
||||
setMobile(ctx.getStringAttribute("mobile"));
|
||||
setO(ctx.getStringAttribute("o"));
|
||||
setOu(ctx.getStringAttribute("ou"));
|
||||
setPostalAddress(ctx.getStringAttribute("postalAddress"));
|
||||
setPostalCode(ctx.getStringAttribute("postalCode"));
|
||||
setRoomNumber(ctx.getStringAttribute("roomNumber"));
|
||||
setStreet(ctx.getStringAttribute("street"));
|
||||
setTitle(ctx.getStringAttribute("title"));
|
||||
setUid(ctx.getStringAttribute("uid"));
|
||||
}
|
||||
|
||||
protected LdapUserDetailsImpl createTarget() {
|
||||
return new InetOrgPerson();
|
||||
}
|
||||
|
||||
public void setMail(String email) {
|
||||
((InetOrgPerson) instance).mail = email;
|
||||
}
|
||||
|
||||
public void setUid(String uid) {
|
||||
((InetOrgPerson) instance).uid = uid;
|
||||
|
||||
if(instance.getUsername() == null) {
|
||||
setUsername(uid);
|
||||
}
|
||||
}
|
||||
|
||||
public void setInitials(String initials) {
|
||||
((InetOrgPerson) instance).initials = initials;
|
||||
}
|
||||
|
||||
public void setO(String organization) {
|
||||
((InetOrgPerson) instance).o = organization;
|
||||
}
|
||||
|
||||
public void setOu(String ou) {
|
||||
((InetOrgPerson) instance).ou = ou;
|
||||
}
|
||||
|
||||
public void setRoomNumber(String no) {
|
||||
((InetOrgPerson) instance).roomNumber = no;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
((InetOrgPerson) instance).title = title;
|
||||
}
|
||||
|
||||
public void setCarLicense(String carLicense) {
|
||||
((InetOrgPerson) instance).carLicense = carLicense;
|
||||
}
|
||||
|
||||
public void setDepartmentNumber(String departmentNumber) {
|
||||
((InetOrgPerson) instance).departmentNumber = departmentNumber;
|
||||
}
|
||||
|
||||
public void setDisplayName(String displayName) {
|
||||
((InetOrgPerson) instance).displayName = displayName;
|
||||
}
|
||||
|
||||
public void setEmployeeNumber(String no) {
|
||||
((InetOrgPerson) instance).employeeNumber = no;
|
||||
}
|
||||
|
||||
public void setDestinationIndicator(String destination) {
|
||||
((InetOrgPerson) instance).destinationIndicator = destination;
|
||||
}
|
||||
|
||||
public void setHomePhone(String homePhone) {
|
||||
((InetOrgPerson) instance).homePhone = homePhone;
|
||||
}
|
||||
|
||||
public void setStreet(String street) {
|
||||
((InetOrgPerson) instance).street = street;
|
||||
}
|
||||
|
||||
public void setPostalCode(String postalCode) {
|
||||
((InetOrgPerson) instance).postalCode = postalCode;
|
||||
}
|
||||
|
||||
public void setPostalAddress(String postalAddress) {
|
||||
((InetOrgPerson) instance).postalAddress = postalAddress;
|
||||
}
|
||||
|
||||
public void setMobile(String mobile) {
|
||||
((InetOrgPerson) instance).mobile = mobile;
|
||||
}
|
||||
|
||||
public void setHomePostalAddress(String homePostalAddress) {
|
||||
((InetOrgPerson) instance).homePostalAddress = homePostalAddress;
|
||||
}
|
||||
}
|
||||
}
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.userdetails.ldap;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.security.userdetails.UserDetails;
|
||||
import org.springframework.security.GrantedAuthority;
|
||||
import org.springframework.ldap.core.DirContextOperations;
|
||||
import org.springframework.ldap.core.DirContextAdapter;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public class InetOrgPersonContextMapper implements UserDetailsContextMapper {
|
||||
|
||||
public UserDetails mapUserFromContext(DirContextOperations ctx, String username, List<GrantedAuthority> authorities) {
|
||||
InetOrgPerson.Essence p = new InetOrgPerson.Essence(ctx);
|
||||
|
||||
p.setUsername(username);
|
||||
p.setAuthorities(authorities);
|
||||
|
||||
return p.createUserDetails();
|
||||
|
||||
}
|
||||
|
||||
public void mapUserToContext(UserDetails user, DirContextAdapter ctx) {
|
||||
Assert.isInstanceOf(InetOrgPerson.class, user, "UserDetails must be an InetOrgPerson instance");
|
||||
|
||||
InetOrgPerson p = (InetOrgPerson) user;
|
||||
p.populateContext(ctx);
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.userdetails.ldap;
|
||||
|
||||
import org.springframework.security.userdetails.UserDetails;
|
||||
|
||||
/**
|
||||
* Captures the information for a user's LDAP entry.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface LdapUserDetails extends UserDetails {
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
/**
|
||||
* The DN of the entry for this user's account.
|
||||
*
|
||||
* @return the user's DN
|
||||
*/
|
||||
String getDn();
|
||||
}
|
||||
-221
@@ -1,221 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.userdetails.ldap;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.naming.Name;
|
||||
|
||||
import org.springframework.ldap.core.DirContextOperations;
|
||||
import org.springframework.security.GrantedAuthority;
|
||||
import org.springframework.security.util.AuthorityUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* A UserDetails implementation which is used internally by the Ldap services. It also contains the user's
|
||||
* distinguished name and a set of attributes that have been retrieved from the Ldap server.
|
||||
* <p>
|
||||
* An instance may be created as the result of a search, or when user information is retrieved during authentication.
|
||||
* </p>
|
||||
* <p>
|
||||
* An instance of this class will be used by the <tt>LdapAuthenticationProvider</tt> to construct the final user details
|
||||
* object that it returns.
|
||||
* </p>
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public class LdapUserDetailsImpl implements LdapUserDetails {
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private String dn;
|
||||
private String password;
|
||||
private String username;
|
||||
private List<GrantedAuthority> authorities = AuthorityUtils.NO_AUTHORITIES;
|
||||
private boolean accountNonExpired = true;
|
||||
private boolean accountNonLocked = true;
|
||||
private boolean credentialsNonExpired = true;
|
||||
private boolean enabled = true;
|
||||
|
||||
//~ Constructors ===================================================================================================
|
||||
|
||||
protected LdapUserDetailsImpl() {}
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public List<GrantedAuthority> getAuthorities() {
|
||||
return authorities;
|
||||
}
|
||||
|
||||
public String getDn() {
|
||||
return dn;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public boolean isAccountNonExpired() {
|
||||
return accountNonExpired;
|
||||
}
|
||||
|
||||
public boolean isAccountNonLocked() {
|
||||
return accountNonLocked;
|
||||
}
|
||||
|
||||
public boolean isCredentialsNonExpired() {
|
||||
return credentialsNonExpired;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append(super.toString()).append(": ");
|
||||
sb.append("Username: ").append(this.username).append("; ");
|
||||
sb.append("Password: [PROTECTED]; ");
|
||||
sb.append("Enabled: ").append(this.enabled).append("; ");
|
||||
sb.append("AccountNonExpired: ").append(this.accountNonExpired).append("; ");
|
||||
sb.append("credentialsNonExpired: ").append(this.credentialsNonExpired).append("; ");
|
||||
sb.append("AccountNonLocked: ").append(this.accountNonLocked).append("; ");
|
||||
|
||||
if (this.getAuthorities() != null) {
|
||||
sb.append("Granted Authorities: ");
|
||||
|
||||
for (int i = 0; i < this.getAuthorities().size(); i++) {
|
||||
if (i > 0) {
|
||||
sb.append(", ");
|
||||
}
|
||||
|
||||
sb.append(this.getAuthorities().get(i).toString());
|
||||
}
|
||||
} else {
|
||||
sb.append("Not granted any authorities");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
//~ Inner Classes ==================================================================================================
|
||||
|
||||
/**
|
||||
* Variation of essence pattern. Used to create mutable intermediate object
|
||||
*/
|
||||
public static class Essence {
|
||||
protected LdapUserDetailsImpl instance = createTarget();
|
||||
private List<GrantedAuthority> mutableAuthorities = new ArrayList<GrantedAuthority>();
|
||||
|
||||
public Essence() { }
|
||||
|
||||
public Essence(DirContextOperations ctx) {
|
||||
setDn(ctx.getDn());
|
||||
}
|
||||
|
||||
public Essence(LdapUserDetails copyMe) {
|
||||
setDn(copyMe.getDn());
|
||||
setUsername(copyMe.getUsername());
|
||||
setPassword(copyMe.getPassword());
|
||||
setEnabled(copyMe.isEnabled());
|
||||
setAccountNonExpired(copyMe.isAccountNonExpired());
|
||||
setCredentialsNonExpired(copyMe.isCredentialsNonExpired());
|
||||
setAccountNonLocked(copyMe.isAccountNonLocked());
|
||||
setAuthorities(copyMe.getAuthorities());
|
||||
}
|
||||
|
||||
protected LdapUserDetailsImpl createTarget() {
|
||||
return new LdapUserDetailsImpl();
|
||||
}
|
||||
|
||||
/** Adds the authority to the list, unless it is already there, in which case it is ignored */
|
||||
public void addAuthority(GrantedAuthority a) {
|
||||
if(!hasAuthority(a)) {
|
||||
mutableAuthorities.add(a);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasAuthority(GrantedAuthority a) {
|
||||
for (GrantedAuthority authority : mutableAuthorities) {
|
||||
if(authority.equals(a)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public LdapUserDetails createUserDetails() {
|
||||
Assert.notNull(instance, "Essence can only be used to create a single instance");
|
||||
Assert.notNull(instance.username, "username must not be null");
|
||||
Assert.notNull(instance.getDn(), "Distinguished name must not be null");
|
||||
|
||||
instance.authorities = getGrantedAuthorities();
|
||||
|
||||
LdapUserDetails newInstance = instance;
|
||||
|
||||
instance = null;
|
||||
|
||||
return newInstance;
|
||||
}
|
||||
|
||||
public List<GrantedAuthority> getGrantedAuthorities() {
|
||||
return mutableAuthorities;
|
||||
}
|
||||
|
||||
public void setAccountNonExpired(boolean accountNonExpired) {
|
||||
instance.accountNonExpired = accountNonExpired;
|
||||
}
|
||||
|
||||
public void setAccountNonLocked(boolean accountNonLocked) {
|
||||
instance.accountNonLocked = accountNonLocked;
|
||||
}
|
||||
|
||||
public void setAuthorities(List<GrantedAuthority> authorities) {
|
||||
mutableAuthorities = authorities;
|
||||
}
|
||||
|
||||
public void setCredentialsNonExpired(boolean credentialsNonExpired) {
|
||||
instance.credentialsNonExpired = credentialsNonExpired;
|
||||
}
|
||||
|
||||
public void setDn(String dn) {
|
||||
instance.dn = dn;
|
||||
}
|
||||
|
||||
public void setDn(Name dn) {
|
||||
instance.dn = dn.toString();
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
instance.enabled = enabled;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
instance.password = password;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
instance.username = username;
|
||||
}
|
||||
}
|
||||
}
|
||||
-395
@@ -1,395 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.userdetails.ldap;
|
||||
|
||||
import org.springframework.security.Authentication;
|
||||
import org.springframework.security.BadCredentialsException;
|
||||
import org.springframework.security.GrantedAuthority;
|
||||
import org.springframework.security.GrantedAuthorityImpl;
|
||||
import org.springframework.security.context.SecurityContextHolder;
|
||||
import org.springframework.security.ldap.LdapUsernameToDnMapper;
|
||||
import org.springframework.security.ldap.LdapUtils;
|
||||
import org.springframework.security.ldap.DefaultLdapUsernameToDnMapper;
|
||||
import org.springframework.security.userdetails.UserDetails;
|
||||
import org.springframework.security.userdetails.UserDetailsManager;
|
||||
import org.springframework.security.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.ldap.core.AttributesMapper;
|
||||
import org.springframework.ldap.core.AttributesMapperCallbackHandler;
|
||||
import org.springframework.ldap.core.ContextExecutor;
|
||||
import org.springframework.ldap.core.ContextSource;
|
||||
import org.springframework.ldap.core.DirContextAdapter;
|
||||
import org.springframework.ldap.core.DistinguishedName;
|
||||
import org.springframework.ldap.core.LdapTemplate;
|
||||
import org.springframework.ldap.core.SearchExecutor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import javax.naming.Context;
|
||||
import javax.naming.NameNotFoundException;
|
||||
import javax.naming.NamingEnumeration;
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.directory.Attribute;
|
||||
import javax.naming.directory.Attributes;
|
||||
import javax.naming.directory.BasicAttribute;
|
||||
import javax.naming.directory.DirContext;
|
||||
import javax.naming.directory.ModificationItem;
|
||||
import javax.naming.directory.SearchControls;
|
||||
import javax.naming.directory.SearchResult;
|
||||
import javax.naming.ldap.LdapContext;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
|
||||
/**
|
||||
* An Ldap implementation of UserDetailsManager.
|
||||
* <p>
|
||||
* It is designed around a standard setup where users and groups/roles are stored under separate contexts,
|
||||
* defined by the "userDnBase" and "groupSearchBase" properties respectively.
|
||||
* <p>
|
||||
* In this case, LDAP is being used purely to retrieve information and this class can be used in place of any other
|
||||
* UserDetailsService for authentication. Authentication isn't performed directly against the directory, unlike with the
|
||||
* LDAP authentication provider setup.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @since 2.0
|
||||
* @version $Id$
|
||||
*/
|
||||
public class LdapUserDetailsManager implements UserDetailsManager {
|
||||
private final Log logger = LogFactory.getLog(LdapUserDetailsManager.class);
|
||||
|
||||
/**
|
||||
* The strategy for mapping usernames to LDAP distinguished names.
|
||||
* This will be used when building DNs for creating new users etc.
|
||||
*/
|
||||
LdapUsernameToDnMapper usernameMapper = new DefaultLdapUsernameToDnMapper("cn=users", "uid");
|
||||
|
||||
/** The DN under which groups are stored */
|
||||
private DistinguishedName groupSearchBase = new DistinguishedName("cn=groups");
|
||||
|
||||
/** Password attribute name */
|
||||
private String passwordAttributeName = "userPassword";
|
||||
|
||||
/** The attribute which corresponds to the role name of a group. */
|
||||
private String groupRoleAttributeName ="cn";
|
||||
/** The attribute which contains members of a group */
|
||||
private String groupMemberAttributeName = "uniquemember";
|
||||
|
||||
private String rolePrefix = "ROLE_";
|
||||
|
||||
/** The pattern to be used for the user search. {0} is the user's DN */
|
||||
private String groupSearchFilter = "(uniquemember={0})";
|
||||
/**
|
||||
* The strategy used to create a UserDetails object from the LDAP context, username and list of authorities.
|
||||
* This should be set to match the required UserDetails implementation.
|
||||
*/
|
||||
private UserDetailsContextMapper userDetailsMapper = new InetOrgPersonContextMapper();
|
||||
|
||||
private LdapTemplate template;
|
||||
|
||||
/** Default context mapper used to create a set of roles from a list of attributes */
|
||||
private AttributesMapper roleMapper = new AttributesMapper() {
|
||||
|
||||
public Object mapFromAttributes(Attributes attributes) throws NamingException {
|
||||
Attribute roleAttr = attributes.get(groupRoleAttributeName);
|
||||
|
||||
NamingEnumeration<?> ne = roleAttr.getAll();
|
||||
// assert ne.hasMore();
|
||||
Object group = ne.next();
|
||||
String role = group.toString();
|
||||
|
||||
return new GrantedAuthorityImpl(rolePrefix + role.toUpperCase());
|
||||
}
|
||||
};
|
||||
|
||||
private String[] attributesToRetrieve;
|
||||
|
||||
public LdapUserDetailsManager(ContextSource contextSource) {
|
||||
template = new LdapTemplate(contextSource);
|
||||
}
|
||||
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
|
||||
DistinguishedName dn = usernameMapper.buildDn(username);
|
||||
List<GrantedAuthority> authorities = getUserAuthorities(dn, username);
|
||||
|
||||
logger.debug("Loading user '"+ username + "' with DN '" + dn + "'");
|
||||
|
||||
DirContextAdapter userCtx = loadUserAsContext(dn, username);
|
||||
|
||||
return userDetailsMapper.mapUserFromContext(userCtx, username, authorities);
|
||||
}
|
||||
|
||||
private DirContextAdapter loadUserAsContext(final DistinguishedName dn, final String username) {
|
||||
return (DirContextAdapter) template.executeReadOnly(new ContextExecutor() {
|
||||
public Object executeWithContext(DirContext ctx) throws NamingException {
|
||||
try {
|
||||
Attributes attrs = ctx.getAttributes(dn, attributesToRetrieve);
|
||||
return new DirContextAdapter(attrs, LdapUtils.getFullDn(dn, ctx));
|
||||
} catch(NameNotFoundException notFound) {
|
||||
throw new UsernameNotFoundException("User " + username + " not found", notFound);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the password for the current user. The username is obtained from the security context.
|
||||
* <p>
|
||||
* If the old password is supplied, the update will be made by rebinding as the user, thus modifying the password
|
||||
* using the user's permissions. If <code>oldPassword</code> is null, the update will be attempted using a
|
||||
* standard read/write context supplied by the context source.
|
||||
* </p>
|
||||
*
|
||||
* @param oldPassword the old password
|
||||
* @param newPassword the new value of the password.
|
||||
*/
|
||||
public void changePassword(final String oldPassword, final String newPassword) {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
Assert.notNull(authentication,
|
||||
"No authentication object found in security context. Can't change current user's password!");
|
||||
|
||||
String username = authentication.getName();
|
||||
|
||||
logger.debug("Changing password for user '"+ username);
|
||||
|
||||
final DistinguishedName dn = usernameMapper.buildDn(username);
|
||||
final ModificationItem[] passwordChange = new ModificationItem[] {
|
||||
new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute(passwordAttributeName, newPassword))
|
||||
};
|
||||
|
||||
if(oldPassword == null) {
|
||||
template.modifyAttributes(dn, passwordChange);
|
||||
return;
|
||||
}
|
||||
|
||||
template.executeReadWrite(new ContextExecutor() {
|
||||
|
||||
public Object executeWithContext(DirContext dirCtx) throws NamingException {
|
||||
LdapContext ctx = (LdapContext) dirCtx;
|
||||
ctx.removeFromEnvironment("com.sun.jndi.ldap.connect.pool");
|
||||
ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, LdapUtils.getFullDn(dn, ctx).toString());
|
||||
ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, oldPassword);
|
||||
// TODO: reconnect doesn't appear to actually change the credentials
|
||||
try {
|
||||
ctx.reconnect(null);
|
||||
} catch (javax.naming.AuthenticationException e) {
|
||||
throw new BadCredentialsException("Authentication for password change failed.");
|
||||
}
|
||||
|
||||
ctx.modifyAttributes(dn, passwordChange);
|
||||
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param dn the distinguished name of the entry - may be either relative to the base context
|
||||
* or a complete DN including the name of the context (either is supported).
|
||||
* @param username the user whose roles are required.
|
||||
* @return the granted authorities returned by the group search
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
List<GrantedAuthority> getUserAuthorities(final DistinguishedName dn, final String username) {
|
||||
SearchExecutor se = new SearchExecutor() {
|
||||
public NamingEnumeration<SearchResult> executeSearch(DirContext ctx) throws NamingException {
|
||||
DistinguishedName fullDn = LdapUtils.getFullDn(dn, ctx);
|
||||
SearchControls ctrls = new SearchControls();
|
||||
ctrls.setReturningAttributes(new String[] {groupRoleAttributeName});
|
||||
|
||||
return ctx.search(groupSearchBase, groupSearchFilter, new String[] {fullDn.toUrl(), username}, ctrls);
|
||||
}
|
||||
};
|
||||
|
||||
AttributesMapperCallbackHandler roleCollector =
|
||||
new AttributesMapperCallbackHandler(roleMapper);
|
||||
|
||||
template.search(se, roleCollector);
|
||||
return roleCollector.getList();
|
||||
}
|
||||
|
||||
// protected String getRoleFilter(DistinguishedName dn, String username) {
|
||||
// return new EqualsFilter("uniquemember", dn.toString()).encode();
|
||||
// }
|
||||
|
||||
public void createUser(UserDetails user) {
|
||||
DirContextAdapter ctx = new DirContextAdapter();
|
||||
copyToContext(user, ctx);
|
||||
DistinguishedName dn = usernameMapper.buildDn(user.getUsername());
|
||||
// Check for any existing authorities which might be set for this DN
|
||||
List<GrantedAuthority> authorities = getUserAuthorities(dn, user.getUsername());
|
||||
|
||||
if(authorities.size() > 0) {
|
||||
removeAuthorities(dn, authorities);
|
||||
}
|
||||
|
||||
logger.debug("Creating new user '"+ user.getUsername() + "' with DN '" + dn + "'");
|
||||
|
||||
template.bind(dn, ctx, null);
|
||||
|
||||
addAuthorities(dn, user.getAuthorities());
|
||||
}
|
||||
|
||||
public void updateUser(UserDetails user) {
|
||||
// Assert.notNull(attributesToRetrieve, "Configuration must specify a list of attributes in order to use update.");
|
||||
DistinguishedName dn = usernameMapper.buildDn(user.getUsername());
|
||||
|
||||
logger.debug("Updating user '"+ user.getUsername() + "' with DN '" + dn + "'");
|
||||
|
||||
List<GrantedAuthority> authorities = getUserAuthorities(dn, user.getUsername());
|
||||
|
||||
DirContextAdapter ctx = loadUserAsContext(dn, user.getUsername());
|
||||
ctx.setUpdateMode(true);
|
||||
copyToContext(user, ctx);
|
||||
|
||||
// Remove the objectclass attribute from the list of mods (if present).
|
||||
List<ModificationItem> mods = new LinkedList<ModificationItem>(Arrays.asList(ctx.getModificationItems()));
|
||||
ListIterator<ModificationItem> modIt = mods.listIterator();
|
||||
|
||||
while(modIt.hasNext()) {
|
||||
ModificationItem mod = (ModificationItem) modIt.next();
|
||||
Attribute a = mod.getAttribute();
|
||||
if("objectclass".equalsIgnoreCase(a.getID())) {
|
||||
modIt.remove();
|
||||
}
|
||||
}
|
||||
|
||||
template.modifyAttributes(dn, mods.toArray(new ModificationItem[mods.size()]));
|
||||
|
||||
// template.rebind(dn, ctx, null);
|
||||
// Remove the old authorities and replace them with the new one
|
||||
removeAuthorities(dn, authorities);
|
||||
addAuthorities(dn, user.getAuthorities());
|
||||
}
|
||||
|
||||
public void deleteUser(String username) {
|
||||
DistinguishedName dn = usernameMapper.buildDn(username);
|
||||
removeAuthorities(dn, getUserAuthorities(dn, username));
|
||||
template.unbind(dn);
|
||||
}
|
||||
|
||||
public boolean userExists(String username) {
|
||||
DistinguishedName dn = usernameMapper.buildDn(username);
|
||||
|
||||
try {
|
||||
Object obj = template.lookup(dn);
|
||||
if (obj instanceof Context) {
|
||||
LdapUtils.closeContext((Context) obj);
|
||||
}
|
||||
return true;
|
||||
} catch(org.springframework.ldap.NameNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DN from a group name.
|
||||
*
|
||||
* @param group the name of the group
|
||||
* @return the DN of the corresponding group, including the groupSearchBase
|
||||
*/
|
||||
protected DistinguishedName buildGroupDn(String group) {
|
||||
DistinguishedName dn = new DistinguishedName(groupSearchBase);
|
||||
dn.add(groupRoleAttributeName, group.toLowerCase());
|
||||
|
||||
return dn;
|
||||
}
|
||||
|
||||
protected void copyToContext(UserDetails user, DirContextAdapter ctx) {
|
||||
userDetailsMapper.mapUserToContext(user, ctx);
|
||||
}
|
||||
|
||||
protected void addAuthorities(DistinguishedName userDn, List<GrantedAuthority> authorities) {
|
||||
modifyAuthorities(userDn, authorities, DirContext.ADD_ATTRIBUTE);
|
||||
}
|
||||
|
||||
protected void removeAuthorities(DistinguishedName userDn, List<GrantedAuthority> authorities) {
|
||||
modifyAuthorities(userDn, authorities, DirContext.REMOVE_ATTRIBUTE);
|
||||
}
|
||||
|
||||
private void modifyAuthorities(final DistinguishedName userDn, final List<GrantedAuthority> authorities, final int modType) {
|
||||
template.executeReadWrite(new ContextExecutor() {
|
||||
public Object executeWithContext(DirContext ctx) throws NamingException {
|
||||
for(int i=0; i < authorities.size(); i++) {
|
||||
GrantedAuthority authority = authorities.get(i);
|
||||
String group = convertAuthorityToGroup(authority);
|
||||
DistinguishedName fullDn = LdapUtils.getFullDn(userDn, ctx);
|
||||
ModificationItem addGroup = new ModificationItem(modType,
|
||||
new BasicAttribute(groupMemberAttributeName, fullDn.toUrl()));
|
||||
|
||||
ctx.modifyAttributes(buildGroupDn(group), new ModificationItem[] {addGroup});
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String convertAuthorityToGroup(GrantedAuthority authority) {
|
||||
String group = authority.getAuthority();
|
||||
|
||||
if(group.startsWith(rolePrefix)) {
|
||||
group = group.substring(rolePrefix.length());
|
||||
}
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
public void setUsernameMapper(LdapUsernameToDnMapper usernameMapper) {
|
||||
this.usernameMapper = usernameMapper;
|
||||
}
|
||||
|
||||
public void setPasswordAttributeName(String passwordAttributeName) {
|
||||
this.passwordAttributeName = passwordAttributeName;
|
||||
}
|
||||
|
||||
public void setGroupSearchBase(String groupSearchBase) {
|
||||
this.groupSearchBase = new DistinguishedName(groupSearchBase);
|
||||
}
|
||||
|
||||
public void setGroupRoleAttributeName(String groupRoleAttributeName) {
|
||||
this.groupRoleAttributeName = groupRoleAttributeName;
|
||||
}
|
||||
|
||||
public void setAttributesToRetrieve(String[] attributesToRetrieve) {
|
||||
Assert.notNull(attributesToRetrieve);
|
||||
this.attributesToRetrieve = attributesToRetrieve;
|
||||
}
|
||||
|
||||
public void setUserDetailsMapper(UserDetailsContextMapper userDetailsMapper) {
|
||||
this.userDetailsMapper = userDetailsMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the multi-valued attribute which holds the DNs of users who are members of a group.
|
||||
* <p>
|
||||
* Usually this will be <tt>uniquemember</tt> (the default value) or <tt>member</tt>.
|
||||
* </p>
|
||||
*
|
||||
* @param groupMemberAttributeName the name of the attribute used to store group members.
|
||||
*/
|
||||
public void setGroupMemberAttributeName(String groupMemberAttributeName) {
|
||||
Assert.hasText(groupMemberAttributeName);
|
||||
this.groupMemberAttributeName = groupMemberAttributeName;
|
||||
this.groupSearchFilter = "(" + groupMemberAttributeName + "={0})";
|
||||
}
|
||||
|
||||
public void setRoleMapper(AttributesMapper roleMapper) {
|
||||
this.roleMapper = roleMapper;
|
||||
}
|
||||
}
|
||||
-177
@@ -1,177 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.userdetails.ldap;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.security.GrantedAuthorityImpl;
|
||||
import org.springframework.security.GrantedAuthority;
|
||||
import org.springframework.security.userdetails.UserDetails;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.ldap.core.DirContextAdapter;
|
||||
import org.springframework.ldap.core.DirContextOperations;
|
||||
|
||||
|
||||
/**
|
||||
* The context mapper used by the LDAP authentication provider to create an LDAP user object.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public class LdapUserDetailsMapper implements UserDetailsContextMapper {
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private final Log logger = LogFactory.getLog(LdapUserDetailsMapper.class);
|
||||
private String passwordAttributeName = "userPassword";
|
||||
private String rolePrefix = "ROLE_";
|
||||
private String[] roleAttributes = null;
|
||||
private boolean convertToUpperCase = true;
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public UserDetails mapUserFromContext(DirContextOperations ctx, String username, List<GrantedAuthority> authorities) {
|
||||
String dn = ctx.getNameInNamespace();
|
||||
|
||||
logger.debug("Mapping user details from context with DN: " + dn);
|
||||
|
||||
LdapUserDetailsImpl.Essence essence = new LdapUserDetailsImpl.Essence();
|
||||
essence.setDn(dn);
|
||||
|
||||
Object passwordValue = ctx.getObjectAttribute(passwordAttributeName);
|
||||
|
||||
if (passwordValue != null) {
|
||||
essence.setPassword(mapPassword(passwordValue));
|
||||
}
|
||||
|
||||
essence.setUsername(username);
|
||||
|
||||
// Map the roles
|
||||
for (int i = 0; (roleAttributes != null) && (i < roleAttributes.length); i++) {
|
||||
String[] rolesForAttribute = ctx.getStringAttributes(roleAttributes[i]);
|
||||
|
||||
if (rolesForAttribute == null) {
|
||||
logger.debug("Couldn't read role attribute '" + roleAttributes[i] + "' for user " + dn);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int j = 0; j < rolesForAttribute.length; j++) {
|
||||
GrantedAuthority authority = createAuthority(rolesForAttribute[j]);
|
||||
|
||||
if (authority != null) {
|
||||
essence.addAuthority(authority);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add the supplied authorities
|
||||
|
||||
for (int i=0; i < authorities.size(); i++) {
|
||||
essence.addAuthority(authorities.get(i));
|
||||
}
|
||||
|
||||
return essence.createUserDetails();
|
||||
|
||||
}
|
||||
|
||||
public void mapUserToContext(UserDetails user, DirContextAdapter ctx) {
|
||||
throw new UnsupportedOperationException("LdapUserDetailsMapper only supports reading from a context. Please" +
|
||||
"use a subclass if mapUserToContext() is required.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension point to allow customized creation of the user's password from
|
||||
* the attribute stored in the directory.
|
||||
*
|
||||
* @param passwordValue the value of the password attribute
|
||||
* @return a String representation of the password.
|
||||
*/
|
||||
protected String mapPassword(Object passwordValue) {
|
||||
|
||||
if (!(passwordValue instanceof String)) {
|
||||
// Assume it's binary
|
||||
passwordValue = new String((byte[]) passwordValue);
|
||||
}
|
||||
|
||||
return (String) passwordValue;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a GrantedAuthority from a role attribute. Override to customize
|
||||
* authority object creation.
|
||||
* <p>
|
||||
* The default implementation converts string attributes to roles, making use of the <tt>rolePrefix</tt>
|
||||
* and <tt>convertToUpperCase</tt> properties. Non-String attributes are ignored.
|
||||
* </p>
|
||||
*
|
||||
* @param role the attribute returned from
|
||||
* @return the authority to be added to the list of authorities for the user, or null
|
||||
* if this attribute should be ignored.
|
||||
*/
|
||||
protected GrantedAuthority createAuthority(Object role) {
|
||||
if (role instanceof String) {
|
||||
if (convertToUpperCase) {
|
||||
role = ((String) role).toUpperCase();
|
||||
}
|
||||
return new GrantedAuthorityImpl(rolePrefix + role);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether role field values will be converted to upper case when loaded.
|
||||
* The default is true.
|
||||
*
|
||||
* @param convertToUpperCase true if the roles should be converted to upper case.
|
||||
*/
|
||||
public void setConvertToUpperCase(boolean convertToUpperCase) {
|
||||
this.convertToUpperCase = convertToUpperCase;
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the attribute which contains the user's password.
|
||||
* Defaults to "userPassword".
|
||||
*
|
||||
* @param passwordAttributeName the name of the attribute
|
||||
*/
|
||||
public void setPasswordAttributeName(String passwordAttributeName) {
|
||||
this.passwordAttributeName = passwordAttributeName;
|
||||
}
|
||||
|
||||
/**
|
||||
* The names of any attributes in the user's entry which represent application
|
||||
* roles. These will be converted to <tt>GrantedAuthority</tt>s and added to the
|
||||
* list in the returned LdapUserDetails object. The attribute values must be Strings by default.
|
||||
*
|
||||
* @param roleAttributes the names of the role attributes.
|
||||
*/
|
||||
public void setRoleAttributes(String[] roleAttributes) {
|
||||
Assert.notNull(roleAttributes, "roleAttributes array cannot be null");
|
||||
this.roleAttributes = roleAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* The prefix that should be applied to the role names
|
||||
* @param rolePrefix the prefix (defaults to "ROLE_").
|
||||
*/
|
||||
public void setRolePrefix(String rolePrefix) {
|
||||
this.rolePrefix = rolePrefix;
|
||||
}
|
||||
}
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
package org.springframework.security.userdetails.ldap;
|
||||
|
||||
import org.springframework.ldap.core.DirContextOperations;
|
||||
import org.springframework.security.ldap.LdapAuthoritiesPopulator;
|
||||
import org.springframework.security.ldap.LdapUserSearch;
|
||||
import org.springframework.security.userdetails.UserDetails;
|
||||
import org.springframework.security.userdetails.UserDetailsService;
|
||||
import org.springframework.security.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* LDAP implementation of UserDetailsService based around an {@link LdapUserSearch}
|
||||
* and an {@link LdapAuthoritiesPopulator}. The final <tt>UserDetails</tt> object
|
||||
* returned from <tt>loadUserByUsername</tt> is created by the configured <tt>UserDetailsContextMapper</tt>.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public class LdapUserDetailsService implements UserDetailsService {
|
||||
private LdapUserSearch userSearch;
|
||||
private LdapAuthoritiesPopulator authoritiesPopulator;
|
||||
private UserDetailsContextMapper userDetailsMapper = new LdapUserDetailsMapper();
|
||||
|
||||
public LdapUserDetailsService(LdapUserSearch userSearch, LdapAuthoritiesPopulator authoritiesPopulator) {
|
||||
Assert.notNull(userSearch, "userSearch must not be null");
|
||||
Assert.notNull(authoritiesPopulator, "authoritiesPopulator must not be null");
|
||||
this.userSearch = userSearch;
|
||||
this.authoritiesPopulator = authoritiesPopulator;
|
||||
}
|
||||
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
DirContextOperations userData = userSearch.searchForUser(username);
|
||||
|
||||
return userDetailsMapper.mapUserFromContext(userData, username,
|
||||
authoritiesPopulator.getGrantedAuthorities(userData, username));
|
||||
}
|
||||
|
||||
public void setUserDetailsMapper(UserDetailsContextMapper userDetailsMapper) {
|
||||
Assert.notNull(userDetailsMapper, "userDetailsMapper must not be null");
|
||||
this.userDetailsMapper = userDetailsMapper;
|
||||
}
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.userdetails.ldap;
|
||||
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import org.springframework.ldap.core.DirContextAdapter;
|
||||
import org.springframework.ldap.core.DirContextOperations;
|
||||
|
||||
import org.springframework.security.ldap.LdapUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* UserDetails implementation whose properties are based on the LDAP schema for <tt>Person</tt>.
|
||||
*
|
||||
* @author Luke
|
||||
* @since 2.0
|
||||
* @version $Id$
|
||||
*/
|
||||
public class Person extends LdapUserDetailsImpl {
|
||||
private String sn;
|
||||
private String description;
|
||||
private String telephoneNumber;
|
||||
private List<String> cn = new ArrayList<String>();
|
||||
|
||||
protected Person() {
|
||||
}
|
||||
|
||||
public String getSn() {
|
||||
return sn;
|
||||
}
|
||||
|
||||
public String[] getCn() {
|
||||
return cn.toArray(new String[cn.size()]);
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public String getTelephoneNumber() {
|
||||
return telephoneNumber;
|
||||
}
|
||||
|
||||
protected void populateContext(DirContextAdapter adapter) {
|
||||
adapter.setAttributeValue("sn", sn);
|
||||
adapter.setAttributeValues("cn", getCn());
|
||||
adapter.setAttributeValue("description", getDescription());
|
||||
adapter.setAttributeValue("telephoneNumber", getTelephoneNumber());
|
||||
|
||||
if(getPassword() != null) {
|
||||
adapter.setAttributeValue("userPassword", getPassword());
|
||||
}
|
||||
adapter.setAttributeValues("objectclass", new String[] {"top", "person"});
|
||||
}
|
||||
|
||||
public static class Essence extends LdapUserDetailsImpl.Essence {
|
||||
|
||||
public Essence() {
|
||||
}
|
||||
|
||||
public Essence(DirContextOperations ctx) {
|
||||
super(ctx);
|
||||
setCn(ctx.getStringAttributes("cn"));
|
||||
setSn(ctx.getStringAttribute("sn"));
|
||||
setDescription(ctx.getStringAttribute("description"));
|
||||
setTelephoneNumber(ctx.getStringAttribute("telephoneNumber"));
|
||||
Object passo = ctx.getObjectAttribute("userPassword");
|
||||
|
||||
if(passo != null) {
|
||||
String password = LdapUtils.convertPasswordToString(passo);
|
||||
setPassword(password);
|
||||
}
|
||||
}
|
||||
|
||||
public Essence(Person copyMe) {
|
||||
super(copyMe);
|
||||
setSn(copyMe.sn);
|
||||
setDescription(copyMe.getDescription());
|
||||
setTelephoneNumber(copyMe.getTelephoneNumber());
|
||||
((Person) instance).cn = new ArrayList<String>(copyMe.cn);
|
||||
}
|
||||
|
||||
protected LdapUserDetailsImpl createTarget() {
|
||||
return new Person();
|
||||
}
|
||||
|
||||
public void setSn(String sn) {
|
||||
((Person) instance).sn = sn;
|
||||
}
|
||||
|
||||
public void setCn(String[] cn) {
|
||||
((Person) instance).cn = Arrays.asList(cn);
|
||||
}
|
||||
|
||||
public void addCn(String value) {
|
||||
((Person) instance).cn.add(value);
|
||||
}
|
||||
|
||||
public void setTelephoneNumber(String tel) {
|
||||
((Person) instance).telephoneNumber = tel;
|
||||
}
|
||||
|
||||
public void setDescription(String desc) {
|
||||
((Person) instance).description = desc;
|
||||
}
|
||||
|
||||
public LdapUserDetails createUserDetails() {
|
||||
Person p = (Person) super.createUserDetails();
|
||||
Assert.hasLength(p.sn);
|
||||
Assert.notNull(p.cn);
|
||||
Assert.notEmpty(p.cn);
|
||||
// TODO: Check contents for null entries
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
package org.springframework.security.userdetails.ldap;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.security.userdetails.UserDetails;
|
||||
import org.springframework.security.GrantedAuthority;
|
||||
import org.springframework.ldap.core.DirContextOperations;
|
||||
import org.springframework.ldap.core.DirContextAdapter;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public class PersonContextMapper implements UserDetailsContextMapper {
|
||||
|
||||
public UserDetails mapUserFromContext(DirContextOperations ctx, String username, List<GrantedAuthority> authorities) {
|
||||
Person.Essence p = new Person.Essence(ctx);
|
||||
|
||||
p.setUsername(username);
|
||||
p.setAuthorities(authorities);
|
||||
|
||||
return p.createUserDetails();
|
||||
|
||||
}
|
||||
|
||||
public void mapUserToContext(UserDetails user, DirContextAdapter ctx) {
|
||||
Assert.isInstanceOf(Person.class, user, "UserDetails must be a Person instance");
|
||||
|
||||
Person p = (Person) user;
|
||||
p.populateContext(ctx);
|
||||
}
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.userdetails.ldap;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.security.userdetails.UserDetails;
|
||||
import org.springframework.security.GrantedAuthority;
|
||||
import org.springframework.ldap.core.DirContextOperations;
|
||||
import org.springframework.ldap.core.DirContextAdapter;
|
||||
|
||||
/**
|
||||
* Operations to map a UserDetails object to and from a Spring LDAP <tt>DirContextOperations</tt> implementation.
|
||||
* Used by LdapUserDetailsManager when loading and saving/creating user information.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @since 2.0
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface UserDetailsContextMapper {
|
||||
|
||||
/**
|
||||
* Creates a fully populated UserDetails object for use by the security framework.
|
||||
*
|
||||
* @param ctx the context object which contains the user information.
|
||||
* @param username the user's supplied login name.
|
||||
* @param authority the list of authorities which the user should be given.
|
||||
* @return the user object.
|
||||
*/
|
||||
UserDetails mapUserFromContext(DirContextOperations ctx, String username, List<GrantedAuthority> authority);
|
||||
|
||||
/**
|
||||
* Reverse of the above operation. Populates a context object from the supplied user object.
|
||||
* Called when saving a user, for example.
|
||||
*/
|
||||
void mapUserToContext(UserDetails user, DirContextAdapter ctx);
|
||||
}
|
||||
Reference in New Issue
Block a user