/* (c) 2014 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.security.ldap; import java.text.MessageFormat; import javax.naming.directory.DirContext; import javax.naming.directory.SearchControls; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.ldap.NamingException; import org.springframework.ldap.core.DirContextOperations; import org.springframework.ldap.core.support.BaseLdapPathContextSource; import org.springframework.ldap.support.LdapUtils; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.ldap.SpringSecurityLdapTemplate; import org.springframework.security.ldap.authentication.BindAuthenticator; import org.springframework.security.ldap.ppolicy.PasswordPolicyControl; import org.springframework.security.ldap.ppolicy.PasswordPolicyControlExtractor; import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** * Extended BindAuthenticator using a filter to find user data as an alternative * to a direct dn access. * * @author "Mauro Bartolomeoli - mauro.bartolomeoli@geo-solutions.it" * */ public class GeoserverLdapBindAuthenticator extends BindAuthenticator { private static final Log logger = LogFactory .getLog(GeoserverLdapBindAuthenticator.class); private String userFilter = ""; private String userFormat = ""; public GeoserverLdapBindAuthenticator( BaseLdapPathContextSource contextSource) { super(contextSource); } public void setUserFilter(String userFilter) { this.userFilter = userFilter; } @Override public DirContextOperations authenticate(Authentication authentication) { if (userFilter == null || userFilter.equals("")) { // authenticate using dn return super.authenticate(authentication); } else { return authenticateUsingFilter(authentication); } } /** * If userFilter is defined we extract user data using the filter and * dnPattern (if defined) to transform username for authentication. * * @param authentication * */ protected DirContextOperations authenticateUsingFilter( Authentication authentication) { DirContextOperations user = null; Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication, "Can only process UsernamePasswordAuthenticationToken objects"); String username = authentication.getName(); String originalUser = username; String password = (String) authentication.getCredentials(); // format given username if required if (userFormat != null && !userFormat.equals("")) { username = MessageFormat.format(userFormat, username); } if (!StringUtils.hasLength(password)) { logger.debug("Rejecting empty password for user " + username); throw new BadCredentialsException(messages.getMessage( "BindAuthenticator.emptyPassword", "Empty Password")); } DirContext ctx = null; String userDnStr = ""; try { ctx = getContextSource().getContext(username, password); // Check for password policy control PasswordPolicyControl ppolicy = PasswordPolicyControlExtractor .extractControl(ctx); logger.debug("Retrieving user object using filter..."); SearchControls searchCtls = new SearchControls(); searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE); user = SpringSecurityLdapTemplate.searchForSingleEntryInternal(ctx, searchCtls, "", userFilter, new Object[] { username, originalUser }); userDnStr = user.getDn().toString(); if (ppolicy != null) { user.setAttributeValue(ppolicy.getID(), ppolicy); } } 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(userDnStr, username, e); } else { throw e; } } catch (javax.naming.NamingException e) { throw LdapUtils.convertLdapException(e); } finally { LdapUtils.closeContext(ctx); } if (user == null) { throw new BadCredentialsException(messages.getMessage( "BindAuthenticator.badCredentials", "Bad credentials")); } return user; } public void setUserFormat(String userFormat) { this.userFormat = userFormat; } }