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
[email protected]
https://lists.sourceforge.net/lists/listinfo/dspace-tech