return OpenILS::Event->new( 'LOGIN_FAILED' )
unless (&enabled() and ($args->{'username'} or $args->{'barcode'}));
+ # provided username may not be the user's actual EG username;
+ # hang onto the provided value (if any) so we can use it later
+ $args->{'provided_username'} = $args->{'username'};
+
if ($args->{barcode} and !$args->{username}) {
# translate barcode logins into username logins by locating
# the matching card/user and collecting the username.
if ($code) {
push @error_events, $event;
} elsif (defined $code) { # code is '0', i.e. SUCCESS
- if (exists $event->{'payload'}) { # we have a complete native login
+ if ($authenticator->name eq 'native' and exists $event->{'payload'}) { # we have a complete native login
return $event;
} else { # create an EG session for the successful external login
- #
+ # if external login returns a payload, that payload is the
+ # user's Evergreen username
+ if ($event->{'payload'}) {
+ $args->{'username'} = $event->{'payload'};
+ }
+
# before we actually create the session, let's first check if
# Evergreen thinks this user is allowed to login
#
$logger->debug("Authenticated username '" . $args->{'username'} . "' has no Evergreen account, aborting");
return OpenILS::Event->new( 'LOGIN_FAILED' );
} else {
+ # TODO: verify that this authenticator is allowed to do auth
+ # for the specified username (i.e. if the authenticator is for
+ # Library A only, it shouldn't be able to do auth for
+ # Library B's users)
$args->{user_id} = $user->[0]->id;
}
my $authid = $self->{'authid'};
my $authid_pass = $self->{'password'};
$id_attr = $self->{'id_attr'} || $id_attr;
+ my $bind_attr = $self->{'bind_attr'} || $id_attr; # use id_attr to bind if bind_attr not set
+
+ # bind_attr: name of LDAP attribute containing user's LDAP username
+ # id_attr: name of LDAP attribute containing user's Evergreen username
+ #
+ # Normally the LDAP username *is* the Evergreen username, in which case
+ # we don't need the extra step of getting the username from the LDAP entry.
+ # Thus, bind_attr and id_attr are the same. (This is the default scenario.)
+ #
+ # However, suppose we have two college libraries in a consortium. Each
+ # college has its own LDAP-based SSO solution. An LDAP username like
+ # "jsmith" may be in use at both libraries, for two different patrons.
+ # In this case, we can't use the LDAP username as the EG username, since
+ # every patron must have a unique username in EG.
+ #
+ # Here's how we handle this second scenario:
+ #
+ # 1. The user logs in with their LDAP username.
+ # 2. EG makes a bind request to the LDAP server using the LDAP username,
+ # which is in the LDAP attribute specified by bind_attr.
+ # 3. If the bind succeeds, we pull the user's EG username from the LDAP
+ # attribute specified by id_attr, and pass it along so that EG looks up the
+ # correct user.
+ #
+ # If bind_attr is not set, or if it specifies the same LDAP attribute as
+ # id_attr, we fallback to the default scenario.
+ #
+ my $username_from_ldap = $bind_attr eq $id_attr ? 0 : 1;
+
+ # When the EG username is retrieved from the LDAP server, we want to ensure
+ # that we bind using the actual username provided by the user.
+ if ($username_from_ldap) {
+ $username = $args->{'provided_username'} || $username;
+ }
my $ldap;
+ my $ldap_search;
if ( $ldap = Net::LDAP->new($hostname) ) {
$hostname_is_ldap = 1;
if ( $ldap->bind( $authid, password => $authid_pass )->code == 0 ) {
$reached_ldap = 1;
# verify username and lookup user's DN
- my $ldap_search = $ldap->search( base => $basedn,
- filter => "($id_attr=$username)" );
+ $ldap_search = $ldap->search( base => $basedn,
+ filter => "($bind_attr=$username)" );
if ( $ldap_search->count != 0 ) {
$user_in_ldap = 1;
}
if ( $login_succeeded ) {
- return OpenILS::Event->new('SUCCESS');
+ if ($username_from_ldap) {
+ my $id_attr_val = $ldap_search->entry(0)->get_value($id_attr);
+ return OpenILS::Event->new('SUCCESS', payload => $id_attr_val);
+ } else {
+ return OpenILS::Event->new('SUCCESS');
+ }
} elsif ( !$hostname_is_ldap ) {
# TODO: custom failure events?
$logger->debug("User login failed: Incorrect LDAP hostname");