1 package OpenILS::Application::AuthProxy::LDAP_Auth;
4 use base 'OpenILS::Application::AuthProxy::AuthBase';
7 use OpenSRF::Utils::SettingsClient;
8 use OpenSRF::Utils::Logger qw(:logger);
10 # default config var (override in configuration xml)
14 my ( $self, $args ) = @_;
15 my $username = $args->{'username'};
16 my $password = $args->{'password'};
19 $logger->debug("User login failed: No username provided");
20 return OpenILS::Event->new( 'LOGIN_FAILED' );
23 $logger->debug("User login failed: No password provided");
24 return OpenILS::Event->new( 'LOGIN_FAILED' );
27 my $hostname_is_ldap = 0;
30 my $login_succeeded = 0;
32 my $hostname = $self->{'hostname'};
33 my $basedn = $self->{'basedn'};
34 my $authid = $self->{'authid'};
35 my $authid_pass = $self->{'password'};
36 $id_attr = $self->{'id_attr'} || $id_attr;
37 my $bind_attr = $self->{'bind_attr'} || $id_attr; # use id_attr to bind if bind_attr not set
39 # bind_attr: name of LDAP attribute containing user's LDAP username
40 # id_attr: name of LDAP attribute containing user's Evergreen username
42 # Normally the LDAP username *is* the Evergreen username, in which case
43 # we don't need the extra step of getting the username from the LDAP entry.
44 # Thus, bind_attr and id_attr are the same. (This is the default scenario.)
46 # However, suppose we have two college libraries in a consortium. Each
47 # college has its own LDAP-based SSO solution. An LDAP username like
48 # "jsmith" may be in use at both libraries, for two different patrons.
49 # In this case, we can't use the LDAP username as the EG username, since
50 # every patron must have a unique username in EG.
52 # Here's how we handle this second scenario:
54 # 1. The user logs in with their LDAP username.
55 # 2. EG makes a bind request to the LDAP server using the LDAP username,
56 # which is in the LDAP attribute specified by bind_attr.
57 # 3. If the bind succeeds, we pull the user's EG username from the LDAP
58 # attribute specified by id_attr, and pass it along so that EG looks up the
61 # If bind_attr is not set, or if it specifies the same LDAP attribute as
62 # id_attr, we fallback to the default scenario.
64 my $username_from_ldap = $bind_attr eq $id_attr ? 0 : 1;
66 # When the EG username is retrieved from the LDAP server, we want to ensure
67 # that we bind using the actual username provided by the user.
68 if ($username_from_ldap) {
69 $username = $args->{'provided_username'} || $username;
74 if ( $ldap = Net::LDAP->new($hostname) ) {
75 $hostname_is_ldap = 1;
76 if ( $ldap->bind( $authid, password => $authid_pass )->code == 0 ) {
78 # verify username and lookup user's DN
79 $ldap_search = $ldap->search( base => $basedn,
80 filter => "($bind_attr=$username)" );
81 if ( $ldap_search->count != 0 ) {
84 # verify password (bind check)
85 my $binddn = $ldap_search->entry(0)->dn();
86 if ( $ldap->bind( $binddn, password => $password )
94 if ( $login_succeeded ) {
95 if ($username_from_ldap) {
96 my $id_attr_val = $ldap_search->entry(0)->get_value($id_attr);
97 return OpenILS::Event->new('SUCCESS', payload => $id_attr_val);
99 return OpenILS::Event->new('SUCCESS');
101 } elsif ( !$hostname_is_ldap ) {
102 # TODO: custom failure events?
103 $logger->debug("User login failed: Incorrect LDAP hostname");
104 return OpenILS::Event->new( 'LOGIN_FAILED' );
105 } elsif ( !$reached_ldap ) {
106 $logger->debug("User login failed: The LDAP server is misconfigured or unavailable");
107 return OpenILS::Event->new( 'LOGIN_FAILED' );
108 } elsif ( !$user_in_ldap ) {
109 $logger->debug("User login failed: Username $username not in LDAP");
110 return OpenILS::Event->new( 'LOGIN_FAILED' );
112 $logger->debug("User login failed: Incorrect LDAP password");
113 return OpenILS::Event->new( 'LOGIN_FAILED' );