blob: c79fb82876cfdca90b12ee70751a37b421211e3d [file] [log] [blame]
package org.apache.archiva.redback.authentication.ldap;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
import org.apache.archiva.redback.authentication.AbstractAuthenticator;
import org.apache.archiva.redback.common.ldap.connection.DefaultLdapConnection;
import org.apache.archiva.redback.common.ldap.connection.LdapConnection;
import org.apache.archiva.redback.common.ldap.user.UserMapper;
import org.apache.archiva.redback.common.ldap.connection.LdapConnectionFactory;
import org.apache.archiva.redback.configuration.UserConfiguration;
import org.apache.archiva.redback.configuration.UserConfigurationKeys;
import org.apache.commons.lang.StringUtils;
import org.apache.archiva.redback.authentication.AuthenticationDataSource;
import org.apache.archiva.redback.authentication.AuthenticationException;
import org.apache.archiva.redback.authentication.AuthenticationResult;
import org.apache.archiva.redback.authentication.Authenticator;
import org.apache.archiva.redback.authentication.PasswordBasedAuthenticationDataSource;
import org.apache.archiva.redback.common.ldap.connection.LdapException;
import org.apache.archiva.redback.users.ldap.service.LdapCacheService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.inject.Inject;
import javax.inject.Named;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
/**
* LdapBindAuthenticator:
*
* @author: Jesse McConnell
*/
@Service( "authenticator#ldap" )
public class LdapBindAuthenticator
extends AbstractAuthenticator
implements Authenticator
{
private Logger log = LoggerFactory.getLogger( getClass() );
@Inject
@Named( value = "userMapper#ldap" )
private UserMapper mapper;
@Inject
@Named( value = "ldapConnectionFactory#configurable" )
private LdapConnectionFactory connectionFactory;
@Inject
@Named( value = "userConfiguration#default" )
private UserConfiguration config;
@Inject
private LdapCacheService ldapCacheService;
public String getId()
{
return "LdapBindAuthenticator";
}
public AuthenticationResult authenticate( AuthenticationDataSource s )
throws AuthenticationException
{
PasswordBasedAuthenticationDataSource source = (PasswordBasedAuthenticationDataSource) s;
if ( !config.getBoolean( UserConfigurationKeys.LDAP_BIND_AUTHENTICATOR_ENABLED ) || (
!config.getBoolean( UserConfigurationKeys.LDAP_BIND_AUTHENTICATOR_ALLOW_EMPTY_PASSWORDS, false )
&& StringUtils.isEmpty( source.getPassword() ) ) )
{
return new AuthenticationResult( false, source.getUsername(), null );
}
SearchControls ctls = new SearchControls();
ctls.setCountLimit( 1 );
ctls.setDerefLinkFlag( true );
ctls.setSearchScope( SearchControls.SUBTREE_SCOPE );
String filter = "(&(objectClass=" + mapper.getUserObjectClass() + ")" + ( mapper.getUserFilter() != null
? mapper.getUserFilter()
: "" ) + "(" + mapper.getUserIdAttribute() + "=" + source.getUsername() + "))";
log.debug( "Searching for users with filter: '{}' from base dn: {}", filter, mapper.getUserBaseDn() );
LdapConnection ldapConnection = null;
LdapConnection authLdapConnection = null;
NamingEnumeration<SearchResult> results = null;
try
{
ldapConnection = getLdapConnection();
// check the cache for user's userDn in the ldap server
String userDn = ldapCacheService.getLdapUserDn( source.getUsername() );
if ( userDn == null )
{
log.debug( "userDn for user {} not found in cache. Retrieving from ldap server..",
source.getUsername() );
DirContext context = ldapConnection.getDirContext();
results = context.search( mapper.getUserBaseDn(), filter, ctls );
log.debug( "Found user '{}': {}", source.getUsername(), results.hasMoreElements() );
if ( results.hasMoreElements() )
{
SearchResult result = results.nextElement();
userDn = result.getNameInNamespace();
log.debug( "Adding userDn {} for user {} to the cache..", userDn, source.getUsername() );
// REDBACK-289/MRM-1488 cache the ldap user's userDn to lessen calls to ldap server
ldapCacheService.addLdapUserDn( source.getUsername(), userDn );
}
else
{
return new AuthenticationResult( false, source.getUsername(), null );
}
}
log.debug( "Attempting Authenication: {}", userDn );
authLdapConnection = connectionFactory.getConnection( userDn, source.getPassword() );
log.info( "user '{}' authenticated", source.getUsername() );
return new AuthenticationResult( true, source.getUsername(), null );
}
catch ( LdapException e )
{
return new AuthenticationResult( false, source.getUsername(), e );
}
catch ( NamingException e )
{
return new AuthenticationResult( false, source.getUsername(), e );
}
finally
{
closeNamingEnumeration( results );
closeLdapConnection( ldapConnection );
if ( authLdapConnection != null )
{
closeLdapConnection( authLdapConnection );
}
}
}
public boolean supportsDataSource( AuthenticationDataSource source )
{
return ( source instanceof PasswordBasedAuthenticationDataSource );
}
private LdapConnection getLdapConnection()
throws LdapException
{
return connectionFactory.getConnection();
}
private void closeLdapConnection( LdapConnection ldapConnection )
{
if ( ldapConnection != null )
{
ldapConnection.close();
}
}
private void closeNamingEnumeration( NamingEnumeration<SearchResult> results )
{
try
{
if ( results != null )
{
results.close();
}
}
catch ( NamingException e )
{
log.warn( "skip exception closing naming search result {}", e.getMessage() );
}
}
}