use List::MoreUtils qw/ any /;
# use utf8;
-use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $debug $ldap $cas $caslogout);
+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $debug $ldap $cas $caslogout $shib $shib_login);
BEGIN {
sub psgi_env { any { /^psgi\./ } keys %ENV }
%EXPORT_TAGS = ( EditPermissions => [qw(get_all_subpermissions get_user_subpermissions)] );
$ldap = C4::Context->config('useldapserver') || 0;
$cas = C4::Context->preference('casAuthentication');
+ $shib = C4::Context->preference('shibbolethAuthentication');
$caslogout = C4::Context->preference('casLogout');
require C4::Auth_with_cas; # no import
+ require C4::Auth_with_Shibboleth;
if ($ldap) {
require C4::Auth_with_ldap;
import C4::Auth_with_ldap qw(checkpw_ldap);
}
+ if ($shib) {
+ import C4::Auth_with_Shibboleth qw(checkpw_shib logout_shib login_shib_url get_login_shib);
+ # Getting user login
+ $shib_login = get_login_shib();
+ }
if ($cas) {
import C4::Auth_with_cas qw(check_api_auth_cas checkpw_cas login_cas logout_cas login_cas_url);
}
my $casparam = $query->param('cas');
my $q_userid = $query->param('userid') // '';
- if ( $userid = $ENV{'REMOTE_USER'} ) {
- # Using Basic Authentication, no cookies required
+ # Basic authentication is incompatible with the use of Shibboleth,
+ # as Shibboleth may return REMOTE_USER as a Shibboleth attribute,
+ # and it may not be the attribute we want to use to match the koha login.
+ #
+ # Also, do not consider an empty REMOTE_USER.
+ #
+ # Finally, after those tests, we can assume (although if it would be better with
+ # a syspref) that if we get a REMOTE_USER, that's from basic authentication,
+ # and we can affect it to $userid.
+ if ( !$shib and $ENV{'REMOTE_USER'} ne '' and $userid = $ENV{'REMOTE_USER'} ) {
+
+ # Using Basic Authentication, no cookies required
$cookie = $query->cookie(
-name => 'CGISESSID',
-value => '',
$sessionID = undef;
$userid = undef;
- if ($cas and $caslogout) {
- logout_cas($query);
- }
+ if ($cas and $caslogout) {
+ logout_cas($query);
+ }
+
+ # If we are in a shibboleth session (shibboleth is enabled, and a shibboleth username is set)
+ if ( $shib and $shib_login and $type eq 'opac') {
+ # (Note: $type eq 'opac' condition should be removed when shibboleth authentication for intranet will be implemented)
+ logout_shib($query);
+ }
}
elsif ( !$lasttime || ($lasttime < time() - $timeout) ) {
# timed logout
}
if ( ( $cas && $query->param('ticket') )
|| $userid
+ || $shib
|| $pki_field ne 'None'
- || $persona )
+ || $persona )
{
my $password = $query->param('password');
my ( $return, $cardnumber );
- if ( $cas && $query->param('ticket') ) {
+ if ($shib && $shib_login && $type eq 'opac' && !$password) {
+ my $retuserid;
+ ( $return, $cardnumber, $retuserid ) = checkpw( $dbh, $userid, $password, $query );
+ $userid = $retuserid;
+ $info{'invalidShibLogin'} = 1 unless ($return);
+
+ } elsif ( $cas && $query->param('ticket') ) {
my $retuserid;
( $return, $cardnumber, $retuserid ) =
checkpw( $dbh, $userid, $password, $query );
login => 1,
INPUTS => \@inputs,
casAuthentication => C4::Context->preference("casAuthentication"),
+ shibbolethAuthentication => C4::Context->preference("shibbolethAuthentication"),
suggestion => C4::Context->preference("suggestion"),
virtualshelves => C4::Context->preference("virtualshelves"),
LibraryName => "" . C4::Context->preference("LibraryName"),
);
}
+ if ($shib) {
+ $template->param(
+ shibbolethLoginUrl => login_shib_url($query),
+ );
+ }
+
my $self_url = $query->url( -absolute => 1 );
$template->param(
url => $self_url,
return 0;
}
+ # If we are in a shibboleth session (shibboleth is enabled and no password has been provided)
+ if ($shib && !$password) {
+
+ $debug and print STDERR "## checkpw - checking Shibboleth\n";
+ # In case of a Shibboleth authentication, we expect a shibboleth user attribute
+ # (defined in the shibbolethLoginAttribute) tto contain the login of the
+ # shibboleth-authenticated user
+
+ # Shibboleth attributes are mapped into http environmement variables,
+ # so we're getting the login of the user this way
+ my $attributename = C4::Context->preference('shibbolethLoginAttribute');
+ my $attributevalue = $ENV{$attributename};
+
+ # Then, we check if it matches a valid koha user
+ if ($shib_login) {
+ my ( $retval, $retcard, $retuserid ) = C4::Auth_with_Shibboleth::checkpw_shib( $dbh, $shib_login ); # EXTERNAL AUTH
+ ($retval) and return ( $retval, $retcard, $retuserid );
+ return 0;
+ }
+ }
+
+ # INTERNAL AUTH
return checkpw_internal(@_)
}
--- /dev/null
+package C4::Auth_with_Shibboleth;
+
+# Copyright 2011 BibLibre
+#
+# This file is part of Koha.
+#
+# Koha is free software; you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 2 of the License, or (at your option) any later
+# version.
+#
+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with Koha; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+use strict;
+use warnings;
+
+use C4::Debug;
+use C4::Context;
+use C4::Utils qw( :all );
+use CGI;
+
+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $debug);
+
+BEGIN {
+ require Exporter;
+ $VERSION = 3.03; # set the version for version checking
+ $debug = $ENV{DEBUG};
+ @ISA = qw(Exporter);
+ @EXPORT = qw(logout_shib login_shib_url checkpw_shib get_login_shib);
+}
+my $context = C4::Context->new() or die 'C4::Context->new failed';
+my $protocol = "https://";
+
+# Logout from Shibboleth
+sub logout_shib {
+ my ($query) = @_;
+ my $uri = $protocol . $ENV{'SERVER_NAME'};
+ print $query->redirect( $uri . "/Shibboleth.sso/Logout?return=$uri" );
+}
+
+# Returns Shibboleth login URL with callback to the requesting URL
+sub login_shib_url {
+
+ my ($query) = @_;
+ my $param = $protocol . $ENV{'SERVER_NAME'} . $query->script_name();
+ my $uri = $protocol . $ENV{'SERVER_NAME'} . "/Shibboleth.sso/Login?target=$param";
+ return $uri;
+}
+
+# Returns shibboleth user login
+sub get_login_shib {
+
+ # In case of a Shibboleth authentication, we expect a shibboleth user attribute (defined in the shibbolethLoginAttribute)
+ # to contain the login of the shibboleth-authenticated user
+
+ # Shibboleth attributes are mapped into http environmement variables,
+ # so we're getting the login of the user this way
+
+ my $shibbolethLoginAttribute = C4::Context->preference('shibbolethLoginAttribute');
+ $debug and warn "shibbolethLoginAttribute value: $shibbolethLoginAttribute";
+ $debug and warn "$shibbolethLoginAttribute value: " . $ENV{$shibbolethLoginAttribute};
+
+ return $ENV{$shibbolethLoginAttribute};
+}
+
+# Checks for password correctness
+# In our case : does the given username matches one of our users ?
+sub checkpw_shib {
+ $debug and warn "checkpw_shib";
+
+ my ( $dbh, $userid ) = @_;
+ my $retnumber;
+ $debug and warn "User Shibboleth-authenticated as: $userid";
+
+ # Does it match one of our users ?
+ my $sth = $dbh->prepare("select cardnumber from borrowers where userid=?");
+ $sth->execute($userid);
+ if ( $sth->rows ) {
+ $retnumber = $sth->fetchrow;
+ return ( 1, $retnumber, $userid );
+ }
+ $sth = $dbh->prepare("select userid from borrowers where cardnumber=?");
+ $sth->execute($userid);
+ if ( $sth->rows ) {
+ $retnumber = $sth->fetchrow;
+ return ( 1, $retnumber, $userid );
+ }
+
+ # If we reach this point, the user is not a valid koha user
+ $debug and warn "User $userid is not a valid Koha user";
+ return 0;
+}
+
+1;
}
+$DBversion = "XXX";
+if (C4::Context->preference("Version") < TransformToNum($DBversion)) {
+ $dbh->do("INSERT INTO `systempreferences` (variable,value,options,explanation,type) VALUES('shibbolethAuthentication','','','Enable or disable Shibboleth authentication','YesNo')");
+ $dbh->do("INSERT INTO `systempreferences` (variable,value,options,explanation,type) VALUES('shibbolethLoginAttribute','','','Which shibboleth user attribute should be used to match koha user login?','')");
+ print "Upgrade to $DBversion done (Adds shibbolethAuthentication and shibbolethLoginAttribute preferences)\n";
+ SetVersion ($DBversion);
+}
+
=head1 FUNCTIONS
=head2 TableExists($table)
yes: Allow
no: "Don't Allow"
- Mozilla persona for login
+ Shibboleth Authentication:
+ -
+ - pref: shibbolethAuthentication
+ default: 0
+ choices:
+ yes: Use
+ no: "Don't use"
+ - Shibboleth for login authentication.
+ -
+ - Which shibboleth user attribute should be used to match koha user login?
+ - pref: shibbolethLoginAttribute
+ Search Engine:
+ -
+ - pref: SearchEngine
+ default: Zebra
+ choices:
+ Solr: Solr
+ Zebra: Zebra
+ - is the search engine used.
<p>You entered an incorrect username or password. Please try again! And remember, usernames and passwords are case sensitive.</p>
[% END %]
+[% IF ( shibbolethAuthentication ) %]
+<h4>Shibboleth Login</h4>
+
+[% IF ( invalidShibLogin ) %]
+<!-- This is what is displated if shibboleth login has failed -->
+<p>Sorry, the shibboleth login failed.</p>
+[% END %]
+
+<p>If you have a shibboleth account,
+please <a href="[% shibbolethLoginUrl %]">click here to login</a>.</p>
+
+<h4>Local Login</h4>
+<p>If you do not have a shibboleth account, but a local account, you can still log in : </p>
+
+[% END %]
+
[% IF ( casAuthentication ) %]
<h4>Cas login</h4>
[% IF ( opacuserlogin ) %]
[% UNLESS ( loggedinusername ) %]
[% UNLESS ( casAuthentication ) %]
+ [% UNLESS ( shibbolethAuthentication ) %]
<div id="login" class="container clearfix">
<form action="/cgi-bin/koha/opac-user.pl" method="post" name="auth" id="auth">
<input type="hidden" name="koha_login_context" value="opac" />
casAuthentication => $casAuthentication,
);
+my $shibbolethAuthentication = C4::Context->preference('shibbolethAuthentication');
+$template->param( shibbolethAuthentication => $shibbolethAuthentication);
# display news
# use cookie setting for language, bug default to syspref if it's not set
my $patronupdate = $query->param('patronupdate');
my $canrenew = 1;
+my $shibbolethAuthentication = C4::Context->preference('shibbolethAuthentication');
+$template->param( shibbolethAuthentication => $shibbolethAuthentication );
+
# get borrower information ....
my ( $borr ) = GetMemberDetails( $borrowernumber );