Integrating EPrints with LDAP

From EPrints Documentation
Revision as of 11:55, 12 August 2009 by Tdb01r (talk | contribs)
Jump to: navigation, search

Note: some things on this page are specific to EPrints2. EPrints3 comes with example code in archives/ARCHIVEID/cfg/cfg.d/user_login.pl that just needs to be uncommented (and maybe customised). See LDAP or LDAP_user_login.pl for examples. You also do not have to create ldapuser, ldapeditor and ldapadmin, you may also use the plain variants if you plan on using only LDAP accounts.

Install Prerequisites

Install the Net::LDAP and IO::Socket::SSL modules.

Determine LDAP Setup

The first step is to get Perl talking to your LDAP server. Determine the following parameters:

  • the LDAP server name
  • if the server doesn't support anonymous binds, a distinguished name (e.g. OU=jsmith, OU=users, DC=somewhere, DC=edu) and password to bind with
  • the base name of user accounts (e.g. OU=users, DC=somewhere, DC=edu)
  • the name of the username field (e.g. samaccountname, cn)

The http://files.eprints.org/27/01/ldaplookup script should get you started. Add your LDAP parameters to the script.

When run with an username as its parameter, ldaplookup should dump all the information associated with that account:

$ ./ldaplookup jsmith
Using LDAP protocol version 3
LDAP_EXTENSION_START_TLS supported
------------------------------------------------------------------------
dn:CN=jsmith,OU=users,DC=somewhere,DC=edu
              objectClass: top
                           person
                           organizationalPerson
                           user
                       cn: jsmith
                       sn: Smith
...

You will probably need to tweak the LDAP parameters to suit your setup. See perldoc Net::LDAP.

Configure user authentication

The EPrints configuration lets you specify any number of user types (the defaults are "user", "editor" and "admin") each of which can be authorised in a different way. Suppose we want to have user, editor and admin accounts that are authorised by our LDAP server (i.e. internal staff) and also user, editor and admin accounts that are local to EPrints and authorised in the usual way (i.e. external users).

Create new user types

In /opt/eprints2/archives/ARCHIVEID/cfg/metadata-types.xml, copy and paste the existing user, editor, and admin types and change their names to ldapuser, ldapeditor and ldapadmin:

<dataset name="user">
   <type name="ldapuser" >
       <field name="password" />
       <field name="username" staffonly="yes"/>
       ...
   </type>
   <type name="ldapeditor" >
       <field name="password" />
       <field name="username" staffonly="yes"/>
       ...
   </type>
   <type name="ldapadmin" >
       <field name="password" />
       <field name="usertype" staffonly="yes" />
       ...
   </type>
   <type name="user" >
       <field name="password" />
       <field name="usertype" staffonly="yes"/>
       ...
   </type>
   <type name="editor" >
       <field name="password" />
       <field name="usertype" staffonly="yes" />
       ...
   </type>
   <type name="admin" >
       <field name="password" />
       <field name="username" staffonly="yes" />
       ...
   </type>
</dataset>

Define user citation styles

Define citation styles for the new user types in /opt/eprints2/archives/ARCHIVEID/cfg/citations-en.xml:

Just copy and paste the citation types for user_user, user_editor and user_admin and rename them to user_ldapuser, user_ldapeditor and user_ldapadmin.

Define user typenames

Define typenames for the new user types in /opt/eprints2/archives/ARCHIVEID/cfg/phrases-en.xml:

Just copy and paste the phrases for user_typename_user, user_typename_editor and user_typename_admin and rename them to user_typename_ldapuser, user_typename_ldapeditor and user_typename_ldapadmin.

Then, adjust the three new phrases values, e.g., the phrase value for the user_typename_ldapuser could be changed from "User" to "LDAP User".

Configure user authentication

Now configure user authentication in /opt/eprints2/archives/ARCHIVEID/cfg/ArchiveConfig.pm:

my $LDAP = { handler => \&ldapauthen };
 
