| Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||||||
| LdapAuthenticationProvider |
|
| 1.9166666666666667;1.917 |
| 1 | /* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited |
|
| 2 | * |
|
| 3 | * Licensed under the Apache License, Version 2.0 (the "License"); |
|
| 4 | * you may not use this file except in compliance with the License. |
|
| 5 | * You may obtain a copy of the License at |
|
| 6 | * |
|
| 7 | * http://www.apache.org/licenses/LICENSE-2.0 |
|
| 8 | * |
|
| 9 | * Unless required by applicable law or agreed to in writing, software |
|
| 10 | * distributed under the License is distributed on an "AS IS" BASIS, |
|
| 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 12 | * See the License for the specific language governing permissions and |
|
| 13 | * limitations under the License. |
|
| 14 | */ |
|
| 15 | ||
| 16 | package org.acegisecurity.providers.ldap; |
|
| 17 | ||
| 18 | import org.acegisecurity.AuthenticationException; |
|
| 19 | import org.acegisecurity.BadCredentialsException; |
|
| 20 | import org.acegisecurity.GrantedAuthority; |
|
| 21 | import org.acegisecurity.AuthenticationServiceException; |
|
| 22 | import org.acegisecurity.ldap.LdapDataAccessException; |
|
| 23 | ||
| 24 | import org.acegisecurity.providers.UsernamePasswordAuthenticationToken; |
|
| 25 | import org.acegisecurity.providers.dao.AbstractUserDetailsAuthenticationProvider; |
|
| 26 | ||
| 27 | import org.acegisecurity.userdetails.UserDetails; |
|
| 28 | import org.acegisecurity.userdetails.ldap.LdapUserDetails; |
|
| 29 | import org.acegisecurity.userdetails.ldap.LdapUserDetailsImpl; |
|
| 30 | ||
| 31 | import org.apache.commons.logging.Log; |
|
| 32 | import org.apache.commons.logging.LogFactory; |
|
| 33 | ||
| 34 | import org.springframework.util.Assert; |
|
| 35 | import org.springframework.util.StringUtils; |
|
| 36 | import org.springframework.dao.DataAccessException; |
|
| 37 | ||
| 38 | ||
| 39 | /** |
|
| 40 | * An {@link org.acegisecurity.providers.AuthenticationProvider} implementation that provides integration with an |
|
| 41 | * LDAP server. |
|
| 42 | * |
|
| 43 | * <p>There are many ways in which an LDAP directory can be configured so this class delegates most of |
|
| 44 | * its responsibilites to two separate strategy interfaces, {@link LdapAuthenticator} |
|
| 45 | * and {@link LdapAuthoritiesPopulator}.</p> |
|
| 46 | * |
|
| 47 | * <h3>LdapAuthenticator</h3> |
|
| 48 | * This interface is responsible for performing the user authentication and retrieving |
|
| 49 | * the user's information from the directory. Example implementations are {@link |
|
| 50 | * org.acegisecurity.providers.ldap.authenticator.BindAuthenticator BindAuthenticator} which authenticates the user by |
|
| 51 | * "binding" as that user, and {@link org.acegisecurity.providers.ldap.authenticator.PasswordComparisonAuthenticator |
|
| 52 | * PasswordComparisonAuthenticator} which performs a comparison of the supplied password with the value stored in the |
|
| 53 | * directory, either by retrieving the password or performing an LDAP "compare" operation. |
|
| 54 | * <p>The task of retrieving the user attributes is delegated to the authenticator because the permissions on the |
|
| 55 | * attributes may depend on the type of authentication being used; for example, if binding as the user, it may be |
|
| 56 | * necessary to read them with the user's own permissions (using the same context used for the bind operation).</p> |
|
| 57 | * |
|
| 58 | * <h3>LdapAuthoritiesPopulator</h3> |
|
| 59 | * Once the user has been authenticated, this interface is called to obtain the set of granted authorities for the |
|
| 60 | * user. |
|
| 61 | * The |
|
| 62 | * {@link org.acegisecurity.providers.ldap.populator.DefaultLdapAuthoritiesPopulator DefaultLdapAuthoritiesPopulator} |
|
| 63 | * can be configured to obtain user role information from the user's attributes and/or to perform a search for |
|
| 64 | * "groups" that the user is a member of and map these to roles. |
|
| 65 | * |
|
| 66 | * <p>A custom implementation could obtain the roles from a completely different source, for example from a database. |
|
| 67 | * </p> |
|
| 68 | * |
|
| 69 | * <h3>Configuration</h3> |
|
| 70 | * |
|
| 71 | * A simple configuration might be as follows: |
|
| 72 | * <pre> |
|
| 73 | * <bean id="initialDirContextFactory" class="org.acegisecurity.providers.ldap.DefaultInitialDirContextFactory"> |
|
| 74 | * <constructor-arg value="ldap://monkeymachine:389/dc=acegisecurity,dc=org"/> |
|
| 75 | * <property name="managerDn"><value>cn=manager,dc=acegisecurity,dc=org</value></property> |
|
| 76 | * <property name="managerPassword"><value>password</value></property> |
|
| 77 | * </bean> |
|
| 78 | * |
|
| 79 | * <bean id="ldapAuthProvider" class="org.acegisecurity.providers.ldap.LdapAuthenticationProvider"> |
|
| 80 | * <constructor-arg> |
|
| 81 | * <bean class="org.acegisecurity.providers.ldap.authenticator.BindAuthenticator"> |
|
| 82 | * <constructor-arg><ref local="initialDirContextFactory"/></constructor-arg> |
|
| 83 | * <property name="userDnPatterns"><list><value>uid={0},ou=people</value></list></property> |
|
| 84 | * </bean> |
|
| 85 | * </constructor-arg> |
|
| 86 | * <constructor-arg> |
|
| 87 | * <bean class="org.acegisecurity.providers.ldap.populator.DefaultLdapAuthoritiesPopulator"> |
|
| 88 | * <constructor-arg><ref local="initialDirContextFactory"/></constructor-arg> |
|
| 89 | * <constructor-arg><value>ou=groups</value></constructor-arg> |
|
| 90 | * <property name="groupRoleAttribute"><value>ou</value></property> |
|
| 91 | * </bean> |
|
| 92 | * </constructor-arg> |
|
| 93 | * </bean></pre> |
|
| 94 | * |
|
| 95 | * <p>This would set up the provider to access an LDAP server with URL |
|
| 96 | * <tt>ldap://monkeymachine:389/dc=acegisecurity,dc=org</tt>. Authentication will be performed by attempting to bind |
|
| 97 | * with the DN <tt>uid=<user-login-name>,ou=people,dc=acegisecurity,dc=org</tt>. After successful |
|
| 98 | * authentication, roles will be assigned to the user by searching under the DN |
|
| 99 | * <tt>ou=groups,dc=acegisecurity,dc=org</tt> with the default filter <tt>(member=<user's-DN>)</tt>. The role |
|
| 100 | * name will be taken from the "ou" attribute of each match.</p> |
|
| 101 | * <p> |
|
| 102 | * The authenticate method will reject empty passwords outright. LDAP servers may allow an anonymous |
|
| 103 | * bind operation with an empty password, even if a DN is supplied. In practice this means that if |
|
| 104 | * the LDAP directory is configured to allow unauthenitcated access, it might be possible to |
|
| 105 | * authenticate as <i>any</i> user just by supplying an empty password. |
|
| 106 | * More information on the misuse of unauthenticated access can be found in |
|
| 107 | * <a href="http://www.ietf.org/internet-drafts/draft-ietf-ldapbis-authmeth-19.txt"> |
|
| 108 | * draft-ietf-ldapbis-authmeth-19.txt</a>. |
|
| 109 | * </p> |
|
| 110 | * |
|
| 111 | * @author Luke Taylor |
|
| 112 | * @version $Id: LdapAuthenticationProvider.java 1995 2007-08-30 21:12:16Z luke_t $ |
|
| 113 | * |
|
| 114 | * @see org.acegisecurity.providers.ldap.authenticator.BindAuthenticator |
|
| 115 | * @see org.acegisecurity.providers.ldap.populator.DefaultLdapAuthoritiesPopulator |
|
| 116 | */ |
|
| 117 | public class LdapAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider { |
|
| 118 | //~ Static fields/initializers ===================================================================================== |
|
| 119 | ||
| 120 | 2 | private static final Log logger = LogFactory.getLog(LdapAuthenticationProvider.class); |
| 121 | ||
| 122 | //~ Instance fields ================================================================================================ |
|
| 123 | ||
| 124 | private LdapAuthenticator authenticator; |
|
| 125 | private LdapAuthoritiesPopulator authoritiesPopulator; |
|
| 126 | 5 | private boolean includeDetailsObject = true; |
| 127 | ||
| 128 | //~ Constructors =================================================================================================== |
|
| 129 | ||
| 130 | /** |
|
| 131 | * Create an instance with the supplied authenticator and authorities populator implementations. |
|
| 132 | * |
|
| 133 | * @param authenticator the authentication strategy (bind, password comparison, etc) |
|
| 134 | * to be used by this provider for authenticating users. |
|
| 135 | * @param authoritiesPopulator the strategy for obtaining the authorities for a given user after they've been |
|
| 136 | * authenticated. |
|
| 137 | */ |
|
| 138 | 3 | public LdapAuthenticationProvider(LdapAuthenticator authenticator, LdapAuthoritiesPopulator authoritiesPopulator) { |
| 139 | 3 | this.setAuthenticator(authenticator); |
| 140 | 3 | this.setAuthoritiesPopulator(authoritiesPopulator); |
| 141 | 3 | } |
| 142 | ||
| 143 | /** |
|
| 144 | * Creates an instance with the supplied authenticator and a null authorities populator. |
|
| 145 | * In this case, the authorities must be mapped from the user context. |
|
| 146 | * |
|
| 147 | * @param authenticator the authenticator strategy. |
|
| 148 | */ |
|
| 149 | 2 | public LdapAuthenticationProvider(LdapAuthenticator authenticator) { |
| 150 | 2 | this.setAuthenticator(authenticator); |
| 151 | 2 | this.setAuthoritiesPopulator(new NullAuthoritiesPopulator()); |
| 152 | 2 | } |
| 153 | ||
| 154 | //~ Methods ======================================================================================================== |
|
| 155 | ||
| 156 | private void setAuthenticator(LdapAuthenticator authenticator) { |
|
| 157 | 5 | Assert.notNull(authenticator, "An LdapAuthenticator must be supplied"); |
| 158 | 5 | this.authenticator = authenticator; |
| 159 | 5 | } |
| 160 | ||
| 161 | private LdapAuthenticator getAuthenticator() { |
|
| 162 | 3 | return authenticator; |
| 163 | } |
|
| 164 | ||
| 165 | private void setAuthoritiesPopulator(LdapAuthoritiesPopulator authoritiesPopulator) { |
|
| 166 | 5 | Assert.notNull(authoritiesPopulator, "An LdapAuthoritiesPopulator must be supplied"); |
| 167 | 5 | this.authoritiesPopulator = authoritiesPopulator; |
| 168 | 5 | } |
| 169 | ||
| 170 | protected LdapAuthoritiesPopulator getAuthoritiesPopulator() { |
|
| 171 | 4 | return authoritiesPopulator; |
| 172 | } |
|
| 173 | ||
| 174 | protected void additionalAuthenticationChecks(UserDetails userDetails, |
|
| 175 | UsernamePasswordAuthenticationToken authentication) |
|
| 176 | throws AuthenticationException { |
|
| 177 | 2 | if (!userDetails.getPassword().equals(authentication.getCredentials().toString())) { |
| 178 | 1 | throw new BadCredentialsException(messages.getMessage( |
| 179 | "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"), |
|
| 180 | includeDetailsObject ? userDetails : null); |
|
| 181 | } |
|
| 182 | 1 | } |
| 183 | ||
| 184 | /** |
|
| 185 | * Creates the final <tt>UserDetails</tt> object that will be returned by the provider once the user has |
|
| 186 | * been authenticated.<p>The <tt>LdapAuthoritiesPopulator</tt> will be used to create the granted |
|
| 187 | * authorites for the user.</p> |
|
| 188 | * <p>Can be overridden to customize the creation of the final UserDetails instance. The default will |
|
| 189 | * merge any additional authorities retrieved from the populator with the propertis of original <tt>ldapUser</tt> |
|
| 190 | * object and set the values of the username and password.</p> |
|
| 191 | * |
|
| 192 | * @param ldapUser The intermediate LdapUserDetails instance returned by the authenticator. |
|
| 193 | * @param username the username submitted to the provider |
|
| 194 | * @param password the password submitted to the provider |
|
| 195 | * |
|
| 196 | * @return The UserDetails for the successfully authenticated user. |
|
| 197 | */ |
|
| 198 | protected UserDetails createUserDetails(LdapUserDetails ldapUser, String username, String password) { |
|
| 199 | 3 | LdapUserDetailsImpl.Essence user = new LdapUserDetailsImpl.Essence(ldapUser); |
| 200 | 3 | user.setUsername(username); |
| 201 | 3 | user.setPassword(password); |
| 202 | ||
| 203 | 3 | GrantedAuthority[] extraAuthorities = getAuthoritiesPopulator().getGrantedAuthorities(ldapUser); |
| 204 | ||
| 205 | 5 | for (int i = 0; i < extraAuthorities.length; i++) { |
| 206 | 2 | user.addAuthority(extraAuthorities[i]); |
| 207 | } |
|
| 208 | ||
| 209 | 3 | return user.createUserDetails(); |
| 210 | } |
|
| 211 | ||
| 212 | protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) |
|
| 213 | throws AuthenticationException { |
|
| 214 | 6 | if (!StringUtils.hasLength(username)) { |
| 215 | 2 | throw new BadCredentialsException(messages.getMessage("LdapAuthenticationProvider.emptyUsername", |
| 216 | "Empty Username")); |
|
| 217 | } |
|
| 218 | ||
| 219 | 4 | if (logger.isDebugEnabled()) { |
| 220 | 0 | logger.debug("Retrieving user " + username); |
| 221 | } |
|
| 222 | ||
| 223 | 4 | String password = (String) authentication.getCredentials(); |
| 224 | 4 | Assert.notNull(password, "Null password was supplied in authentication token"); |
| 225 | ||
| 226 | 4 | if (password.length() == 0) { |
| 227 | 1 | logger.debug("Rejecting empty password for user " + username); |
| 228 | 1 | throw new BadCredentialsException(messages.getMessage("LdapAuthenticationProvider.emptyPassword", |
| 229 | "Empty Password")); |
|
| 230 | } |
|
| 231 | ||
| 232 | try { |
|
| 233 | 3 | LdapUserDetails ldapUser = getAuthenticator().authenticate(username, password); |
| 234 | ||
| 235 | 3 | return createUserDetails(ldapUser, username, password); |
| 236 | ||
| 237 | 0 | } catch (DataAccessException ldapAccessFailure) { |
| 238 | 0 | throw new AuthenticationServiceException(ldapAccessFailure.getMessage(), ldapAccessFailure); |
| 239 | } |
|
| 240 | } |
|
| 241 | ||
| 242 | public boolean isIncludeDetailsObject() { |
|
| 243 | 0 | return includeDetailsObject; |
| 244 | } |
|
| 245 | ||
| 246 | public void setIncludeDetailsObject(boolean includeDetailsObject) { |
|
| 247 | 0 | this.includeDetailsObject = includeDetailsObject; |
| 248 | 0 | } |
| 249 | ||
| 250 | //~ Inner Classes ================================================================================================== |
|
| 251 | ||
| 252 | 4 | private static class NullAuthoritiesPopulator implements LdapAuthoritiesPopulator { |
| 253 | public GrantedAuthority[] getGrantedAuthorities(LdapUserDetails userDetails) throws LdapDataAccessException { |
|
| 254 | 1 | return new GrantedAuthority[0]; |
| 255 | } |
|
| 256 | } |
|
| 257 | } |
|
| 258 |