1 package org.apache.archiva.redback.authentication.ldap;
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
22 import org.apache.archiva.redback.authentication.AbstractAuthenticator;
23 import org.apache.archiva.redback.common.ldap.user.UserMapper;
24 import org.apache.archiva.redback.common.ldap.connection.LdapConnectionFactory;
25 import org.apache.archiva.redback.configuration.UserConfiguration;
26 import org.apache.archiva.redback.configuration.UserConfigurationKeys;
27 import org.apache.commons.lang.StringUtils;
28 import org.apache.archiva.redback.authentication.AuthenticationDataSource;
29 import org.apache.archiva.redback.authentication.AuthenticationException;
30 import org.apache.archiva.redback.authentication.AuthenticationResult;
31 import org.apache.archiva.redback.authentication.Authenticator;
32 import org.apache.archiva.redback.authentication.PasswordBasedAuthenticationDataSource;
33 import org.apache.archiva.redback.common.ldap.connection.LdapConnection;
34 import org.apache.archiva.redback.common.ldap.connection.LdapException;
35 import org.apache.archiva.redback.users.ldap.service.LdapCacheService;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38 import org.springframework.stereotype.Service;
40 import javax.inject.Inject;
41 import javax.inject.Named;
42 import javax.naming.NamingEnumeration;
43 import javax.naming.NamingException;
44 import javax.naming.directory.DirContext;
45 import javax.naming.directory.SearchControls;
46 import javax.naming.directory.SearchResult;
49 * LdapBindAuthenticator:
51 * @author: Jesse McConnell <jesse@codehaus.org>
53 @Service( "authenticator#ldap" )
54 public class LdapBindAuthenticator
55 extends AbstractAuthenticator
56 implements Authenticator
59 private Logger log = LoggerFactory.getLogger( getClass() );
62 @Named( value = "userMapper#ldap" )
63 private UserMapper mapper;
66 @Named( value = "ldapConnectionFactory#configurable" )
67 private LdapConnectionFactory connectionFactory;
70 @Named( value = "userConfiguration#default" )
71 private UserConfiguration config;
74 private LdapCacheService ldapCacheService;
78 return "LdapBindAuthenticator";
81 public AuthenticationResult authenticate( AuthenticationDataSource s )
82 throws AuthenticationException
84 PasswordBasedAuthenticationDataSource source = (PasswordBasedAuthenticationDataSource) s;
86 if ( !config.getBoolean( UserConfigurationKeys.LDAP_BIND_AUTHENTICATOR_ENABLED ) || (
87 !config.getBoolean( UserConfigurationKeys.LDAP_BIND_AUTHENTICATOR_ALLOW_EMPTY_PASSWORDS, false )
88 && StringUtils.isEmpty( source.getPassword() ) ) )
90 return new AuthenticationResult( false, source.getPrincipal(), null );
93 SearchControls ctls = new SearchControls();
95 ctls.setCountLimit( 1 );
97 ctls.setDerefLinkFlag( true );
98 ctls.setSearchScope( SearchControls.SUBTREE_SCOPE );
100 String filter = "(&(objectClass=" + mapper.getUserObjectClass() + ")" + ( mapper.getUserFilter() != null
101 ? mapper.getUserFilter()
102 : "" ) + "(" + mapper.getUserIdAttribute() + "=" + source.getPrincipal() + "))";
104 log.debug( "Searching for users with filter: '{}' from base dn: {}", filter, mapper.getUserBaseDn() );
106 LdapConnection ldapConnection = null;
107 LdapConnection authLdapConnection = null;
108 NamingEnumeration<SearchResult> results = null;
111 ldapConnection = getLdapConnection();
112 // check the cache for user's userDn in the ldap server
113 String userDn = ldapCacheService.getLdapUserDn( source.getPrincipal() );
115 if ( userDn == null )
117 log.debug( "userDn for user {} not found in cache. Retrieving from ldap server..",
118 source.getPrincipal() );
120 DirContext context = ldapConnection.getDirContext();
122 results = context.search( mapper.getUserBaseDn(), filter, ctls );
124 log.debug( "Found user '{}': {}", source.getPrincipal(), results.hasMoreElements() );
126 if ( results.hasMoreElements() )
128 SearchResult result = results.nextElement();
130 userDn = result.getNameInNamespace();
132 log.debug( "Adding userDn {} for user {} to the cache..", userDn, source.getPrincipal() );
134 // REDBACK-289/MRM-1488 cache the ldap user's userDn to lessen calls to ldap server
135 ldapCacheService.addLdapUserDn( source.getPrincipal(), userDn );
139 return new AuthenticationResult( false, source.getPrincipal(), null );
143 log.debug( "Attempting Authenication: {}", userDn );
145 authLdapConnection = connectionFactory.getConnection( userDn, source.getPassword() );
147 log.info( "user '{}' authenticated", source.getPrincipal() );
149 return new AuthenticationResult( true, source.getPrincipal(), null );
151 catch ( LdapException e )
153 return new AuthenticationResult( false, source.getPrincipal(), e );
155 catch ( NamingException e )
157 return new AuthenticationResult( false, source.getPrincipal(), e );
161 closeNamingEnumeration( results );
162 closeLdapConnection( ldapConnection );
163 if ( authLdapConnection != null )
165 closeLdapConnection( authLdapConnection );
170 public boolean supportsDataSource( AuthenticationDataSource source )
172 return ( source instanceof PasswordBasedAuthenticationDataSource );
175 private LdapConnection getLdapConnection()
178 return connectionFactory.getConnection();
181 private void closeLdapConnection( LdapConnection ldapConnection )
183 if ( ldapConnection != null )
185 ldapConnection.close();
189 private void closeNamingEnumeration( NamingEnumeration<SearchResult> results )
193 if ( results != null )
198 catch ( NamingException e )
200 log.warn( "skip exception closing naming search result " + e.getMessage() );