blob: efa4cee41950766870573bb2e352496d0826a5c4 [file] [log] [blame]
package org.apache.archiva.redback.users.ldap.ctl;
/*
* 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.common.ldap.MappingException;
import org.apache.archiva.redback.common.ldap.user.LdapUser;
import org.apache.archiva.redback.common.ldap.user.LdapUserMapper;
import org.apache.archiva.redback.common.ldap.user.UserMapper;
import org.apache.archiva.redback.configuration.UserConfiguration;
import org.apache.archiva.redback.configuration.UserConfigurationKeys;
import org.apache.archiva.redback.policy.PasswordEncoder;
import org.apache.archiva.redback.policy.encoders.SHA1PasswordEncoder;
import org.apache.archiva.redback.users.User;
import org.apache.archiva.redback.users.UserManager;
import org.apache.archiva.redback.users.ldap.LdapUserQuery;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.inject.Named;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author jesse
*/
@Service
public class DefaultLdapController
implements LdapController
{
private Logger log = LoggerFactory.getLogger( getClass() );
@Inject
@Named(value = "userMapper#ldap")
private UserMapper mapper;
@Inject
@Named( value = "userConfiguration#default" )
private UserConfiguration userConf;
private boolean writableLdap = false;
private PasswordEncoder passwordEncoder;
private String baseDn;
private String groupsDn;
private String ldapGroupClass = "groupOfUniqueNames";
@PostConstruct
public void initialize()
{
this.writableLdap = userConf.getBoolean( UserConfigurationKeys.LDAP_WRITABLE, this.writableLdap );
this.baseDn = userConf.getConcatenatedList( UserConfigurationKeys.LDAP_BASEDN, null );
this.passwordEncoder = new SHA1PasswordEncoder();
this.groupsDn = userConf.getConcatenatedList( UserConfigurationKeys.LDAP_GROUPS_BASEDN, this.groupsDn );
this.ldapGroupClass = userConf.getString( UserConfigurationKeys.LDAP_GROUPS_CLASS, this.ldapGroupClass );
}
/**
* @see org.apache.archiva.redback.users.ldap.ctl.LdapController#removeUser(String, javax.naming.directory.DirContext)
*/
public void removeUser( String principal, DirContext context )
throws LdapControllerException
{
// no op
}
/**
* @see org.apache.archiva.redback.users.ldap.ctl.LdapController#updateUser(org.apache.archiva.redback.users.User, javax.naming.directory.DirContext)
*/
public void updateUser( User user, DirContext context )
throws LdapControllerException, MappingException
{
// no op
}
/**
* @see org.apache.archiva.redback.users.ldap.ctl.LdapController#userExists(String, javax.naming.directory.DirContext)
*/
public boolean userExists( String key, DirContext context )
throws LdapControllerException
{
NamingEnumeration<SearchResult> results = null;
try
{
results = searchUsers( key, context );
return results.hasMoreElements();
}
catch ( NamingException e )
{
throw new LdapControllerException( "Error searching for the existence of user: " + key, e );
}
finally
{
if ( results != null )
{
try
{
results.close();
}
catch ( NamingException e )
{
log.warn( "Error closing search results", e );
}
}
}
}
protected NamingEnumeration<SearchResult> searchUsers( String key, DirContext context )
throws NamingException
{
LdapUserQuery query = new LdapUserQuery();
query.setUsername( key );
return searchUsers( context, null, query );
}
protected NamingEnumeration<SearchResult> searchUsers( DirContext context )
throws NamingException
{
return searchUsers( context, null, null );
}
protected NamingEnumeration<SearchResult> searchUsers( DirContext context, String[] returnAttributes )
throws NamingException
{
return searchUsers( context, returnAttributes, null );
}
protected NamingEnumeration<SearchResult> searchUsers( DirContext context, String[] returnAttributes,
LdapUserQuery query )
throws NamingException
{
if ( query == null )
{
query = new LdapUserQuery();
}
SearchControls ctls = new SearchControls();
ctls.setDerefLinkFlag( true );
ctls.setSearchScope( SearchControls.SUBTREE_SCOPE );
ctls.setReturningAttributes( mapper.getReturningAttributes() );
ctls.setCountLimit( ( (LdapUserMapper) mapper ).getMaxResultCount() );
String finalFilter = new StringBuilder( "(&(objectClass=" + mapper.getUserObjectClass() + ")" ).append(
( mapper.getUserFilter() != null ? mapper.getUserFilter() : "" ) ).append(
query.getLdapFilter( mapper ) + ")" ).toString();
log.debug( "Searching for users with filter: '{}' from base dn: {}", finalFilter, mapper.getUserBaseDn() );
return context.search( mapper.getUserBaseDn(), finalFilter, ctls );
}
/**
* @see org.apache.archiva.redback.users.ldap.ctl.LdapController#getUsers(javax.naming.directory.DirContext)
*/
public Collection<User> getUsers( DirContext context )
throws LdapControllerException, MappingException
{
NamingEnumeration<SearchResult> results = null;
try
{
results = searchUsers( context, null, null );
Set<User> users = new LinkedHashSet<User>();
while ( results.hasMoreElements() )
{
SearchResult result = results.nextElement();
users.add( mapper.getUser( result.getNameInNamespace(), result.getAttributes() ) );
}
return users;
}
catch ( NamingException e )
{
String message = "Failed to retrieve ldap information for users.";
throw new LdapControllerException( message, e );
}
finally
{
if ( results != null )
{
try
{
results.close();
}
catch ( NamingException e )
{
log.warn( "failed to close search results", e );
}
}
}
}
/**
* @see org.apache.archiva.redback.users.ldap.ctl.LdapController#getUsersByQuery(org.apache.archiva.redback.users.ldap.LdapUserQuery, javax.naming.directory.DirContext)
*/
public List<User> getUsersByQuery( LdapUserQuery query, DirContext context )
throws LdapControllerException, MappingException
{
NamingEnumeration<SearchResult> results = null;
try
{
results = searchUsers( context, null, query );
List<User> users = new LinkedList<User>();
while ( results.hasMoreElements() )
{
SearchResult result = results.nextElement();
users.add( mapper.getUser( result.getNameInNamespace(), result.getAttributes() ) );
}
return users;
}
catch ( NamingException e )
{
String message = "Failed to retrieve ldap information for users.";
throw new LdapControllerException( message, e );
}
finally
{
if ( results != null )
{
try
{
results.close();
}
catch ( NamingException e )
{
log.warn( "failed to close search results", e );
}
}
}
}
/**
* @see org.apache.archiva.redback.users.ldap.ctl.LdapController#createUser(org.apache.archiva.redback.users.User, javax.naming.directory.DirContext, boolean)
*/
public void createUser( User user, DirContext context, boolean encodePasswordIfChanged )
throws LdapControllerException, MappingException
{
if ( user == null )
{
return;
}
if ( user.getUsername().equals( UserManager.GUEST_USERNAME ) )
{
log.warn( "skip user '{}' creation" );
//We don't store guest
return;
}
boolean userExists = userExists( user.getUsername(), context );
if ( userExists )
{
log.debug( "user '{}' exists skip creation", user.getUsername() );
return;
}
if ( writableLdap )
{
try
{
bindUserObject( context, user );
log.info( "user {} created in ldap", user.getUsername() );
}
catch ( NamingException e )
{
throw new LdapControllerException( e.getMessage(), e );
}
}
}
private void bindUserObject( DirContext context, User user )
throws NamingException
{
Attributes attributes = new BasicAttributes( true );
BasicAttribute objectClass = new BasicAttribute( "objectClass" );
objectClass.add( "top" );
objectClass.add( "inetOrgPerson" );
objectClass.add( "person" );
objectClass.add( "organizationalperson" );
attributes.put( objectClass );
attributes.put( "cn", user.getUsername() );
attributes.put( "sn", "foo" );
if ( StringUtils.isNotEmpty( user.getEmail() ) )
{
attributes.put( "mail", user.getEmail() );
}
if ( userConf.getBoolean( UserConfigurationKeys.LDAP_BIND_AUTHENTICATOR_ALLOW_EMPTY_PASSWORDS, false )
&& StringUtils.isNotEmpty( user.getPassword() ) )
{
attributes.put( "userPassword", passwordEncoder.encodePassword( user.getPassword() ) );
}
attributes.put( "givenName", "foo" );
context.createSubcontext( "cn=" + user.getUsername() + "," + this.getBaseDn(), attributes );
}
/**
* @see org.apache.archiva.redback.users.ldap.ctl.LdapController#getUser(String, javax.naming.directory.DirContext)
*/
public LdapUser getUser( String username, DirContext context )
throws LdapControllerException, MappingException
{
log.debug( "Searching for user: {}", username );
LdapUserQuery query = new LdapUserQuery();
query.setUsername( username );
NamingEnumeration<SearchResult> result = null;
try
{
result = searchUsers( context, null, query );
if ( result.hasMoreElements() )
{
SearchResult next = result.nextElement();
log.info( "Found user: {}", username );
return mapper.getUser( next.getNameInNamespace(), next.getAttributes() );
}
else
{
return null;
}
}
catch ( NamingException e )
{
String message = "Failed to retrieve information for user: " + username;
throw new LdapControllerException( message, e );
}
finally
{
if ( result != null )
{
try
{
result.close();
}
catch ( NamingException e )
{
log.warn( "failed to close search results", e );
}
}
}
}
public Map<String, Collection<String>> findUsersWithRoles( DirContext dirContext )
throws LdapControllerException
{
Map<String, Collection<String>> usersWithRoles = new HashMap<String, Collection<String>>();
NamingEnumeration<SearchResult> namingEnumeration = null;
try
{
SearchControls searchControls = new SearchControls();
searchControls.setDerefLinkFlag( true );
searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE );
String filter = "objectClass=" + getLdapGroupClass();
namingEnumeration = dirContext.search( getGroupsDn(), filter, searchControls );
while ( namingEnumeration.hasMore() )
{
SearchResult searchResult = namingEnumeration.next();
String groupName = searchResult.getName();
// cn=blabla we only want bla bla
groupName = StringUtils.substringAfter( groupName, "=" );
Attribute uniqueMemberAttr = searchResult.getAttributes().get( "uniquemember" );
if ( uniqueMemberAttr != null )
{
NamingEnumeration<?> allMembersEnum = uniqueMemberAttr.getAll();
while ( allMembersEnum.hasMore() )
{
String userName = allMembersEnum.next().toString();
// uid=blabla we only want bla bla
userName = StringUtils.substringAfter( userName, "=" );
userName = StringUtils.substringBefore( userName, "," );
Collection<String> roles = usersWithRoles.get( userName );
if ( roles == null )
{
roles = new HashSet<String>();
}
roles.add( groupName );
usersWithRoles.put( userName, roles );
}
}
log.debug( "found groupName: '{}' with users: {}", groupName );
}
return usersWithRoles;
}
catch ( NamingException e )
{
throw new LdapControllerException( e.getMessage(), e );
}
finally
{
if ( namingEnumeration != null )
{
try
{
namingEnumeration.close();
}
catch ( NamingException e )
{
log.warn( "failed to close search results", e );
}
}
}
}
//-----------------------------
// setters/getters
//-----------------------------
public UserMapper getMapper()
{
return mapper;
}
public void setMapper( UserMapper mapper )
{
this.mapper = mapper;
}
public UserConfiguration getUserConf()
{
return userConf;
}
public void setUserConf( UserConfiguration userConf )
{
this.userConf = userConf;
}
public boolean isWritableLdap()
{
return writableLdap;
}
public void setWritableLdap( boolean writableLdap )
{
this.writableLdap = writableLdap;
}
public PasswordEncoder getPasswordEncoder()
{
return passwordEncoder;
}
public void setPasswordEncoder( PasswordEncoder passwordEncoder )
{
this.passwordEncoder = passwordEncoder;
}
public String getBaseDn()
{
return baseDn;
}
public void setBaseDn( String baseDn )
{
this.baseDn = baseDn;
}
public String getGroupsDn()
{
return groupsDn;
}
public void setGroupsDn( String groupsDn )
{
this.groupsDn = groupsDn;
}
public String getLdapGroupClass()
{
return ldapGroupClass;
}
public void setLdapGroupClass( String ldapGroupClass )
{
this.ldapGroupClass = ldapGroupClass;
}
}