From 5940695c59d290ebfca126e7007a0fa02ceb5ccc Mon Sep 17 00:00:00 2001 From: Dan Scott Date: Mon, 27 Aug 2012 18:38:51 -0400 Subject: [PATCH] LDAP authentication enablement for OSUL We use the local-part of the email address to authenticate against the LDAP server. In the VirtualHost sections of Apache, we can use SetEnv to force the physical_loc to match the org unit ID(s) specified in opensrf.xml; for example: SetEnv physical_loc 103 At Laurentian, all usernames will be lowercase. This will prevent mismatches like the user entering "dscott" and the database having the value "DScott" (in that we're going to force all of the Laurentian user names in the database to lower case). Add an ou_host_name parameter for TPAC login forms If set, and the incoming username does not already include an '@' symbol (a very simple attempt to detect if we're already dealing with an email address), then append '@' + the ou_host_name value to the username for authentication purposes. The rationale is that in a large consortium, you might want to enable users to log in with short usernames (like 'fred'), but you also want to avoid conflicts between short usernames at different organizational units. Thus, create the users with the email equivalent of their usernames, like 'fred@br1.example.com' and 'fred@br4.example.com', and let the templates for the TPAC in br1 and br4 contain a hidden input field to append the appropriate email hostname. Truth be told, this is probably most appropriate for a large consortium containing two or more academic institutions that hope to use LDAP authentication rather than native authentication, and therefore have LDAP CNs that map to email addresses of CN@hostname that can then be mapped to actor.usr.usrname (and actor.usr.email, of course). Signed-off-by: Dan Scott --- .../Application/AuthProxy/LDAP_Auth_OSUL.pm | 86 ++++++++++++++++++++++ .../src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm | 9 +++ 2 files changed, 95 insertions(+) create mode 100644 Open-ILS/src/perlmods/lib/OpenILS/Application/AuthProxy/LDAP_Auth_OSUL.pm diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/AuthProxy/LDAP_Auth_OSUL.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/AuthProxy/LDAP_Auth_OSUL.pm new file mode 100644 index 0000000000..1a904ecb56 --- /dev/null +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/AuthProxy/LDAP_Auth_OSUL.pm @@ -0,0 +1,86 @@ +package OpenILS::Application::AuthProxy::LDAP_Auth_OSUL; +use strict; +use warnings; +use base 'OpenILS::Application::AuthProxy::AuthBase'; +use OpenILS::Event; +use Net::LDAP; +use OpenSRF::Utils::SettingsClient; +use OpenSRF::Utils::Logger qw(:logger); + +# default config var (override in configuration xml) +my $id_attr = 'uid'; + +sub authenticate { + my ( $self, $args ) = @_; + + # Convert the entire user name to lowercase + # This assumes that all of the user names in the database are lower case + $args->{'username'} = lc($args->{'username'}); + my $username = $args->{'username'}; + + # Authenticate against LDAP based on the user portion of the email address + my $ldap_username = $username; + $ldap_username =~ s/\@.*$//; + + my $password = $args->{'password'}; + + if (!$username) { + $logger->debug("User login failed: No username provided"); + return OpenILS::Event->new( 'LOGIN_FAILED' ); + } + if (!$password) { + $logger->debug("User login failed: No password provided"); + return OpenILS::Event->new( 'LOGIN_FAILED' ); + } + + my $hostname_is_ldap = 0; + my $reached_ldap = 0; + my $user_in_ldap = 0; + my $login_succeeded = 0; + + my $hostname = $self->{'hostname'}; + my $basedn = $self->{'basedn'}; + my $authid = $self->{'authid'}; + my $authid_pass = $self->{'password'}; + $id_attr = $self->{'id_attr'} || $id_attr; + + my $ldap; + if ( $ldap = Net::LDAP->new($hostname) ) { + $hostname_is_ldap = 1; + if ( $ldap->bind( $authid, password => $authid_pass )->code == 0 ) { + $reached_ldap = 1; + # verify username + my $ldap_search = $ldap->search( base => $basedn, + filter => "($id_attr=$ldap_username)" ); + if ( $ldap_search->count != 0 ) { + $user_in_ldap = 1; + + # verify password (bind check) + my $binddn = "$id_attr=$ldap_username,$basedn"; + if ( $ldap->bind( $binddn, password => $password ) + ->code == 0 ) { + $login_succeeded = 1; + } + } + } + } + + if ( $login_succeeded ) { + return OpenILS::Event->new('SUCCESS'); + } elsif ( !$hostname_is_ldap ) { + # TODO: custom failure events? + $logger->debug("User login failed: Incorrect LDAP hostname"); + return OpenILS::Event->new( 'LOGIN_FAILED' ); + } elsif ( !$reached_ldap ) { + $logger->debug("User login failed: The LDAP server is misconfigured or unavailable"); + return OpenILS::Event->new( 'LOGIN_FAILED' ); + } elsif ( !$user_in_ldap ) { + $logger->debug("User login failed: Username $ldap_username not in LDAP"); + return OpenILS::Event->new( 'LOGIN_FAILED' ); + } else { + $logger->debug("User login failed: Incorrect LDAP password"); + return OpenILS::Event->new( 'LOGIN_FAILED' ); + } +} + +1; diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm index 105c3bd1af..4d7ac0a767 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm @@ -506,6 +506,9 @@ sub load_login { # initial log form only return Apache2::Const::OK unless $username and $password; + # Should we append an email hostname to the username? + my $ou_email_host = $cgi->param('ou_email_host') || ''; + my $auth_proxy_enabled = 0; # default false try { # if the service is not running, just let this fail silently $auth_proxy_enabled = $U->simplereq( @@ -529,6 +532,12 @@ sub load_login { if ($bc_regex and ($username =~ /$bc_regex/)) { $args->{barcode} = $username; } else { + # do we need to append an email hostname? + if ($ou_email_host) { + # Assume they already passed in an email address + next if $username =~ m/\@/; + $username .= "\@$ou_email_host"; + } $args->{username} = $username; } -- 2.11.0