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 &lt;[EMAIL PROTECTED]&gt;
 *
 */
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 

Reply via email to