Hello! I've put together a set of patches to the LDAPAuthentication code to get it working against Active Directory at Auburn University, support implicit-group member-ids in dspace.cfg, and add a JUnit regression test. I think the changes are backward compatable and generic, but I've only tested the code in my environment.
I hope that we can check this patch into the dspace repository. An overview and svn diff follow, and a zip file with the modified files is available here: http://erwg.lib.auburn.edu/dspace-ldap_20080828.zip Please take a look, and let me know what you think. Cheers, Reuben ----------------------------------- Changes under dspace-api org.dspace.authentication. *. Moved the LDAPAuthentication.SpeakerToLDAP nested class out to its own non-nested interface with a DefaultSpeakerToLDAP implementation. *. Refactored SpeakerToLDAP#ldapAuthenticate(...) to return a DataFromLDAP POJO data object rather than set object member variables. *. Implemented SpeakerToLDAPCase JUnit test-case and PackageTestSuite classes to support simple regression tests against SpeakerToLDAP implementations. Modified pom.xml so that 'mvn test' runs with a verbose log4j setting. *. Modified the way SpeakToLDAP handles the ldap.object_context dspace.cfg configuration property so that if the ldap.object_context matches 'WINDOWS_DOMAIN:DOMAIN_NAME', then LDAP attempts to bind with 'DOMAIN_NAME\NETID' rather than 'cn=NETID,ldap.object_context' . This change allows us to configure LDAP to bind with Active Directory out of the box. *. Modifed the LDAP search for user-info to take a SearchControls parameter that specifies a recursive tree-search under the ldap.search_context tree for a single user-object result. Once again - this allows LDAPAuthenticate to work with an Active Directory tree that has user objects organized into different folders under a tree. *. Modified LDAPAuthentication.getSpecialGroups to access the ldap.dspace.autogroup dspace.cfg configuration property to get the group-ids that an LDAP-authenticated user should be an implicit member of. This makes it easy to configure a system where every user that can authenticate can also submit material to some collections. *. Changed some of the if/else nesting in LDAPAuthentication.authenticate so that instead of having something like if () { ... return bla; } else { we have if () { ... return bla; } ... // no else and instead of } else { if () { return goo } } we just have } else if () { just to make the control flow a little easier to look at. ---------------------------------- $ svn diff pom.xml src/test src/main/java/org/dspace/authenticate > /tmp/bla Index: pom.xml =================================================================== --- pom.xml (revision 2942) +++ pom.xml (working copy) @@ -61,6 +61,7 @@ <url>http://dspace.svn.sourceforge.net/viewvc/dspace/branches/dspace-1_5_x/dspace</url> </scm> + <!-- Runtime and Compile Time dependencies for DSpace. --> @@ -188,6 +189,23 @@ <groupId>com.ibm.icu</groupId> <artifactId>icu4j</artifactId> </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>3.8.1</version> + <scope>test</scope> + </dependency> </dependencies> -</project> \ No newline at end of file +<build> + <testResources> + <testResource> + <directory>src/test/resources</directory> + <includes> + <include>log4j.properties</include> + </includes> + </testResource> + </testResources> + </build> + +</project> Index: src/test/java/org/dspace/authenticate/SpeakerToLDAPCase.java =================================================================== --- src/test/java/org/dspace/authenticate/SpeakerToLDAPCase.java (revision 0) +++ src/test/java/org/dspace/authenticate/SpeakerToLDAPCase.java (revision 0) @@ -0,0 +1,56 @@ +package org.dspace.authenticate; + + +import junit.framework.TestCase; + +import org.apache.log4j.Logger; + +/** + * Generic test runner for SpeakerToLDAP implementations. + */ +public class SpeakerToLDAPCase extends TestCase { + private static final Logger olog = Logger.getLogger( SpeakerToLDAPCase.class ); + + private SpeakerToLDAP oldap; + private String os_netid; + private String os_password; + + + /** + * Inject the test dependencies - initializes test properties + * + * @param s_name of test - pass to super + * @param ldap instance to authenticate agains + * @param s_netid user-id to authenticate as + * @param s_password for s_netid + */ + public SpeakerToLDAPCase ( String s_name, SpeakerToLDAP ldap, + String s_netid, String s_password + ) { + super( s_name ); + oldap = ldap; + os_netid = s_netid; + os_password = s_password; + } + + /** + * Try to authenticate against the constructor-supplied + * (SpeakerToLDAP, s_netid, s_password) + */ + public void testAuthenticate() { + try { + DataFromLDAP ldap_info = oldap.ldapAuthenticate( os_netid, os_password ); + assertTrue( "Test user logged in ok: " + os_netid, + null != ldap_info + ); + // need e-mail to key into eperson database + olog.info( "Got e-mail for " + os_netid + ": " + ldap_info.getEmail () ); + assertTrue( "Got e-mail info for " + os_netid + " from ldap", + null != ldap_info.getEmail () + ); + } catch ( Exception e ) { + olog.info( "Failed to authenticate user: " + os_netid, e ); + assertTrue( "Failed to authenticate user: " + os_netid + ", caught: " + e, false ); + } + } +} Index: src/test/java/org/dspace/authenticate/PackageTest.java =================================================================== --- src/test/java/org/dspace/authenticate/PackageTest.java (revision 0) +++ src/test/java/org/dspace/authenticate/PackageTest.java (revision 0) @@ -0,0 +1,40 @@ +package org.dspace.authenticate; + + +import junit.framework.TestCase; +import junit.framework.TestSuite; + +import org.apache.log4j.Logger; + +/** + * Specialization of AbstactSpeakerToLDAPTest configured + * to run a DefaultSpeakerToLDAPTest through a test. + */ +public class PackageTest extends TestCase { + private static final Logger olog = Logger.getLogger( PackageTest.class ); + + + /** + * Build up the batch of tests to run for the org.dspace.authenticate + * package. You'll have to modify the properties injected into the + * test SpeakerToLDAP to work with your environment + */ + public static TestSuite suite () { + TestSuite suite = new TestSuite( PackageTest.class.getName () ); + SpeakerToLDAP ldap = new DefaultSpeakerToLDAP ( "ldap://ldaps.university.edu", + "cn", + "OU=People,OU=AUMain,DC=auburn,DC=edu", + "WINDOWS_DOMAIN:AUBURN", + "mail", + "givenName", + "sn", + "telephoneNumber" + ); + suite.addTest ( new SpeakerToLDAPCase( "testAuthenticate", ldap, + "USER", "PASSWORD" + ) + ); + return suite; + } + +} Index: src/test/resources/log4j.properties =================================================================== --- src/test/resources/log4j.properties (revision 0) +++ src/test/resources/log4j.properties (revision 0) @@ -0,0 +1,9 @@ +# Set root logger level to DEBUG and its only appender to A1. +log4j.rootLogger=DEBUG, A1 + +# A1 is set to be a ConsoleAppender. +log4j.appender.A1=org.apache.log4j.ConsoleAppender + +# A1 uses PatternLayout. +log4j.appender.A1.layout=org.apache.log4j.PatternLayout +log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n Index: src/main/java/org/dspace/authenticate/LDAPAuthentication.java =================================================================== --- src/main/java/org/dspace/authenticate/LDAPAuthentication.java (revision 3054) +++ src/main/java/org/dspace/authenticate/LDAPAuthentication.java (working copy) @@ -40,17 +40,11 @@ package org.dspace.authenticate; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; import java.util.Hashtable; -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.InitialDirContext; -import javax.naming.directory.SearchResult; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -60,6 +54,7 @@ import org.dspace.core.Context; import org.dspace.core.LogManager; import org.dspace.eperson.EPerson; +import org.dspace.eperson.Group; /** * Authentication module to authenticate against a flat LDAP tree where @@ -72,9 +67,66 @@ implements AuthenticationMethod { /** log4j category */ - private static Logger log = Logger.getLogger(LDAPAuthentication.class); + private static final Logger olog = Logger.getLogger(LDAPAuthentication.class); + private final SpeakerToLDAP oldap; + /** + * Constructor injects SpeakerToLDAP dependency + * + * @param ldap SpeakerToLDAP knows how to authenticate + * against and query the LDAP directory + * @param b_autoregister set true to auto-register an eperson + * for a new user that succesfully authenticates + * with LDAP + * @param v_special_group ids of user groups that an LDAP-authenticated + * user should be considered an implicit member of + */ + public LDAPAuthentication ( SpeakerToLDAP ldap, + boolean b_autoregister, + int[] v_special_group + ) { + oldap = ldap; + ob_autoregister = b_autoregister; + ov_special_group = v_special_group; + } + + /** + * Constructor invoked by dspace.cfg based configuration + * engine sets up DefaultSpeakerToLDAP, + * checks ldap.autoregister and ldap.dspace.autogroup + * configuration values to determine canSelfRegister + * and getSpecialGroups property values. + */ + public LDAPAuthentication () { + String s_groups = ConfigurationManager.getProperty("ldap.dspace.autogroup"); + + List<Integer> v_group_id = new ArrayList<Integer>(); + if ( null != s_groups ) { + String[] v_group_name = s_groups.trim ().split( ",\\s*" ); + for ( int i=0; i < v_group_name.length; ++i ) { + String s_group = v_group_name[i].trim (); + if ( s_group.length () > 0 ) { + try { + v_group_id.add ( Integer.parseInt( s_group ) ); + } catch ( Exception e ) { + olog.warn( "Exception parsing group " + s_group, e ); + } + } + } + } + oldap = new DefaultSpeakerToLDAP (); + ob_autoregister = ConfigurationManager.getBooleanProperty("webui.ldap.autoregister"); + ov_special_group = new int[ v_group_id.size () ]; + int i_count = 0; + for ( Integer i_group_id : v_group_id ) { + ov_special_group[ i_count ] = i_group_id; + ++i_count; + } + } + + private final boolean ob_autoregister; + /** * Let a real auth method return true if it wants. */ public boolean canSelfRegister(Context context, @@ -83,8 +135,7 @@ throws SQLException { // XXX might also want to check that username exists in LDAP. - - return ConfigurationManager.getBooleanProperty("webui.ldap.autoregister"); + return ob_autoregister; } /** @@ -118,12 +169,10 @@ return false; } - /* - * Nothing here. - */ + private final int[] ov_special_group; public int[] getSpecialGroups(Context context, HttpServletRequest request) { - return new int[0]; + return ov_special_group; } /* @@ -139,261 +188,140 @@ HttpServletRequest request) throws SQLException { - log.info(LogManager.getHeader(context, "auth", "attempting trivial auth of user="+netid)); + olog.info(LogManager.getHeader(context, "auth", "attempting auth of user="+netid)); // Skip out when no netid or password is given. - if (netid == null || password == null) - return BAD_ARGS; + if (netid == null || password == null) { + return BAD_ARGS; + } // Locate the eperson - EPerson eperson = null; + EPerson eperson = null; try { - eperson = EPerson.findByNetid(context, netid.toLowerCase()); + eperson = EPerson.findByNetid(context, netid.toLowerCase()); } catch (SQLException e) { } - boolean loggedIn = false; - SpeakerToLDAP ldap = new SpeakerToLDAP(log); + olog.debug( "Found eperson for " + netid ); + // if they entered a netid that matches an eperson if (eperson != null) { // e-mail address corresponds to active account - if (eperson.getRequireCertificate()) + if (eperson.getRequireCertificate()) { return CERT_REQUIRED; - else if (!eperson.canLogIn()) + } else if (!eperson.canLogIn()) { return BAD_ARGS; - { - if (ldap.ldapAuthenticate(netid, password, context)) - { + } + try { + // authenticate + olog.debug( "Attempting LDAP auth-1 for " + netid ); + DataFromLDAP ldap_info = oldap.ldapAuthenticate( netid, password ); + if ( null != ldap_info ) { context.setCurrentUser(eperson = EPerson.findByNetid(context, netid.toLowerCase())); - log.info(LogManager - .getHeader(context, "authenticate", "type=ldap")); + olog.info(LogManager + .getHeader(context, "authenticate", "type=ldap")); return SUCCESS; - } - else - return BAD_CREDENTIALS; + } + } catch ( NamingException e ) { + olog.warn( "Failed to authenticate user: " + netid, e ); } + //else { + return BAD_CREDENTIALS; + } + // eperson == null + if ( null != eperson ) { + throw new AssertionError( "eperson should be null here!" ); } - - // the user does not already exist so try and authenticate them - // with ldap and create an eperson for them - else - { - if (ldap.ldapAuthenticate(netid, password, context)) - { - // Register the new user automatically - log.info(LogManager.getHeader(context, - "autoregister", "netid=" + netid)); - - if ((ldap.ldapEmail!=null)&&(!ldap.ldapEmail.equals(""))) - { - try - { - eperson = EPerson.findByEmail(context, ldap.ldapEmail); - if (eperson!=null) - { - log.info(LogManager.getHeader(context, - "type=ldap-login", "type=ldap_but_already_email")); - context.setIgnoreAuthorization(true); - eperson.setNetid(netid); - eperson.update(); - context.commit(); - context.setIgnoreAuthorization(false); - context.setCurrentUser(eperson); - return SUCCESS; - } - else - { - if (canSelfRegister(context, request, netid)) - { - // TEMPORARILY turn off authorisation - try - { - context.setIgnoreAuthorization(true); - eperson = EPerson.create(context); - if ((ldap.ldapEmail!=null)&&(!ldap.ldapEmail.equals(""))) eperson.setEmail(ldap.ldapEmail); - else eperson.setEmail(netid); - if ((ldap.ldapGivenName!=null)&&(!ldap.ldapGivenName.equals(""))) eperson.setFirstName(ldap.ldapGivenName); - if ((ldap.ldapSurname!=null)&&(!ldap.ldapSurname.equals(""))) eperson.setLastName(ldap.ldapSurname); - if ((ldap.ldapPhone!=null)&&(!ldap.ldapPhone.equals(""))) eperson.setMetadata("phone", ldap.ldapPhone); - eperson.setNetid(netid); - eperson.setCanLogIn(true); - AuthenticationManager.initEPerson(context, request, eperson); - eperson.update(); - context.commit(); - context.setCurrentUser(eperson); - } - catch (AuthorizeException e) - { - return NO_SUCH_USER; - } - finally - { - context.setIgnoreAuthorization(false); - } - - log.info(LogManager.getHeader(context, "authenticate", - "type=ldap-login, created ePerson")); - return SUCCESS; - } - else - { - // No auto-registration for valid certs - log.info(LogManager.getHeader(context, - "failed_login", "type=ldap_but_no_record")); - return NO_SUCH_USER; - } - } - } - catch (AuthorizeException e) - { - eperson = null; - } - finally - { - context.setIgnoreAuthorization(false); - } - } - } + olog.debug( "Attempting LDAP auth-2 for " + netid ); + DataFromLDAP ldap_info = null; + try { + ldap_info = oldap.ldapAuthenticate( netid, password ); + } catch ( NamingException e ) { + olog.warn( "Failed to authenticate user: " + netid, e ); } - return BAD_ARGS; - } - - /** - * Internal class to manage LDAP query and results, mainly - * because there are multiple values to return. - */ - public class SpeakerToLDAP { - - private Logger log = null; - - /** ldap email result */ - protected String ldapEmail = null; - - /** ldap name result */ - protected String ldapGivenName = null; - protected String ldapSurname = null; - protected String ldapPhone = null; - - SpeakerToLDAP(Logger thelog) - { - log = thelog; + if ( (null == ldap_info) + || (ldap_info.getEmail()==null) + || ldap_info.getEmail().equals("") + ) { + return BAD_ARGS; // failed to authenticate or get e-mail address } - /** - * contact the ldap server and attempt to authenticate - */ - protected boolean ldapAuthenticate(String netid, String password, Context context) - { - if (!password.equals("")) - { - String ldap_provider_url = ConfigurationManager.getProperty("ldap.provider_url"); - String ldap_id_field = ConfigurationManager.getProperty("ldap.id_field"); - String ldap_search_context = ConfigurationManager.getProperty("ldap.search_context"); - String ldap_object_context = ConfigurationManager.getProperty("ldap.object_context"); - - // Set up environment for creating initial context - Hashtable env = new Hashtable(11); - env.put(javax.naming.Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); - env.put(javax.naming.Context.PROVIDER_URL, ldap_provider_url); - - // Authenticate - env.put(javax.naming.Context.SECURITY_AUTHENTICATION, "simple"); - env.put(javax.naming.Context.SECURITY_PRINCIPAL, ldap_id_field+"="+netid+","+ldap_object_context); - env.put(javax.naming.Context.SECURITY_CREDENTIALS, password); - - DirContext ctx = null; - try - { - // Create initial context - ctx = new InitialDirContext(env); - - String ldap_email_field = ConfigurationManager.getProperty("ldap.email_field"); - String ldap_givenname_field = ConfigurationManager.getProperty("ldap.givenname_field"); - String ldap_surname_field = ConfigurationManager.getProperty("ldap.surname_field"); - String ldap_phone_field = ConfigurationManager.getProperty("ldap.phone_field"); - - Attributes matchAttrs = new BasicAttributes(true); - matchAttrs.put(new BasicAttribute(ldap_id_field, netid)); - - String attlist[] = {ldap_email_field, ldap_givenname_field, ldap_surname_field, ldap_phone_field}; - - // look up attributes - try - { - NamingEnumeration answer = ctx.search(ldap_search_context, matchAttrs, attlist); - while(answer.hasMore()) { - SearchResult sr = (SearchResult)answer.next(); - Attributes atts = sr.getAttributes(); - Attribute att; - - if (attlist[0]!=null) - { - att = atts.get(attlist[0]); - if (att != null) ldapEmail = (String)att.get(); - } - - if (attlist[1]!=null) - { - att = atts.get(attlist[1]); - if (att != null) ldapGivenName = (String)att.get(); - } - - if (attlist[2]!=null) - { - att = atts.get(attlist[2]); - if (att != null) ldapSurname = (String)att.get(); - } - - if (attlist[3]!=null) - { - att = atts.get(attlist[3]); - if (att != null) ldapPhone = (String)att.get(); - } - } + // + // autoregister the ldap-authenticated user + // + olog.info(LogManager.getHeader(context, + "autoregister", "netid=" + netid) + ); + + try { + eperson = EPerson.findByEmail(context, ldap_info.getEmail()); + if (eperson!=null) { + // Just need to set the netid on the eperson record + olog.info(LogManager.getHeader(context, + "type=ldap-login", "type=ldap_but_already_email")); + context.setIgnoreAuthorization(true); + eperson.setNetid(netid); + eperson.update(); + context.commit(); + context.setIgnoreAuthorization(false); + context.setCurrentUser(eperson); + return SUCCESS; + } else if (canSelfRegister(context, request, netid)) { + // TEMPORARILY turn off authorisation + try { + context.setIgnoreAuthorization(true); + eperson = EPerson.create(context); + eperson.setEmail(ldap_info.getEmail()); + if ((ldap_info.getGivenName()!=null) + &&(!ldap_info.getGivenName().equals("")) + ) { + eperson.setFirstName(ldap_info.getGivenName()); } - catch (NamingException e) - { - // if the lookup fails go ahead and create a new record for them because the authentication - // succeeded - log.warn(LogManager.getHeader(context, - "ldap_attribute_lookup", "type=failed_search "+e)); - return true; + if ((ldap_info.getSurname()!=null) + &&(!ldap_info.getSurname().equals("")) + ) { + eperson.setLastName(ldap_info.getSurname()); } - } - catch (NamingException e) - { - log.warn(LogManager.getHeader(context, - "ldap_authentication", "type=failed_auth "+e)); - return false; - } - finally - { - // Close the context when we're done - try - { - if (ctx != null) - ctx.close(); + if ((ldap_info.getPhone()!=null) + &&(!ldap_info.getPhone().equals("")) + ) { + eperson.setMetadata("phone", ldap_info.getPhone()); } - catch (NamingException e) - { - } + eperson.setNetid(ldap_info.getNetId()); + eperson.setCanLogIn(true); + AuthenticationManager.initEPerson(context, request, eperson); + eperson.update(); + context.commit(); + context.setCurrentUser(eperson); + } catch (AuthorizeException e) { + return NO_SUCH_USER; + } finally { + context.setIgnoreAuthorization(false); } + olog.info(LogManager.getHeader(context, "authenticate", + "type=ldap-login, created ePerson")); + return SUCCESS; + } else { + // No auto-registration for valid certs + olog.info(LogManager.getHeader(context, + "failed_login", "type=ldap_but_no_record")); + return NO_SUCH_USER; } - else - { - return false; - } - - return true; + } catch (AuthorizeException e) { + eperson = null; + // authentication failed + return BAD_ARGS; + } finally { + context.setIgnoreAuthorization(false); } - - + // Unreachable! } + /* * Returns URL to which to redirect to obtain credentials (either password * prompt or e.g. HTTPS port for client cert.); null means no redirect. @@ -430,4 +358,4 @@ { return "org.dspace.eperson.LDAPAuthentication.title"; } -} \ No newline at end of file +} Index: src/main/java/org/dspace/authenticate/DefaultSpeakerToLDAP.java =================================================================== --- src/main/java/org/dspace/authenticate/DefaultSpeakerToLDAP.java (revision 0) +++ src/main/java/org/dspace/authenticate/DefaultSpeakerToLDAP.java (revision 0) @@ -0,0 +1,183 @@ +package org.dspace.authenticate; + +import java.util.Hashtable; + +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.InitialDirContext; +import javax.naming.directory.SearchControls; +import javax.naming.directory.SearchResult; + +import org.apache.log4j.Logger; +import org.dspace.core.ConfigurationManager; + +/** + * Internal class to manage LDAP query and results, mainly + * because there are multiple values to return. + */ +public class DefaultSpeakerToLDAP implements SpeakerToLDAP { + private static final Logger olog = Logger.getLogger(DefaultSpeakerToLDAP.class); + private final String os_provider_url; + private final String os_id_field; + private final String os_search_context; + private final String os_object_context; + private final String os_email_field; + private final String os_givenname_field; + private final String os_surname_field; + private final String os_phone_field; + + + /** + * Constructor allows injection of + * configuration parameters. + * + * @param s_provider_url to the server - we assume simple authentication + * @param s_id_field attribute of user object - usually cn + * @param s_search_context subtree under which to search for user info, + * ex: ou=People,dc=myschool,dc=edu + * @param s_object_context of user bind-path - + * ex: ou=People,dc=myschool,dc=edu leads to bind attempt + * againt cn=username,ou=People,dc=myschool,dc=edu + * @param s_email_field in user record + * @param s_givenname_field in user record + * @param s_surname_field in user record, usually sn + * @param s_phone_field in user record + */ + public DefaultSpeakerToLDAP( String s_provider_url, + String s_id_field, + String s_search_context, + String s_object_context, + String s_email_field, + String s_givenname_field, + String s_surname_field, + String s_phone_field + ) + { + os_provider_url = s_provider_url; + os_id_field = s_id_field; + os_search_context = s_search_context; + os_object_context = s_object_context; + os_email_field = s_email_field; + os_givenname_field = s_givenname_field; + os_surname_field = s_surname_field; + os_phone_field = s_phone_field; + } + + /** + * Default constructor extracts LDAP-server parameters + * from ConfigurationManager (dspace.cfg): + * ldap.provider_url, ldap_id_field, + * ldap_search_contect, ldap_object_context + */ + public DefaultSpeakerToLDAP() { + os_provider_url = ConfigurationManager.getProperty("ldap.provider_url"); + os_id_field = ConfigurationManager.getProperty("ldap.id_field"); + os_search_context = ConfigurationManager.getProperty("ldap.search_context"); + os_object_context = ConfigurationManager.getProperty("ldap.object_context"); + os_email_field = ConfigurationManager.getProperty("ldap.email_field"); + os_givenname_field = ConfigurationManager.getProperty("ldap.givenname_field"); + os_surname_field = ConfigurationManager.getProperty("ldap.surname_field"); + os_phone_field = ConfigurationManager.getProperty("ldap.phone_field"); + } + + /** + * contact the ldap server and attempt to authenticate + */ + public DataFromLDAP ldapAuthenticate(String s_netid, String s_password ) throws NamingException + { + if (s_password.equals("")) + { + return null; + } + // Set up environment for creating initial context + Hashtable env = new Hashtable(11); + env.put(javax.naming.Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); + env.put(javax.naming.Context.PROVIDER_URL, os_provider_url); + + // Authenticate + env.put(javax.naming.Context.SECURITY_AUTHENTICATION, "simple"); + final String s_ad_key = "WINDOWS_DOMAIN:"; + if ( os_object_context.toUpperCase ().startsWith( s_ad_key ) ) { + // Active Directory bind + String s_principal = os_object_context.substring( s_ad_key.length () ) + "\\" + s_netid; + olog.debug( "Binding principal to: " + s_principal ); + env.put(javax.naming.Context.SECURITY_PRINCIPAL, s_principal ); + } else { + env.put(javax.naming.Context.SECURITY_PRINCIPAL, os_id_field+"="+s_netid+","+os_object_context); + } + env.put(javax.naming.Context.SECURITY_CREDENTIALS, s_password); + + DirContext ctx = new InitialDirContext(env); + try { + Attributes search_attributes = new BasicAttributes(true); + search_attributes.put(new BasicAttribute(os_id_field, s_netid)); + + String[] v_result_atts = {os_email_field, os_givenname_field, os_surname_field, os_phone_field}; + + olog.debug( "Searching LDAP for " + os_id_field + "=" + s_netid + + " under " + os_search_context + ); + + NamingEnumeration answer = ctx.search(os_search_context, + "(" + os_id_field + "=" + s_netid + ")", + new SearchControls( SearchControls.SUBTREE_SCOPE, + 1, 20000, + v_result_atts, + false, false + ) + ); + if( ! answer.hasMore()) { + olog.info( "Able to bind as " + s_netid + ", but unable to find LDAP record" ); + return null; + } + // look up attributes + String ldapEmail = null; + String ldapGivenName = null; + String ldapSurname = null; + String ldapPhone = null; + SearchResult sr = (SearchResult)answer.next(); + Attributes atts = sr.getAttributes(); + Attribute att; + + if (v_result_atts[0]!=null) { + att = atts.get(v_result_atts[0]); + if (att != null) ldapEmail = (String)att.get(); + } + + if (v_result_atts[1]!=null) { + att = atts.get(v_result_atts[1]); + if (att != null) { + ldapGivenName = (String)att.get(); + } + } + + if (v_result_atts[2]!=null) { + att = atts.get(v_result_atts[2]); + if (att != null) { + ldapSurname = (String)att.get(); + } + } + + if (v_result_atts[3]!=null) { + att = atts.get(v_result_atts[3]); + if (att != null) { + ldapPhone = (String)att.get(); + } + } + return new SimpleDataFromLDAP( ldapEmail, ldapGivenName, ldapSurname, ldapPhone, s_netid ); + } finally { + // Close the context when we're done + try { + if (ctx != null) { + ctx.close(); + } + } catch (NamingException e) { } + } + // Unreachable + } +} Index: src/main/java/org/dspace/authenticate/SimpleDataFromLDAP.java =================================================================== --- src/main/java/org/dspace/authenticate/SimpleDataFromLDAP.java (revision 0) +++ src/main/java/org/dspace/authenticate/SimpleDataFromLDAP.java (revision 0) @@ -0,0 +1,48 @@ +package org.dspace.authenticate; + + +/** + * Simple implementation of DataFromLDAP + */ +public class SimpleDataFromLDAP implements DataFromLDAP { + /** + * Constructor injects all the property values + */ + public SimpleDataFromLDAP ( String s_email, + String s_given_name, + String s_surname, + String s_phone, + String s_netid + ) { + os_email = s_email; + os_given_name = s_given_name; + os_surname = s_surname; + os_phone = s_phone; + os_netid = s_netid; + } + + private final String os_email; + public String getEmail () { + return os_email; + } + + private final String os_given_name; + public String getGivenName () { + return os_given_name; + } + + private final String os_surname; + public String getSurname() { + return os_surname; + } + + private final String os_phone; + public String getPhone () { + return os_phone; + } + + private final String os_netid; + public String getNetId () { + return os_netid; + } +} Index: src/main/java/org/dspace/authenticate/SpeakerToLDAP.java =================================================================== --- src/main/java/org/dspace/authenticate/SpeakerToLDAP.java (revision 0) +++ src/main/java/org/dspace/authenticate/SpeakerToLDAP.java (revision 0) @@ -0,0 +1,20 @@ +package org.dspace.authenticate; + +import javax.naming.NamingException; + +/** + * Interface for LDAP interaction handler + */ +public interface SpeakerToLDAP { + /** + * Authenticate the given user with LDAP, + * and get some data about him/her from the directory. + * + * @param s_netid cn of the user to authenticate + * @param s_password + * @return user info + */ + public DataFromLDAP ldapAuthenticate( String s_netid, + String s_password + ) throws NamingException; +} Index: src/main/java/org/dspace/authenticate/DataFromLDAP.java =================================================================== --- src/main/java/org/dspace/authenticate/DataFromLDAP.java (revision 0) +++ src/main/java/org/dspace/authenticate/DataFromLDAP.java (revision 0) @@ -0,0 +1,15 @@ +package org.dspace.authenticate; + + +/** + * POJO data bucket interface for user-data obtained during + * LDAP authentication + */ +public interface DataFromLDAP { + public String getEmail (); + public String getGivenName (); + public String getSurname(); + public String getPhone (); + /** LDAP common name (cn) for the user */ + public String getNetId (); +} ------------------------------------------------------------------------- This SF.Net email is sponsored by the Moblin Your Move Developer's challenge Build the coolest Linux based applications with Moblin SDK & win great prizes Grand prize is a trip for two to an Open Source event anywhere in the world http://moblin-contest.org/redirect.php?banner_id=100&url=/ _______________________________________________ DSpace-tech mailing list DSpace-tech@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/dspace-tech