From 530f686149f2567ddbce1a97476361214b5edc19 Mon Sep 17 00:00:00 2001 From: Luke Taylor Date: Sun, 6 Mar 2011 22:13:32 +0000 Subject: [PATCH] SEC-1181: Basic AuthenticationProvider for Active Directory. --- .../ldap/SpringSecurityLdapTemplate.java | 88 +++++---- .../AbstractLdapAuthenticationProvider.java | 134 ++++++++++++++ .../LdapAuthenticationProvider.java | 120 +----------- ...veDirectoryLdapAuthenticationProvider.java | 175 ++++++++++++++++++ ...ectoryLdapAuthenticationProviderTests.java | 25 +++ 5 files changed, 389 insertions(+), 153 deletions(-) create mode 100644 ldap/src/main/java/org/springframework/security/ldap/authentication/AbstractLdapAuthenticationProvider.java create mode 100644 ldap/src/main/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProvider.java create mode 100644 ldap/src/test/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProviderTests.java diff --git a/ldap/src/main/java/org/springframework/security/ldap/SpringSecurityLdapTemplate.java b/ldap/src/main/java/org/springframework/security/ldap/SpringSecurityLdapTemplate.java index bb3d2208db..835c6587da 100644 --- a/ldap/src/main/java/org/springframework/security/ldap/SpringSecurityLdapTemplate.java +++ b/ldap/src/main/java/org/springframework/security/ldap/SpringSecurityLdapTemplate.java @@ -195,47 +195,55 @@ public class SpringSecurityLdapTemplate extends LdapTemplate { return (DirContextOperations) executeReadOnly(new ContextExecutor() { public Object executeWithContext(DirContext ctx) throws NamingException { - final DistinguishedName ctxBaseDn = new DistinguishedName(ctx.getNameInNamespace()); - final DistinguishedName searchBaseDn = new DistinguishedName(base); - final NamingEnumeration resultsEnum = ctx.search(searchBaseDn, filter, params, searchControls); - - if (logger.isDebugEnabled()) { - logger.debug("Searching for entry under DN '" + ctxBaseDn - + "', base = '" + searchBaseDn + "', filter = '" + filter + "'"); - } - - Set results = new HashSet(); - try { - while (resultsEnum.hasMore()) { - SearchResult searchResult = resultsEnum.next(); - // Work out the DN of the matched entry - DistinguishedName dn = new DistinguishedName(new CompositeName(searchResult.getName())); - - if (base.length() > 0) { - dn.prepend(searchBaseDn); - } - - if (logger.isDebugEnabled()) { - logger.debug("Found DN: " + dn); - } - results.add(new DirContextAdapter(searchResult.getAttributes(), dn, ctxBaseDn)); - } - } catch (PartialResultException e) { - LdapUtils.closeEnumeration(resultsEnum); - 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]; + return searchForSingleEntryInternal(ctx, searchControls, base, filter, params); } - }); + }); + } + + /** + * Internal method extracted to avoid code duplication in AD search. + */ + public static DirContextOperations searchForSingleEntryInternal(DirContext ctx, SearchControls searchControls, + String base, String filter, Object[] params) throws NamingException { + final DistinguishedName ctxBaseDn = new DistinguishedName(ctx.getNameInNamespace()); + final DistinguishedName searchBaseDn = new DistinguishedName(base); + final NamingEnumeration resultsEnum = ctx.search(searchBaseDn, filter, params, searchControls); + + if (logger.isDebugEnabled()) { + logger.debug("Searching for entry under DN '" + ctxBaseDn + + "', base = '" + searchBaseDn + "', filter = '" + filter + "'"); + } + + Set results = new HashSet(); + try { + while (resultsEnum.hasMore()) { + SearchResult searchResult = resultsEnum.next(); + // Work out the DN of the matched entry + DistinguishedName dn = new DistinguishedName(new CompositeName(searchResult.getName())); + + if (base.length() > 0) { + dn.prepend(searchBaseDn); + } + + if (logger.isDebugEnabled()) { + logger.debug("Found DN: " + dn); + } + results.add(new DirContextAdapter(searchResult.getAttributes(), dn, ctxBaseDn)); + } + } catch (PartialResultException e) { + LdapUtils.closeEnumeration(resultsEnum); + 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.iterator().next(); } /** diff --git a/ldap/src/main/java/org/springframework/security/ldap/authentication/AbstractLdapAuthenticationProvider.java b/ldap/src/main/java/org/springframework/security/ldap/authentication/AbstractLdapAuthenticationProvider.java new file mode 100644 index 0000000000..5389a5f40f --- /dev/null +++ b/ldap/src/main/java/org/springframework/security/ldap/authentication/AbstractLdapAuthenticationProvider.java @@ -0,0 +1,134 @@ +package org.springframework.security.ldap.authentication; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.context.MessageSource; +import org.springframework.context.MessageSourceAware; +import org.springframework.context.support.MessageSourceAccessor; +import org.springframework.ldap.core.DirContextOperations; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.SpringSecurityMessageSource; +import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; +import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.ldap.userdetails.LdapUserDetailsMapper; +import org.springframework.security.ldap.userdetails.UserDetailsContextMapper; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +import java.util.*; + +/** + * Base class for the standard {@code LdapAuthenticationProvider} and the + * {@code ActiveDirectoryLdapAuthenticationProvider}. + * + * @author Luke Taylor + * @since 3.1 + */ +public abstract class AbstractLdapAuthenticationProvider implements AuthenticationProvider, MessageSourceAware { + protected final Log logger = LogFactory.getLog(LdapAuthenticationProvider.class); + protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor(); + private boolean useAuthenticationRequestCredentials = true; + private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper(); + protected UserDetailsContextMapper userDetailsContextMapper = new LdapUserDetailsMapper(); + + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication, + messages.getMessage("LdapAuthenticationProvider.onlySupports", + "Only UsernamePasswordAuthenticationToken is supported")); + + final UsernamePasswordAuthenticationToken userToken = (UsernamePasswordAuthenticationToken)authentication; + + String username = userToken.getName(); + String password = (String) authentication.getCredentials(); + + if (logger.isDebugEnabled()) { + logger.debug("Processing authentication request for user: " + username); + } + + if (!StringUtils.hasLength(username)) { + throw new BadCredentialsException(messages.getMessage("LdapAuthenticationProvider.emptyUsername", + "Empty Username")); + } + + Assert.notNull(password, "Null password was supplied in authentication token"); + + DirContextOperations userData = doAuthentication(userToken); + + UserDetails user = userDetailsContextMapper.mapUserFromContext(userData, authentication.getName(), + loadUserAuthorities(userData, authentication.getName(), (String)authentication.getCredentials())); + + return createSuccessfulAuthentication(userToken, user); + } + + protected abstract DirContextOperations doAuthentication(UsernamePasswordAuthenticationToken auth); + + protected abstract Collection loadUserAuthorities(DirContextOperations userData, String username, String password); + + /** + * Creates the final {@code Authentication} object which will be returned from the {@code authenticate} method. + * + * @param authentication the original authentication request token + * @param user the UserDetails instance returned by the configured UserDetailsContextMapper. + * @return the Authentication object for the fully authenticated user. + */ + protected Authentication createSuccessfulAuthentication(UsernamePasswordAuthenticationToken authentication, + UserDetails user) { + Object password = useAuthenticationRequestCredentials ? authentication.getCredentials() : user.getPassword(); + + UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(user, password, + authoritiesMapper.mapAuthorities(user.getAuthorities())); + result.setDetails(authentication.getDetails()); + + return result; + } + + public boolean supports(Class authentication) { + return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication); + } + + /** + * 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 {@code UserDetailsContextMapper}. + * 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 void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) { + this.authoritiesMapper = authoritiesMapper; + } + + /** + * Allows a custom strategy to be used for creating the UserDetails which will be stored as the principal + * in the Authentication returned by the + * {@link #createSuccessfulAuthentication(org.springframework.security.authentication.UsernamePasswordAuthenticationToken, org.springframework.security.core.userdetails.UserDetails)} method. + * + * @param userDetailsContextMapper the strategy instance. If not set, defaults to a simple + * LdapUserDetailsMapper. + */ + public void setUserDetailsContextMapper(UserDetailsContextMapper userDetailsContextMapper) { + Assert.notNull(userDetailsContextMapper, "UserDetailsContextMapper must not be null"); + this.userDetailsContextMapper = userDetailsContextMapper; + } + + /** + * Provides access to the injected {@code UserDetailsContextMapper} strategy for use by subclasses. + */ + protected UserDetailsContextMapper getUserDetailsContextMapper() { + return userDetailsContextMapper; + } +} diff --git a/ldap/src/main/java/org/springframework/security/ldap/authentication/LdapAuthenticationProvider.java b/ldap/src/main/java/org/springframework/security/ldap/authentication/LdapAuthenticationProvider.java index b89ca6bd56..55cccf14c4 100644 --- a/ldap/src/main/java/org/springframework/security/ldap/authentication/LdapAuthenticationProvider.java +++ b/ldap/src/main/java/org/springframework/security/ldap/authentication/LdapAuthenticationProvider.java @@ -15,27 +15,13 @@ package org.springframework.security.ldap.authentication; -import java.util.Collection; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -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.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.LockedException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.SpringSecurityMessageSource; -import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; -import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper; -import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.ldap.ppolicy.PasswordPolicyException; import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator; @@ -43,7 +29,8 @@ import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator; import org.springframework.security.ldap.userdetails.LdapUserDetailsMapper; import org.springframework.security.ldap.userdetails.UserDetailsContextMapper; import org.springframework.util.Assert; -import org.springframework.util.StringUtils; + +import java.util.*; /** @@ -128,21 +115,12 @@ import org.springframework.util.StringUtils; * @see BindAuthenticator * @see DefaultLdapAuthoritiesPopulator */ -public class LdapAuthenticationProvider implements AuthenticationProvider, MessageSourceAware { - //~ Static fields/initializers ===================================================================================== - - private static final Log logger = LogFactory.getLog(LdapAuthenticationProvider.class); - +public class LdapAuthenticationProvider extends AbstractLdapAuthenticationProvider { //~ 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; - private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper(); //~ Constructors =================================================================================================== @@ -190,78 +168,14 @@ public class LdapAuthenticationProvider implements AuthenticationProvider, Messa return authoritiesPopulator; } - /** - * Allows a custom strategy to be used for creating the UserDetails which will be stored as the principal - * in the Authentication returned by the - * {@link #createSuccessfulAuthentication(UsernamePasswordAuthenticationToken, UserDetails)} method. - * - * @param userDetailsContextMapper the strategy instance. If not set, defaults to a simple - * LdapUserDetailsMapper. - */ - public void setUserDetailsContextMapper(UserDetailsContextMapper userDetailsContextMapper) { - Assert.notNull(userDetailsContextMapper, "UserDetailsContextMapper must not be null"); - this.userDetailsContextMapper = userDetailsContextMapper; - } - - /** - * Provides access to the injected {@code UserDetailsContextMapper} 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 {@code UserDetailsContextMapper}. - * 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 void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) { - this.authoritiesMapper = authoritiesMapper; - } - - public Authentication authenticate(Authentication authentication) throws AuthenticationException { - Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication, - messages.getMessage("LdapAuthenticationProvider.onlySupports", - "Only UsernamePasswordAuthenticationToken is supported")); - - final UsernamePasswordAuthenticationToken userToken = (UsernamePasswordAuthenticationToken)authentication; - - String username = userToken.getName(); - String password = (String) authentication.getCredentials(); - - if (logger.isDebugEnabled()) { - logger.debug("Processing authentication request for user: " + username); - } - - if (!StringUtils.hasLength(username)) { - throw new BadCredentialsException(messages.getMessage("LdapAuthenticationProvider.emptyUsername", - "Empty Username")); - } - - Assert.notNull(password, "Null password was supplied in authentication token"); - + @Override + protected DirContextOperations doAuthentication(UsernamePasswordAuthenticationToken authentication) { try { - DirContextOperations userData = getAuthenticator().authenticate(authentication); - - UserDetails user = userDetailsContextMapper.mapUserFromContext(userData, username, - loadUserAuthorities(userData, username, password)); - - return createSuccessfulAuthentication(userToken, user); + return getAuthenticator().authenticate(authentication); } catch (PasswordPolicyException ppe) { // The only reason a ppolicy exception can occur during a bind is that the account is locked. throw new LockedException(messages.getMessage(ppe.getStatus().getErrorCode(), @@ -278,30 +192,10 @@ public class LdapAuthenticationProvider implements AuthenticationProvider, Messa } } + @Override protected Collection loadUserAuthorities(DirContextOperations userData, String username, String password) { return getAuthoritiesPopulator().getGrantedAuthorities(userData, username); } - /** - * Creates the final {@code Authentication} object which will be returned from the {@code authenticate} method. - * - * @param authentication the original authentication request token - * @param user the UserDetails instance returned by the configured UserDetailsContextMapper. - * @return the Authentication object for the fully authenticated user. - */ - protected Authentication createSuccessfulAuthentication(UsernamePasswordAuthenticationToken authentication, - UserDetails user) { - Object password = useAuthenticationRequestCredentials ? authentication.getCredentials() : user.getPassword(); - - UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(user, password, - authoritiesMapper.mapAuthorities(user.getAuthorities())); - result.setDetails(authentication.getDetails()); - - return result; - } - - public boolean supports(Class authentication) { - return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication)); - } } diff --git a/ldap/src/main/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProvider.java b/ldap/src/main/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProvider.java new file mode 100644 index 0000000000..39ac0f1b48 --- /dev/null +++ b/ldap/src/main/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProvider.java @@ -0,0 +1,175 @@ +package org.springframework.security.ldap.authentication.ad; + +import org.springframework.ldap.core.DirContextOperations; +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.ldap.LdapUtils; +import org.springframework.security.ldap.SpringSecurityLdapTemplate; +import org.springframework.security.ldap.authentication.AbstractLdapAuthenticationProvider; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +import javax.naming.Context; +import javax.naming.NamingException; +import javax.naming.directory.SearchControls; +import javax.naming.ldap.InitialLdapContext; +import javax.naming.ldap.LdapContext; +import java.util.*; + +/** + * Specialized LDAP authentication provider which uses Active Directory configuration conventions. + *

+ * It will authenticate using the Active Directory {@code userPrincipalName} (in the form {@code username@domain}). + * If the {@code usernameIncludesDomain} property is set to {@code true}, it is assumed that the user types in the + * full value, including the domain. Otherwise (the default), the {@code userPrincipalName} will be built from the + * username supplied in the authentication request. + *

+ * The user authorities are obtained from the data contained in the {@code memberOf} attribute. + * + * @author Luke Taylor + * @since 3.1 + */ +public final class ActiveDirectoryLdapAuthenticationProvider extends AbstractLdapAuthenticationProvider { + private final String domain; + private final String rootDn; + private final String url; + private boolean usernameIncludesDomain = false; + + /** + * @param domain the domain for which authentication should take place + */ +// public ActiveDirectoryLdapAuthenticationProvider(String domain) { +// this (domain, null); +// } + + /** + * @param domain the domain name + * @param url an LDAP url (or multiple URLs) + */ + public ActiveDirectoryLdapAuthenticationProvider(String domain, String url) { + Assert.isTrue(StringUtils.hasText(domain) || StringUtils.hasText(url), "Domain and url cannot both be empty"); + this.domain = StringUtils.hasText(domain) ? domain : null; + this.url = StringUtils.hasText(url) ? url : null; + rootDn = this.domain == null ? null : rootDnFromDomain(this.domain); + } + + @Override + protected DirContextOperations doAuthentication(UsernamePasswordAuthenticationToken auth) { + String username = auth.getName(); + String password = (String)auth.getCredentials(); + + LdapContext ctx = bindAsUser(username, password); + + try { + return searchForUser(ctx, username); + + } catch (NamingException e) { + logger.error("Failed to locate directory entry for authentication user: " + username, e); + throw authenticationFailure(); + } finally { + LdapUtils.closeContext(ctx); + } + } + + /** + * Creates the user authority list from the values of the {@code memberOf} attribute obtained from the user's + * Active Directory entry. + */ + @Override + protected Collection loadUserAuthorities(DirContextOperations userData, String username, String password) { + String[] groups = userData.getStringAttributes("memberOf"); + + if (groups == null) { + logger.debug("No values for 'memberOf' attribute."); + + return AuthorityUtils.NO_AUTHORITIES; + } + + if (logger.isDebugEnabled()) { + logger.debug("'memberOf' attribute values: " + Arrays.asList(groups)); + } + + ArrayList authorities = new ArrayList(groups.length); + + for (String group : groups) { + authorities.add(new SimpleGrantedAuthority(new DistinguishedName(group).removeLast().getValue())); + } + + return authorities; + } + + private LdapContext bindAsUser(String username, String password) { + // TODO. add DNS lookup based on domain + final String bindUrl = url; + + Hashtable env = new Hashtable(); + env.put(Context.SECURITY_AUTHENTICATION, "simple"); + env.put(Context.SECURITY_PRINCIPAL, createBindPrincipal(username)); + env.put(Context.PROVIDER_URL, bindUrl); + env.put(Context.SECURITY_CREDENTIALS, password); + env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); + + try { + return new InitialLdapContext(env, null); + } catch (NamingException e) { + logger.debug("Authentication failed", e); + throw authenticationFailure(); + } + } + + private BadCredentialsException authenticationFailure() { + return new BadCredentialsException(messages.getMessage( + "LdapAuthenticationProvider.badCredentials", "Bad credentials")); + } + + private DirContextOperations searchForUser(LdapContext ctx, String username) throws NamingException { + SearchControls searchCtls = new SearchControls(); + searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE); + + String searchFilter = "(&(objectClass=user)(userPrincipalName={0}))"; + + final String bindPrincipal = createBindPrincipal(username); + + String searchRoot = rootDn != null ? rootDn : searchRootFromPrincipal(bindPrincipal); + + return SpringSecurityLdapTemplate.searchForSingleEntryInternal(ctx, searchCtls, searchRoot, searchFilter, + new Object[]{bindPrincipal}); + } + + private String searchRootFromPrincipal(String bindPrincipal) { + return rootDnFromDomain(bindPrincipal.substring(bindPrincipal.lastIndexOf('@') + 1, bindPrincipal.length())); + } + + private String rootDnFromDomain(String domain) { + String[] tokens = StringUtils.tokenizeToStringArray(domain, "."); + StringBuilder root = new StringBuilder(); + + for (String token : tokens) { + if (root.length() > 0) { + root.append(','); + } + root.append("dc=").append(token); + } + + return root.toString(); + } + + private String createBindPrincipal(String username) { + if (usernameIncludesDomain || domain == null) { + return username; + } + + return username + "@" + domain; + } + + public void setUsernameIncludesDomain(boolean usernameIncludesDomain) { + Assert.isTrue(domain != null || usernameIncludesDomain, + "If the domain name is not included in the username, a domain must be set in the constructor"); + this.usernameIncludesDomain = usernameIncludesDomain; + } + +} diff --git a/ldap/src/test/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProviderTests.java b/ldap/src/test/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProviderTests.java new file mode 100644 index 0000000000..d95e3b1683 --- /dev/null +++ b/ldap/src/test/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProviderTests.java @@ -0,0 +1,25 @@ +package org.springframework.security.ldap.authentication.ad; + +import static org.junit.Assert.*; + +import org.junit.*; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.authority.SimpleGrantedAuthority; + +/** + * @author Luke Taylor + */ +public class ActiveDirectoryLdapAuthenticationProviderTests { + + @Test + public void simpleAuthenticationWithIsSucessful() throws Exception { + ActiveDirectoryLdapAuthenticationProvider provider = + new ActiveDirectoryLdapAuthenticationProvider(null, "ldap://192.168.1.200/"); + + Authentication result = provider.authenticate(new UsernamePasswordAuthenticationToken("luke@fenetres.monkeymachine.eu","p!ssw0rd")); + + assertEquals(1, result.getAuthorities().size()); + assertTrue(result.getAuthorities().contains(new SimpleGrantedAuthority("blah"))); + } +}