$c->{userauth} = {
   user => {
       auth  => $CRYPTED_DBI,
       priv  =>  [ "subscription", "set-password", "deposit", "change-email", "change-user" ] },
   editor => {
       auth  => $CRYPTED_DBI,
       priv  =>  [ "subscription", "set-password", "deposit", "change-email", "change-user",
               "view-status", "editor", "staff-view" ] },
   admin => {
       auth  => $CRYPTED_DBI,
       priv  =>  [ "subscription", "set-password", "deposit", "change-email", "change-user",
               "view-status", "editor", "staff-view",
               "edit-subject", "edit-user" ] },
   ldapuser => {
       auth  => $LDAP,
       priv  =>  [ "subscription", "set-password", "deposit",  "change-user","no_edit_own_record" ] },
   ldapeditor => {
       auth  => $LDAP,
       priv  =>  [ "subscription", "set-password", "deposit",  "change-user","no_edit_own_record",
               "view-status", "editor", "staff-view" ] },
   ldapadmin => {
       auth  => $LDAP,
       priv  =>  [ "subscription", "set-password", "deposit", "change-user",
               "view-status", "editor", "staff-view", "edit-user" ] },
};

Here, the default user, editor and admin user types are still authenticated using the usual $CRYPTED_DBI construct, whereas ldapuser, ldapeditor and ldapadmin are authenticated by the $LDAP construct that we defined.

Define LDAP authentication function

Add the ldapauthen function to the end of ArchiveConfig.pm, filling in your LDAP settings:

use Net::LDAP;
BEGIN {
   eval "use Apache::Const ':common'" || eval "use Apache2::Const ':common'";
}
 
sub ldapauthen
{
   my ($r) = @_;
   my ($key, $val, $dbh);
 
   return OK unless $r->is_initial_req; # only the first internal request
 
   my($res, $passwd_sent) = $r->get_basic_auth_pw;
   return $res if $res; # e.g. HTTP_UNAUTHORIZED
 
   # get username
   my ($user_sent) = $r->user;
 
   my $ldap = Net::LDAP->new ( "ldap.host.name", version=>3 );
   $ldap->start_tls( sslversion=>'sslv2' );
 
   unless( $ldap )
   {
       print STDERR "$@";
       return SERVER_ERROR;
   }
 
   # If the distinguished name of the user is not
   # computable from the username, perform a lookup
   # to determine the distinguished name
 
   my $mesg = $ldap->bind;
   # If your LDAP server doesn't allow anonymous binds
   # supply the dn/password of a valid account here
   # (e.g. an 'eprints' account created specially for
   # this purpose)
   #my $dn = "";
   #my $pword = "";
   #my $mesg = $ldap->bind( $dn, password=>$pword );
 
   my $base = "ou=user, dc=somewhere, dc=edu";
   my $result = $ldap->search (
               base    => "$base",
               scope   => "sub",
               filter  => "cn=$user_sent",
               attrs   =>  ['DN'],
               sizelimit=>1
             );
 
   my $entr = $result->pop_entry;
   unless( defined $entr )
   {
       $r->note_basic_auth_failure;
       return AUTH_REQUIRED;
   }
 
   # Bind with the distinguished name and password of the user
   # If the distinguished name of the user is computable, this
   # is the only step required
   my $mesg2 = $ldap->bind( $entr->dn, password=>$passwd_sent );
   if( $mesg2->code )
   {
       $r->note_basic_auth_failure;
       return AUTH_REQUIRED;
   }
 
   return OK;
}

Reload configuration

Restart the apache server to reload the new configuration.

Import user accounts from LDAP

The final step is to import existing user accounts from the LDAP server.

The http://files.eprints.org/27/02/update_users script should get you started. Copy it to the /opt/eprints2/bin directory and add your LDAP settings.

Set $forreal to 1 to make changes to the database.

Notes

  • If you see a "Sizelimit exceeded" error, you may need to import users in smaller batches, for example a faculty/dept at a time.
  • update_users should be scheduled regularly using cron to keep EPrints in sync with the LDAP server
  • For EPrints3 you will need to update two lines below;
    Line 3: use EPrints::DataObj::User; #use EPrints::User;
    Line 77: $user = EPrints::DataObj::User::create($session,"ldapuser"); #$user = EPrints::User::create_user( $session, "ldapuser" );