Hi there,
I've made a simple modification to the LDAP auth code to make it behave
more like other ldap auth implementations.
There are some limitations caused the current implementation in that a
dn must be assembled by plugging in the username.
For example, uid=%s,dc=company,dc=com becomes
uid=barberd,dc=company,dc=com
LDAP Auth should instead by done with a two-step process: first connect
to ldap and search for the user's object using a base dn and a search
filter. Then take that result and attempt to bind as that object with
the supplied password.
For example:
Search 'dc=company,dc=com" for "(&(objectClass=person)(uid=%s))"
will return the dn of 'uid=barberd,dc=company,dc=com". Then take this
dn and try to log in with the supplied password.
This gives several advantages:
1) The uid does not have to be in the dn
There can be an ldap object such as "cn=Don Barber, dc=company, dc=com"
with an attribute of 'uid: barberdg'.
2) The user accounts no longer have to be children of the same node.
For example, one can have two users:
uid=user1, ou=Human Resources, ou=People, dc=company, dc=com
and
uid=user2, ou=Admins, ou=Information Technology, ou=People, dc=company,
dc=com
With a search base of 'ou=People, dc=company, dc=iip' the auth module
will still find the correct dn with a search for 'uid=user1' or
'uid=user2', no matter how the company decides to structure its user
accounts.
The modifications were pretty minor really.
For configuration changes, it gets rid of LDAP_USER_TEMPLATE and renames
LDAP_USER_FILTER to LDAP_USER_SEARCH.
I've attached the new file to this email. Thanks for considering it.
Don
/*
* SWAMP Workflow Administration and Management Platform
*
* Copyright (c) 2006 Thomas Schmidt <[EMAIL PROTECTED]>
* Copyright (c) 2006 Juergen Pabel <[EMAIL PROTECTED]>
*
* Copyright (c) 2006 Novell Inc.
* Copyright (c) 2006 Akkaya Consulting GmbH
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* $Id$
*/
package de.suse.swamp.core.security;
/**
* The LDAPUsermanager authenticates and fetches User
* from a LDAP server and stores them in the SWAMP DB.
*
* @author Thomas Schmidt <[EMAIL PROTECTED]>
*
*/
import java.util.*;
import javax.naming.CommunicationException;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.LdapContext;
import de.suse.swamp.util.*;
import de.suse.swamp.core.util.*;
import de.suse.swamp.core.container.*;
public class SWAMPLDAPUserManager implements UserManagerIface {
private String LDAP_BIND_URL;
private String LDAP_BIND_USER;
private String LDAP_BIND_PASS;
private String LDAP_USER_BASEDN;
private String LDAP_USER_SEARCH;
// LDAP environment
Properties env;
public SWAMPLDAPUserManager() {
LDAP_BIND_URL = SWAMP.getInstance().getProperty("LDAP_BIND_URL");
LDAP_BIND_USER = SWAMP.getInstance().getProperty("LDAP_BIND_USER");
LDAP_BIND_PASS = SWAMP.getInstance().getProperty("LDAP_BIND_PASS");
LDAP_USER_BASEDN = SWAMP.getInstance().getProperty("LDAP_USER_BASEDN");
LDAP_USER_SEARCH = SWAMP.getInstance().getProperty("LDAP_USER_SEARCH");
env = new Properties();
env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
env.put(LdapContext.CONTROL_FACTORIES, "com.sun.jndi.ldap.ControlFactory");
env.put( Context.PROVIDER_URL, LDAP_BIND_URL );
env.put(Context.STATE_FACTORIES, "PersonStateFactory");
env.put(Context.OBJECT_FACTORIES, "PersonObjectFactory");
// use a connection pool
env.put("com.sun.jndi.ldap.connect.pool", "true");
env.put("com.sun.jndi.ldap.connect.pool.maxsize", "20");
env.put("com.sun.jndi.ldap.connect.pool.prefsize", "10");
env.put("com.sun.jndi.ldap.connect.pool.timeout", "30000");
env.put("com.sun.jndi.connect.timeout", "4000");
// TCP timeout connecting to server
env.put("com.sun.jndi.ldap.connect.timeout", "4000");
// connection pool debug mode
env.put("com.sun.jndi.ldap.connect.pool.debug", "all");
}
/**
* Loads the user from db if already there, or tries to fetch from
* LDAP and store into the db then.
*/
public SWAMPUser loadUser(String userName) throws StorageException, UnknownElementException {
// load from db:
if (userName == null || userName.equals("")){
throw new NoSuchElementException("Tried to load user with empty name!");
}
SWAMPUser user = null;
if (userName.equalsIgnoreCase("anonymous")){
user = new SWAMPUser();
user.setUserName(userName);
} else {
user = SecurityStorage.loadUserFromDB(userName);
if (user == null) {
user = loadUserFromLDAP(userName);
if (user != null) {
SecurityStorage.storeUser(user);
} else {
throw new UnknownElementException("User " + userName +
"not found!");
}
}
}
return user;
}
private SWAMPUser loadUserFromLDAP(String userName)
throws StorageException, UnknownElementException {
SWAMPUser user = null;
Logger.DEBUG("Fetching user from ldap: " + userName);
try {
if (LDAP_BIND_USER != null && LDAP_BIND_PASS != null){
env.put( Context.SECURITY_PRINCIPAL, LDAP_BIND_USER );
env.put( Context.SECURITY_CREDENTIALS, LDAP_BIND_PASS );
}
DirContext ctx = new InitialDirContext( env );
String filter = LDAP_USER_SEARCH.replaceAll("%s", userName);
String[] attrIDs = {"givenName","sn","mail"};
SearchControls constraints = new SearchControls();
constraints.setReturningAttributes(attrIDs);
constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
NamingEnumeration answer = ctx.search( LDAP_USER_BASEDN, filter, constraints);
if (answer.hasMore()) {
SearchResult sr = (SearchResult)answer.next();
Attributes attrs = sr.getAttributes();
user = new SWAMPUser();
user.setUserName(userName);
user.setFirstName( extractLDAPValue(attrs,"givenName") );
user.setLastName( extractLDAPValue(attrs,"sn") );
user.setEmail( extractLDAPValue(attrs,"mail") );
}
ctx.close();
} catch ( Exception e ) {
// e.printStackTrace();
throw new StorageException( e.getMessage() );
}
if (user == null){
throw new UnknownElementException("No such user in LDAP: " + userName);
}
return user;
}
/**
* authenticates the user against an LDAP server.
* special users that have a password in the database get authenticated
* directly, so that we can add special users here if the
* LDAP server is not at our control.
*/
public void authenticateUser(String loginName, String password)
throws StorageException, UnknownElementException, PasswordException {
Logger.DEBUG("Trying to authenticate user: " + loginName);
// catch empty passwords:
if (password == null || loginName == null || password.equals("")){
throw new PasswordException("Cannot authenticate with empty pw or username!");
}
SWAMPUser user = loadUser(loginName);
if (user.getPasswordHash() != null && user.getPasswordHash().length() > 0) {
Logger.DEBUG("Switching to DB authentication for user: " + loginName);
SWAMPDBUserManager userManager = new SWAMPDBUserManager();
userManager.authenticateUser(loginName, password);
return;
} else {
String userDn = "";
try {
env.put( Context.SECURITY_PRINCIPAL, LDAP_BIND_USER );
env.put( Context.SECURITY_CREDENTIALS, LDAP_BIND_PASS );
DirContext ctx = new InitialDirContext( env );
String filter = LDAP_USER_SEARCH.replaceAll("%s", loginName);
String[] attrIDs = {};
SearchControls constraints = new SearchControls();
constraints.setReturningAttributes(attrIDs);
constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
NamingEnumeration answer = ctx.search( LDAP_USER_BASEDN, filter, constraints);
if (answer.hasMore()) {
SearchResult sr = (SearchResult)answer.next();
userDn = sr.getName() + ',' + LDAP_USER_BASEDN;
}
ctx.close();
} catch (Exception e) {
throw new UnknownElementException(e.getMessage());
}
Logger.DEBUG("Found dn for "+loginName+": " + userDn);
env.put( Context.SECURITY_PRINCIPAL, userDn);
env.put( Context.SECURITY_CREDENTIALS, password );
try {
DirContext userctx = new InitialDirContext( env );
userctx.close();
} catch (CommunicationException e) {
throw new StorageException("Error in communicating with LDAP server: " +
e.getMessage());
} catch (Exception e) {
throw new PasswordException(e.getMessage());
}
}
}
/**
* Get the LDAP value as string
*/
private String extractLDAPValue(Attributes attributes, String name)
throws NamingException {
String val = (String) attributes.get(name).get();
// TODO: do base64 decoding if needed
/*if( Base64.isBase64( val ) ) {
val = new String( Base64.decode( val.getBytes() ) );
}*/
return val;
}
}
-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2005.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
swamp-devel mailing list
swamp-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/swamp-devel
http://swamp.sf.net