Bugzilla – Attachment 41124 Details for
Bug 7174
Authentication rewriting
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 7174 - Authentication Rewrite
Bug-7174---Authentication-Rewrite.patch (text/plain), 80.37 KB, created by
Olli-Antti Kivilahti
on 2015-07-22 11:25:29 UTC
(
hide
)
Description:
Bug 7174 - Authentication Rewrite
Filename:
MIME Type:
Creator:
Olli-Antti Kivilahti
Created:
2015-07-22 11:25:29 UTC
Size:
80.37 KB
patch
obsolete
>From 5ff962c3de6cf5633c9871ad3c7a43ba1cf23599 Mon Sep 17 00:00:00 2001 >From: Olli-Antti Kivilahti <olli-antti.kivilahti@jns.fi> >Date: Tue, 21 Jul 2015 06:54:53 +0000 >Subject: [PATCH] Bug 7174 - Authentication Rewrite > >Depends heavily on Buugg 14540, which introduces many tests to spot regression >caused by this feature. > >Introduces a new modular authentication system based on Exception signaling >and reusable authentication components/challenges. >This system is cross-framework -compatible, currently supporting Mojolicious >and CGI, but adding support for any other framework is straightforward. > >Central idea is to provide a single authentication function to deal with all >types of authentication scenarios, REST API, Cookie, Password, LDAP... >Also it is important to make a system that is easy to extend and can deal with >many future authentication scenarios. > >Currently only CGI password, LDAP and cookie login is tested. Legacy behaviour >is used to deal with other types of authentication. > >Deprecates a lot of subroutines in C4::Auth. > >See the attached schematic in Bugzilla for a more architectural overview. > >The basic principle is: >1. We get an authentication request from any Web/GUI-framework, for ex CGI. >2. Request is normalized by extracting all necessary authentication data > elements to a separate data structure. >3. Based on the found authentication data elements, system decides which > authentication route to take. >4. Route implements all challenges needed to authenticate the request. > Route returns the authenticated Koha::Borrower or an Exception if login > failed. >5. The user environment/session is set/deleted based on the Route result. >6. a Koha::Borrower and the CGISESSID-cookie is returned to the calling > framework in the format the framework needs. >7. Framework needs to catch possible exceptions and deal with them. > Eg. login failed, no permission, under maintenance. >8. Authentication succeeds and session is set, or failure is reported to user. > >----------------------------------- >-::- TEST PLAN: -::- >----------------------------------- > >Use PageObject tests from Buugg 14540 and Buugg 14536 to test against regression. > >::MANUAL TESTS:: >OPAC: >1. Go to opac-main.pl >1.1. Try to access a restricted page like /cgi-bin/koha/opac-account.pl >2. Login with password. >3. Navigate to any OPAC page requiring login. >4. Observe that the login is still valid and correct username is displayed on > top right of the browser window. >5. Logout >5.1. Try to access a restricted page. >6. Login again. >7. Browse a bit. >8. Logout. > >Anonymous search history (OPAC): >1. Make a search >2. Make another search >3. Login >4. Make yet another search >5. Go to my search history. >6. Observe that the last three searches are shown in your search history. >7. Logout > >STAFF CLIENT (Intra): >0. Try to access a restricted page, like /cgi-bin/koha/mainpage.pl >1. Login >2. Navigate a bit to confirm that the login remains active. >3. Logout. >4. Login again. >5. Navigate to a restricted page. >6. Logout. >6.1. Try to access a restricted page. >--- > C4/Auth.pm | 289 +++++++++++++------- > C4/Auth_with_ldap.pm | 11 +- > C4/Context.pm | 9 +- > Koha/Auth.pm | 225 +++++++++++++++ > Koha/Auth/Component.pm | 302 +++++++++++++++++++++ > Koha/Auth/Component/Password.pm | 91 +++++++ > Koha/Auth/RequestNormalizer.pm | 153 +++++++++++ > Koha/Auth/Route.pm | 69 +++++ > Koha/Auth/Route/Cookie.pm | 41 +++ > Koha/Auth/Route/Password.pm | 42 +++ > Koha/Auth/Route/RESTV1.pm | 41 +++ > Koha/AuthUtils.pm | 65 ++++- > Koha/Borrower.pm | 26 ++ > koha-tmpl/intranet-tmpl/prog/en/modules/auth.tt | 4 +- > .../opac-tmpl/bootstrap/en/modules/opac-auth.tt | 4 +- > opac/opac-user.pl | 2 +- > t/db_dependent/Auth_with_ldap.t | 18 +- > t/db_dependent/Koha/Borrower.t | 55 ++++ > 18 files changed, 1336 insertions(+), 111 deletions(-) > create mode 100644 Koha/Auth.pm > create mode 100644 Koha/Auth/Component.pm > create mode 100644 Koha/Auth/Component/Password.pm > create mode 100644 Koha/Auth/RequestNormalizer.pm > create mode 100644 Koha/Auth/Route.pm > create mode 100644 Koha/Auth/Route/Cookie.pm > create mode 100644 Koha/Auth/Route/Password.pm > create mode 100644 Koha/Auth/Route/RESTV1.pm > create mode 100644 t/db_dependent/Koha/Borrower.t > >diff --git a/C4/Auth.pm b/C4/Auth.pm >index 3743869..77cdfa9 100644 >--- a/C4/Auth.pm >+++ b/C4/Auth.pm >@@ -19,7 +19,7 @@ package C4::Auth; > > use strict; > use warnings; >-use Digest::MD5 qw(md5_base64); >+use Digest::MD5 qw(md5_base64); #@DEPRECATED Digest::MD5, don't use it or you will get hurt. > use JSON qw/encode_json/; > use URI::Escape; > use CGI::Session; >@@ -34,6 +34,8 @@ use C4::Branch; # GetBranches > use C4::Search::History; > use Koha; > use Koha::AuthUtils qw(hash_password); >+use Koha::Auth; >+use Koha::Auth::Component; > use POSIX qw/strftime/; > use List::MoreUtils qw/ any /; > use Encode qw( encode is_utf8); >@@ -636,50 +638,64 @@ has authenticated. > > =cut > >+=head _version_check >+#@DEPRECATED See Bug 7174 >+use Koha::Auth::Component::* instead >+=cut >+ > sub _version_check { >+ #@DEPRECATED See Bug 7174 > my $type = shift; > my $query = shift; > my $version; > >- # If version syspref is unavailable, it means Koha is being installed, >- # and so we must redirect to OPAC maintenance page or to the WebInstaller >- # also, if OpacMaintenance is ON, OPAC should redirect to maintenance >- if ( C4::Context->preference('OpacMaintenance') && $type eq 'opac' ) { >- warn "OPAC Install required, redirecting to maintenance"; >- print $query->redirect("/cgi-bin/koha/maintenance.pl"); >- safe_exit; >- } >- unless ( $version = C4::Context->preference('Version') ) { # assignment, not comparison >- if ( $type ne 'opac' ) { >- warn "Install required, redirecting to Installer"; >- print $query->redirect("/cgi-bin/koha/installer/install.pl"); >- } else { >- warn "OPAC Install required, redirecting to maintenance"; >- print $query->redirect("/cgi-bin/koha/maintenance.pl"); >+ try { >+ Koha::Auth::Component::checkOPACMaintenance() if ( $type eq 'opac' ); >+ Koha::Auth::Component::checkVersion(); >+ } catch { >+ if (blessed($_)) { >+ if ($_->isa('Koha::Exception::VersionMismatch')) { >+ # check that database and koha version are the same >+ # there is no DB version, it's a fresh install, >+ # go to web installer >+ # there is a DB version, compare it to the code version >+ my $warning = $_->error()." Redirecting to %s."; >+ if ( $type ne 'opac' ) { >+ warn sprintf( $warning, 'Installer' ); >+ print $query->redirect("/cgi-bin/koha/installer/install.pl?step=1&op=updatestructure"); >+ } else { >+ warn sprintf( "OPAC: " . $warning, 'maintenance' ); >+ print $query->redirect("/cgi-bin/koha/maintenance.pl"); >+ } >+ safe_exit; >+ } >+ elsif ($_->isa('Koha::Exception::ServiceTemporarilyUnavailable')) { >+ # also, if OpacMaintenance is ON, OPAC should redirect to maintenance >+ warn "OPAC Install required, redirecting to maintenance"; >+ print $query->redirect("/cgi-bin/koha/maintenance.pl"); >+ safe_exit; >+ } >+ elsif ($_->isa('Koha::Exception::BadSystemPreference')) { >+ # If version syspref is unavailable, it means Koha is being installed, >+ # and so we must redirect to OPAC maintenance page or to the WebInstaller >+ if ( $type ne 'opac' ) { >+ warn "Install required, redirecting to Installer"; >+ print $query->redirect("/cgi-bin/koha/installer/install.pl"); >+ } else { >+ warn "OPAC Install required, redirecting to maintenance"; >+ print $query->redirect("/cgi-bin/koha/maintenance.pl"); >+ } >+ safe_exit; >+ } >+ else { >+ warn "Unknown exception class ".ref($_)."\n"; >+ die $_->rethrow(); #Unhandled exception case >+ } > } >- safe_exit; >- } >- >- # check that database and koha version are the same >- # there is no DB version, it's a fresh install, >- # go to web installer >- # there is a DB version, compare it to the code version >- my $kohaversion = Koha::version(); >- >- # remove the 3 last . to have a Perl number >- $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/; >- $debug and print STDERR "kohaversion : $kohaversion\n"; >- if ( $version < $kohaversion ) { >- my $warning = "Database update needed, redirecting to %s. Database is $version and Koha is $kohaversion"; >- if ( $type ne 'opac' ) { >- warn sprintf( $warning, 'Installer' ); >- print $query->redirect("/cgi-bin/koha/installer/install.pl?step=1&op=updatestructure"); >- } else { >- warn sprintf( "OPAC: " . $warning, 'maintenance' ); >- print $query->redirect("/cgi-bin/koha/maintenance.pl"); >+ else { >+ die $_; #Not a Koha::Exception-object, so rethrow it > } >- safe_exit; >- } >+ }; > } > > sub _session_log { >@@ -699,7 +715,101 @@ sub _timeout_syspref { > return $timeout; > } > >+=head checkauth >+ >+Compatibility layer for old Koha authentication system. >+Tries to authenticate using Koha::Auth, but if no authentication mechanism is >+identified, falls back to the deprecated legacy behaviour. >+=cut >+ > sub checkauth { >+ my @params = @_; #Clone params so we don't accidentally change them if we fallback to checkauth_legacy() >+ my $query = shift; >+ my $authnotrequired = shift; >+ my $flagsrequired = _changeAllPermissionsMarkerToAnyPermissionMarker(shift); #circulate => 1 is deprecated, only circulate => '*' is supported. >+ my $type = shift; >+ my $persona = shift; >+ >+ ##Revert to legacy authentication for more complex authentication mechanisms. >+ if ($persona && $cas && $shib) { >+ return checkauth_legacy(@params); >+ } >+ >+ my ($borrower, $cookie); >+ try { >+ ($borrower, $cookie) = Koha::Auth::authenticate($query, $flagsrequired, {authnotrequired => $authnotrequired}); >+ } catch { >+ if (blessed($_)) { >+ if ($_->isa('Koha::Exception::VersionMismatch')) { >+ >+ my $warning = $_->error()." Redirecting to %s."; >+ if ( $type ne 'opac' ) { >+ warn sprintf( $warning, 'Installer' ); >+ print $query->redirect("/cgi-bin/koha/installer/install.pl?step=1&op=updatestructure"); >+ } else { >+ warn sprintf( "OPAC: " . $warning, 'maintenance' ); >+ print $query->redirect("/cgi-bin/koha/maintenance.pl"); >+ } >+ safe_exit; >+ >+ } >+ elsif ($_->isa('Koha::Exception::BadSystemPreference')) { >+ >+ if ( $type ne 'opac' ) { >+ warn $_->error()." Redirecting to installer."; >+ print $query->redirect("/cgi-bin/koha/installer/install.pl"); >+ } else { >+ warn $_->error()." Redirecting to maintenance."; >+ print $query->redirect("/cgi-bin/koha/maintenance.pl"); >+ } >+ safe_exit; >+ >+ } >+ elsif ($_->isa('Koha::Exception::LoginFailed')) { >+ _showLoginPage($query, $type, {invalid_username_or_password => 1}, {}, undef); >+ } >+ elsif ($_->isa('Koha::Exception::NoPermission')) { >+ _showLoginPage($query, $type, {nopermission => 1}, {}, undef); >+ } >+ elsif ($_->isa('Koha::Exception::Logout')) { >+ _showLoginPage($query, $type, {}, {}, undef); >+ } >+ elsif ($_->isa('Koha::Exception::ServiceTemporarilyUnavailable')) { >+ warn $_->error(); >+ print $query->redirect("/cgi-bin/koha/maintenance.pl"); >+ safe_exit; >+ } >+ else { >+ warn "Unknown exception class ".ref($_)."\n"; >+ $_->rethrow(); #Unhandled exception case >+ } >+ } >+ else { >+ die $_; #Not a Koha::Exception-object >+ } >+ }; >+ >+ if ($borrower) { #We authenticated succesfully! Emulate the legacy interface for get_template_and_user(); >+ my $user = $borrower->userid; >+ my $sessionID = $cookie->{value}->[0]; >+ my $aa = $borrower->userid; >+ my $flags = getuserflags(undef, $borrower->userid, undef) if $borrower->userid; >+ return ( $user, $cookie, $sessionID, $flags ); >+ } >+} >+ >+=head checkauth_legacy >+@DEPRECATED See Bug 7174 >+ >+We are calling this because the given authentication mechanism is not yet supported >+in Koha::Auth. >+ >+See checkauth-documentation floating somewhere in this file for info about the >+legacy authentication. >+=cut >+ >+sub checkauth_legacy { >+ #@DEPRECATED See Bug 7174 > my $query = shift; > $debug and warn "Checking Auth"; > >@@ -721,8 +831,6 @@ sub checkauth { > my ( $userid, $cookie, $sessionID, $flags, $barshelves, $pubshelves ); > my $logout = $query->param('logout.x'); > >- my $anon_search_history; >- > # This parameter is the name of the CAS server we want to authenticate against, > # when using authentication against multiple CAS servers, as configured in Auth_cas_servers.yaml > my $casparam = $query->param('cas'); >@@ -783,7 +891,6 @@ sub checkauth { > #if a user enters an id ne to the id in the current session, we need to log them in... > #first we need to clear the anonymous session... > $debug and warn "query id = $q_userid but session id = $s_userid"; >- $anon_search_history = $session->param('search_history'); > $session->delete(); > $session->flush; > C4::Context->_unset_userenv($sessionID); >@@ -864,13 +971,6 @@ sub checkauth { > #we initiate a session prior to checking for a username to allow for anonymous sessions... > my $session = get_session("") or die "Auth ERROR: Cannot get_session()"; > >- # Save anonymous search history in new session so it can be retrieved >- # by get_template_and_user to store it in user's search history after >- # a successful login. >- if ($anon_search_history) { >- $session->param( 'search_history', $anon_search_history ); >- } >- > my $sessionID = $session->id; > C4::Context->_new_userenv($sessionID); > $cookie = $query->cookie( >@@ -989,12 +1089,12 @@ sub checkauth { > $info{'nopermission'} = 1; > C4::Context->_unset_userenv($sessionID); > } >- my ( $borrowernumber, $firstname, $surname, $userflags, >+ my ( $borrowernumber, $firstname, $surname, > $branchcode, $branchname, $branchprinter, $emailaddress ); > > if ( $return == 1 ) { > my $select = " >- SELECT borrowernumber, firstname, surname, flags, borrowers.branchcode, >+ SELECT borrowernumber, firstname, surname, borrowers.branchcode, > branches.branchname as branchname, > branches.branchprinter as branchprinter, > email >@@ -1017,10 +1117,10 @@ sub checkauth { > } > } > if ( $sth->rows ) { >- ( $borrowernumber, $firstname, $surname, $userflags, >+ ( $borrowernumber, $firstname, $surname, > $branchcode, $branchname, $branchprinter, $emailaddress ) = $sth->fetchrow; > $debug and print STDERR "AUTH_3 results: " . >- "$cardnumber,$borrowernumber,$userid,$firstname,$surname,$userflags,$branchcode,$emailaddress\n"; >+ "$cardnumber,$borrowernumber,$userid,$firstname,$surname,$branchcode,$emailaddress\n"; > } else { > print STDERR "AUTH_3: no results for userid='$userid', cardnumber='$cardnumber'.\n"; > } >@@ -1036,7 +1136,7 @@ sub checkauth { > $branchname = GetBranchName($branchcode); > } > my $branches = GetBranches(); >- if ( C4::Context->boolean_preference('IndependentBranches') && C4::Context->boolean_preference('Autolocation') ) { >+ if ( C4::Context->boolean_preference('IndependentBranches') && C4::Context->boolean_preference('Autolocation') ) { #Why Autolocation cannot work without IndependetBranches?? > > # we have to check they are coming from the right ip range > my $domain = $branches->{$branchcode}->{'branchip'}; >@@ -1066,7 +1166,6 @@ sub checkauth { > $session->param( 'surname', $surname ); > $session->param( 'branch', $branchcode ); > $session->param( 'branchname', $branchname ); >- $session->param( 'flags', $userflags ); > $session->param( 'emailaddress', $emailaddress ); > $session->param( 'ip', $session->remote_addr() ); > $session->param( 'lasttime', time() ); >@@ -1149,6 +1248,12 @@ sub checkauth { > # > > # get the inputs from the incoming query >+ _showLoginPage($query, $type, $cookie, \%info, $casparam); >+} >+ >+sub _showLoginPage { >+ my ($query, $type, $cookie, $info, $casparam) = @_; >+ > my @inputs = (); > foreach my $name ( param $query) { > (next) if ( $name eq 'userid' || $name eq 'password' || $name eq 'ticket' ); >@@ -1200,7 +1305,7 @@ sub checkauth { > IntranetUserJS => C4::Context->preference("IntranetUserJS"), > IndependentBranches => C4::Context->preference("IndependentBranches"), > AutoLocation => C4::Context->preference("AutoLocation"), >- wrongip => $info{'wrongip'}, >+ wrongip => $info->{'wrongip'}, > PatronSelfRegistration => C4::Context->preference("PatronSelfRegistration"), > PatronSelfRegistrationDefaultCategory => C4::Context->preference("PatronSelfRegistrationDefaultCategory"), > persona => C4::Context->preference("Persona"), >@@ -1208,7 +1313,7 @@ sub checkauth { > ); > > $template->param( OpacPublic => C4::Context->preference("OpacPublic") ); >- $template->param( loginprompt => 1 ) unless $info{'nopermission'}; >+ $template->param( loginprompt => 1 ) unless $info->{'nopermission'}; > > if ( $type eq 'opac' ) { > require C4::VirtualShelves; >@@ -1238,7 +1343,7 @@ sub checkauth { > } > > $template->param( >- invalidCasLogin => $info{'invalidCasLogin'} >+ invalidCasLogin => $info->{'invalidCasLogin'} > ); > } > >@@ -1254,7 +1359,7 @@ sub checkauth { > url => $self_url, > LibraryName => C4::Context->preference("LibraryName"), > ); >- $template->param(%info); >+ $template->param(%$info); > > # $cookie = $query->cookie(CGISESSID => $session->id > # ); >@@ -1268,6 +1373,7 @@ sub checkauth { > } > > =head2 check_api_auth >+@DEPRECATED See Bug 7174 > > ($status, $cookie, $sessionId) = check_api_auth($query, $userflags); > >@@ -1302,6 +1408,7 @@ Possible return values in C<$status> are: > =cut > > sub check_api_auth { >+ #@DEPRECATED See Bug 7174 > my $query = shift; > my $flagsrequired = shift; > >@@ -1433,17 +1540,17 @@ sub check_api_auth { > if ( $return == 1 ) { > my ( > $borrowernumber, $firstname, $surname, >- $userflags, $branchcode, $branchname, >+ $branchcode, $branchname, > $branchprinter, $emailaddress > ); > my $sth = > $dbh->prepare( >-"select borrowernumber, firstname, surname, flags, borrowers.branchcode, branches.branchname as branchname,branches.branchprinter as branchprinter, email from borrowers left join branches on borrowers.branchcode=branches.branchcode where userid=?" >+"select borrowernumber, firstname, surname, borrowers.branchcode, branches.branchname as branchname,branches.branchprinter as branchprinter, email from borrowers left join branches on borrowers.branchcode=branches.branchcode where userid=?" > ); > $sth->execute($userid); > ( > $borrowernumber, $firstname, $surname, >- $userflags, $branchcode, $branchname, >+ $branchcode, $branchname, > $branchprinter, $emailaddress > ) = $sth->fetchrow if ( $sth->rows ); > >@@ -1454,14 +1561,14 @@ sub check_api_auth { > $sth->execute($cardnumber); > ( > $borrowernumber, $firstname, $surname, >- $userflags, $branchcode, $branchname, >+ $branchcode, $branchname, > $branchprinter, $emailaddress > ) = $sth->fetchrow if ( $sth->rows ); > > unless ( $sth->rows ) { > $sth->execute($userid); > ( >- $borrowernumber, $firstname, $surname, $userflags, >+ $borrowernumber, $firstname, $surname, > $branchcode, $branchname, $branchprinter, $emailaddress > ) = $sth->fetchrow if ( $sth->rows ); > } >@@ -1495,7 +1602,6 @@ sub check_api_auth { > $session->param( 'surname', $surname ); > $session->param( 'branch', $branchcode ); > $session->param( 'branchname', $branchname ); >- $session->param( 'flags', $userflags ); > $session->param( 'emailaddress', $emailaddress ); > $session->param( 'ip', $session->remote_addr() ); > $session->param( 'lasttime', time() ); >@@ -1529,6 +1635,7 @@ sub check_api_auth { > } > > =head2 check_cookie_auth >+@DEPRECATED See Bug 7174 > > ($status, $sessionId) = check_api_auth($cookie, $userflags); > >@@ -1556,6 +1663,7 @@ Possible return values in C<$status> are: > =cut > > sub check_cookie_auth { >+ #@DEPRECATED See Bug 7174 > my $cookie = shift; > my $flagsrequired = shift; > >@@ -1679,12 +1787,13 @@ sub get_session { > return $session; > } > >+#@DEPRECATED See Bug 7174 > sub checkpw { > my ( $dbh, $userid, $password, $query, $type ) = @_; > $type = 'opac' unless $type; > if ($ldap) { > $debug and print STDERR "## checkpw - checking LDAP\n"; >- my ( $retval, $retcard, $retuserid ) = checkpw_ldap(@_); # EXTERNAL AUTH >+ my ( $retval, $retcard, $retuserid ) = checkpw_ldap($userid, $password); # EXTERNAL AUTH > return 0 if $retval == -1; # Incorrect password for LDAP login attempt > ($retval) and return ( $retval, $retcard, $retuserid ); > } >@@ -1723,6 +1832,7 @@ sub checkpw { > return checkpw_internal(@_) > } > >+#@DEPRECATED See Bug 7174 > sub checkpw_internal { > my ( $dbh, $userid, $password ) = @_; > >@@ -1748,13 +1858,13 @@ sub checkpw_internal { > $sth->execute($userid); > if ( $sth->rows ) { > my ( $stored_hash, $cardnumber, $borrowernumber, $userid, $firstname, >- $surname, $branchcode, $branchname, $flags ) >+ $surname, $branchcode, $branchname ) > = $sth->fetchrow; > > if ( checkpw_hash( $password, $stored_hash ) ) { > > C4::Context->set_userenv( "$borrowernumber", $userid, $cardnumber, >- $firstname, $surname, $branchcode, $branchname, $flags ); >+ $firstname, $surname, $branchcode, $branchname ); > return 1, $cardnumber, $userid; > } > } >@@ -1775,6 +1885,8 @@ sub checkpw_internal { > return 1, $cardnumber, $userid; > } > } >+ >+ #@DEPRECATED see Bug 7174. I think the demo-user should be represented with permissions instead of a hard-coded non-borrower anomaly. > if ( $userid && $userid eq 'demo' > && "$password" eq 'demo' > && C4::Context->config('demo') ) >@@ -1787,6 +1899,7 @@ sub checkpw_internal { > return 0; > } > >+#@DEPRECATED See Bug 7174 > sub checkpw_hash { > my ( $password, $stored_hash ) = @_; > >@@ -1797,6 +1910,7 @@ sub checkpw_hash { > if ( substr( $stored_hash, 0, 2 ) eq '$2' ) { > $hash = hash_password( $password, $stored_hash ); > } else { >+ #@DEPRECATED Digest::MD5, don't use it or you will get hurt. > $hash = md5_base64($password); > } > return $hash eq $stored_hash; >@@ -1820,7 +1934,6 @@ sub getuserflags { > my $flags = shift; > my $userid = shift; > my $dbh = @_ ? shift : C4::Context->dbh; >- my $userflags; > { > # I don't want to do this, but if someone logs in as the database > # user, it would be preferable not to spam them to death with >@@ -1829,28 +1942,6 @@ sub getuserflags { > $flags += 0; > } > return get_user_subpermissions($userid); >- >- #@DEPRECATED, USE THE Koha::Auth::PermissionManager >- my $sth = $dbh->prepare("SELECT bit, flag, defaulton FROM userflags"); >- $sth->execute; >- >- while ( my ( $bit, $flag, $defaulton ) = $sth->fetchrow ) { >- if ( ( $flags & ( 2**$bit ) ) || $defaulton ) { >- $userflags->{$flag} = 1; >- } >- else { >- $userflags->{$flag} = 0; >- } >- } >- >- # get subpermissions and merge with top-level permissions >- my $user_subperms = get_user_subpermissions($userid); >- foreach my $module ( keys %$user_subperms ) { >- next if $userflags->{$module} == 1; # user already has permission for everything in this module >- $userflags->{$module} = $user_subperms->{$module}; >- } >- >- return $userflags; > } > > =head2 get_user_subpermissions >@@ -1952,9 +2043,7 @@ sub haspermission { > > my $flags = getuserflags( undef, $userid ); > #Sanitate 1 to * because we no longer have 1's from the koha.borrowers.flags. >- foreach my $module (%$flagsrequired) { >- $flagsrequired->{$module} = '*' if $flagsrequired->{$module} && $flagsrequired->{$module} eq '1'; >- } >+ _changeAllPermissionsMarkerToAnyPermissionMarker($flagsrequired); > > if ( $userid eq C4::Context->config('user') ) { > >@@ -1989,7 +2078,21 @@ sub haspermission { > #FIXME - This fcn should return the failed permission so a suitable error msg can be delivered. > } > >+=head >+@DEPRECATED >+Sanitate 1 to * because we no longer have 1's from the koha.borrowers.flags. >+=cut >+ >+sub _changeAllPermissionsMarkerToAnyPermissionMarker { >+ #@DEPRECATED >+ my ($flagsrequired) = @_; >+ foreach my $module (%$flagsrequired) { >+ $flagsrequired->{$module} = '*' if $flagsrequired->{$module} && $flagsrequired->{$module} eq '1'; >+ } >+} >+ > sub getborrowernumber { >+ #@DEPRECATED See Bug 7174 > my ($userid) = @_; > my $userenv = C4::Context->userenv; > if ( defined($userenv) && ref($userenv) eq 'HASH' && $userenv->{number} ) { >diff --git a/C4/Auth_with_ldap.pm b/C4/Auth_with_ldap.pm >index 58484a2..7f000c5 100644 >--- a/C4/Auth_with_ldap.pm >+++ b/C4/Auth_with_ldap.pm >@@ -103,8 +103,17 @@ sub search_method { > return $search; > } > >+=head checkpw_ldap >+ >+@RETURNS Integer, -1 if login failed >+ , 0 if connection to the LDAP server couldn't be reliably established. >+ or List of (-1|1|0, $cardnumber, $local_userid); >+ where $cardnumber is koha.borrowers.cardnumber >+ $local_userid is the koha.borrowers.userid >+=cut >+ > sub checkpw_ldap { >- my ($dbh, $userid, $password) = @_; >+ my ($userid, $password) = @_; > my @hosts = split(',', $prefhost); > my $db = Net::LDAP->new(\@hosts); > unless ( $db ) { >diff --git a/C4/Context.pm b/C4/Context.pm >index 660a489..05f20b4 100644 >--- a/C4/Context.pm >+++ b/C4/Context.pm >@@ -1064,7 +1064,7 @@ sub userenv { > > C4::Context->set_userenv($usernum, $userid, $usercnum, > $userfirstname, $usersurname, >- $userbranch, $branchname, $userflags, >+ $userbranch, $branchname, $superlibrarian, > $emailaddress, $branchprinter, $persona); > > Establish a hash of user environment variables. >@@ -1076,7 +1076,7 @@ set_userenv is called in Auth.pm > #' > sub set_userenv { > shift @_; >- my ($usernum, $userid, $usercnum, $userfirstname, $usersurname, $userbranch, $branchname, $userflags, $emailaddress, $branchprinter, $persona, $shibboleth)= >+ my ($usernum, $userid, $usercnum, $userfirstname, $usersurname, $userbranch, $branchname, $superlibrarian, $emailaddress, $branchprinter, $persona, $shibboleth)= > map { Encode::is_utf8( $_ ) ? $_ : Encode::decode('UTF-8', $_) } # CGI::Session doesn't handle utf-8, so we decode it here > @_; > my $var=$context->{"activeuser"} || ''; >@@ -1089,7 +1089,7 @@ sub set_userenv { > #possibly a law problem > "branch" => $userbranch, > "branchname" => $branchname, >- "flags" => $userflags, >+ "flags" => $superlibrarian, #@DEPRECATED, use Koha::Borrower->isSuperlibrarian() instead of flags > "emailaddress" => $emailaddress, > "branchprinter" => $branchprinter, > "persona" => $persona, >@@ -1204,6 +1204,7 @@ sub tz { > > > =head2 IsSuperLibrarian >+@DEPRECATED, use Koha::Borrower->isSuperlibrarian() > > C4::Context->IsSuperLibrarian(); > >@@ -1220,7 +1221,7 @@ sub IsSuperLibrarian { > return 1; > } > >- return ($userenv->{flags}//0) % 2; >+ return ($userenv->{flags}//0) % 2; #If flags == 1, this is true. > } > > =head2 interface >diff --git a/Koha/Auth.pm b/Koha/Auth.pm >new file mode 100644 >index 0000000..83c99a5 >--- /dev/null >+++ b/Koha/Auth.pm >@@ -0,0 +1,225 @@ >+package Koha::Auth; >+ >+# Copyright 2015 Vaara-kirjastot >+# >+# 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 3 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, see <http://www.gnu.org/licenses>. >+ >+#Define common packages >+use Modern::Perl; >+use Scalar::Util qw(blessed); >+use Try::Tiny; >+ >+#Define Koha packages >+use Koha::Auth::RequestNormalizer; >+use Koha::Auth::Route::Password; >+use Koha::Auth::Route::Cookie; >+use Koha::Auth::Route::RESTV1; >+ >+#Define Exceptions >+use Koha::Exception::BadParameter; >+use Koha::Exception::Logout; >+use Koha::Exception::UnknownProgramState; >+ >+use C4::Branch; >+ >+#Define the headers, POST-parameters and cookies extracted from the various web-frameworks' >+# request-objects and passed to the authentication system as normalized values. >+our @authenticationHeaders = ('X-Koha-Signature', 'ETag', 'X-Koha-Username'); >+our @authenticationPOSTparams = ('password', 'userid', 'PT', 'branch', 'logout.x'); >+our @authenticationCookies = ('CGISESSID'); #Really we should have only one of these. >+ >+=head authenticate >+ >+@PARAM3 HASHRef of authentication directives. Supported values: >+ 'inOPAC' => 1, #Authentication context is in OPAC >+ 'inREST' => 'v1', #Authentication context is in REST API V1 >+ 'inSC' => 1, #Authentication context is in the staff client >+ 'authnotrequired' => 1, #Disregard all Koha::Exception::LoginFailed||NoPermission-exceptions, >+ #and authenticate as an anonymous user if normal authentication >+ #fails. >+@THROWS Koha::Exception::VersionMismatch >+ Koha::Exception::BadSystemPreference >+ Koha::Exception::ServiceTemporarilyUnavailable >+ Koha::Exception::LoginFailed >+ Koha::Exception::NoPermission >+ Koha::Exception::Logout, catch this and redirect the request to the logout page. >+=cut >+ >+sub authenticate { >+ my ($controller, $permissions, $authParams) = @_; >+ my $rae = _authenticate_validateAndNormalizeParameters(@_); #Get the normalized request authentication elements >+ >+ my $borrower; #Each authentication route returns a Koha::Borrower-object on success. We use this to generate the Context() >+ >+ ##Select the Authentication route. >+ ##Routes are introduced in priority order, and if one matches, the other routes are ignored. >+ try { >+ #0. Logout >+ if ($rae->{postParams}->{'logout.x'}) { >+ clearUserEnvironment($rae, $authParams); >+ Koha::Exception::Logout->throw(error => "User logged out. Please redirect me!"); >+ } >+ #1. Check for password authentication, including LDAP. >+ elsif ($rae->{postParams}->{userid} && $rae->{postParams}->{password}) { >+ $borrower = Koha::Auth::Route::Password::challenge($rae, $permissions, $authParams); >+ } >+ #2. Check for REST's signature-based authentication. >+ elsif ($rae->{headers}->{'X-Koha-Signature'}) { >+ $borrower = Koha::Auth::Route::RESTV1::check($rae, $permissions, $authParams); >+ } >+ #3. Check for the cookie. If cookies go stale, they block all subsequent authentication methods, so keep it down on this list. >+ elsif ($rae->{cookies}->{CGISESSID}) { >+ $borrower = Koha::Auth::Route::Cookie::challenge($rae, $permissions, $authParams); >+ } >+ else { #HTTP CAS ticket or shibboleth or Persona not implemented >+ #We don't know how to authenticate, or there is no authentication attempt. >+ Koha::Exception::LoginFailed->throw(error => "No authentication protocol."); >+ } >+ } catch { >+ if (blessed($_)) { >+ if ($_->isa('Koha::Exception::LoginFailed') || $_->isa('Koha::Exception::NoPermission')) { >+ if ($authParams->{authnotrequired}) { #We failed to login, but we can continue anonymously. >+ $borrower = Koha::Borrower->new(); >+ } >+ else { >+ $_->rethrow(); #Anonymous login not allowed this time >+ } >+ } >+ else { >+ $_->rethrow(); #Propagate other errors to the calling Controller to redirect as it wants. >+ } >+ } >+ else { >+ die $_; #Not a Koha::Exception-object >+ } >+ }; >+ >+ my $session = setUserEnvironment($controller, $rae, $borrower, $authParams); >+ my $cookie = Koha::Auth::RequestNormalizer::getSessionCookie($controller, $session); >+ >+ return ($borrower, $cookie); >+} >+ >+=head _authenticate_validateAndNormalizeParameters >+ >+@PARAM1 CGI- or Mojolicious::Controller-object, this is used to identify which web framework to use. >+@PARAM2 HASHRef or undef, Permissions HASH telling which Koha permissions the user must have, to access the resource. >+@PARAM3 HASHRef or undef, Special authentication parameters, see authenticate() >+@THROWS Koha::Exception::BadParameter, if validating parameters fails. >+=cut >+ >+sub _authenticate_validateAndNormalizeParameters { >+ my ($controller, $permissions, $authParams) = @_; >+ >+ #Validate $controller. >+ my $requestAuthElements; >+ if (blessed($controller) && $controller->isa('CGI')) { >+ $requestAuthElements = Koha::Auth::RequestNormalizer::normalizeCGI($controller, \@authenticationHeaders, \@authenticationPOSTparams, \@authenticationCookies); >+ } >+ elsif (blessed($controller) && $controller->isa('Mojolicious::Controller')) { >+ $requestAuthElements = Koha::Auth::RequestNormalizer::normalizeMojolicious($controller, \@authenticationHeaders, \@authenticationPOSTparams, \@authenticationCookies); >+ } >+ else { >+ Koha::Exception::BadParameter->throw(error => "Koha::Auth::authenticate():> The first parameter MUST be either a 'CGI'-object or a 'Mojolicious::Controller'-object"); >+ } >+ #Validate $permissions >+ unless (not($permissions) || (ref $permissions eq 'HASH')) { >+ Koha::Exception::BadParameter->throw(error => "Koha::Auth::authenticate():> The second parameter MUST be 'undef' or a HASHRef of Koha permissions. See C4::Auth::haspermission()."); >+ } >+ #Validate $authParams >+ unless (not($authParams) || (ref $authParams eq 'HASH')) { >+ Koha::Exception::BadParameter->throw(error => "Koha::Auth::authenticate():> The third parameter MUST be 'undef' or a HASHRef."); >+ } >+ >+ return $requestAuthElements; >+} >+ >+=head setUserEnvironment >+Set the C4::Context::user_env() and CGI::Session. >+ >+Any idea why there is both the CGI::Session and C4::Context::usernenv?? >+=cut >+ >+sub setUserEnvironment { >+ my ($controller, $rae, $borrower, $authParams) = @_; >+ >+ my $session = C4::Auth::get_session( $rae->{cookies}->{CGISESSID} || '' ); >+ C4::Context->_new_userenv( $session->id ); >+ >+ _determineUserBranch($rae, $borrower, $authParams, $session); >+ >+ #Then start setting remaining session parameters >+ $session->param( 'number', $borrower->borrowernumber ); >+ $session->param( 'id', $borrower->userid ); >+ $session->param( 'cardnumber', $borrower->cardnumber ); >+ $session->param( 'firstname', $borrower->firstname ); >+ $session->param( 'surname', $borrower->surname ); >+ $session->param( 'emailaddress', $borrower->email ); >+ $session->param( 'ip', $session->remote_addr() ); >+ $session->param( 'lasttime', time() ); >+ >+ #Finally configure the userenv. >+ C4::Context->set_userenv( >+ $session->param('number'), $session->param('id'), >+ $session->param('cardnumber'), $session->param('firstname'), >+ $session->param('surname'), $session->param('branch'), >+ $session->param('branchname'), undef, >+ $session->param('emailaddress'), $session->param('branchprinter'), >+ $session->param('persona'), $session->param('shibboleth') >+ ); >+ >+ return $session; >+} >+ >+sub _determineUserBranch { >+ my ($rae, $borrower, $authParams, $session) = @_; >+ >+ my ($branchcode, $branchname); >+ if ($rae->{postParams}->{branch}) { >+ #We are instructed to change the active branch >+ $branchcode = $rae->{postParams}->{branch}; >+ } >+ elsif ($session->param('branch')) { >+ ##Branch is already set so no reason to change it. >+ return; >+ } >+ elsif ($borrower->branchcode) { >+ #Default to the borrower's branch >+ $branchcode = $borrower->branchcode; >+ } >+ else { >+ #No borrower branch? This must be the superuser. >+ $branchcode = 'NO_LIBRARY_SET'; >+ $branchname = 'NO_LIBRARY_SET'; >+ } >+ $session->param( 'branch', $branchcode ); >+ $session->param( 'branchname', ($branchname || C4::Branch::GetBranchName($branchcode))); >+} >+ >+=head clearUserEnvironment >+ >+Removes all active authentications >+=cut >+ >+sub clearUserEnvironment { >+ my ($rae, $authParams) = @_; >+ >+ my $session = C4::Auth::get_session( $rae->{cookies}->{CGISESSID} ); >+ $session->delete(); >+ $session->flush; >+ C4::Context->_unset_userenv( $rae->{cookies}->{CGISESSID} ); >+} >+1; >\ No newline at end of file >diff --git a/Koha/Auth/Component.pm b/Koha/Auth/Component.pm >new file mode 100644 >index 0000000..a8ca56d >--- /dev/null >+++ b/Koha/Auth/Component.pm >@@ -0,0 +1,302 @@ >+package Koha::Auth::Component; >+ >+# Copyright 2015 Vaara-kirjastot >+# >+# 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 3 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, see <http://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+ >+use Digest::SHA qw(hmac_sha256_hex); >+ >+use Koha qw(version); >+use Koha::Borrowers; >+use Koha::AuthUtils; >+use Koha::Auth::Component::Password; >+use Koha::Auth::PermissionManager; >+ >+use Koha::Exception::VersionMismatch; >+use Koha::Exception::BadSystemPreference; >+use Koha::Exception::ServiceTemporarilyUnavailable; >+use Koha::Exception::LoginFailed; >+ >+=head1 NAME Koha::Auth::Component >+ >+=head2 SYNOPSIS >+ >+In this package we define the authentication steps we can use to define >+authentication path behaviour. >+ >+=head2 USAGE >+ >+ use Scalar::Util qw(blessed); >+ try { >+ ... >+ Koha::Auth::Component::checkVersion(); >+ Koha::Auth::Component::checkOPACMaintenance(); >+ ... >+ } catch { >+ if (blessed($_)) { >+ if ($_->isa('Koha::Exception::VersionMismatch')) { >+ ##handle exception >+ } >+ elsif ($_->isa('Koha::Exception::AnotherKindOfException')) { >+ ... >+ } >+ ... >+ else { >+ warn "Unknown exception class ".ref($_)."\n"; >+ die $_; #Unhandled exception case >+ } >+ } >+ else { >+ die $_; #Not a Koha::Exception-object >+ } >+ }; >+ >+=cut >+ >+=head checkVersion >+STATIC >+ >+ Koha::Auth::Component::checkVersion(); >+ >+Checks if the DB version is valid. >+ >+@THROWS Koha::Exception::VersionMismatch, if versions do not match >+@THROWS Koha::Exception::BadSystemPreference, if "Version"-syspref is not set. >+ This probably means that Koha has not been installed yet. >+=cut >+ >+sub checkVersion { >+ my $versionSyspref = C4::Context->preference('Version'); >+ unless ( $versionSyspref ) { >+ Koha::Exception::BadSystemPreference->throw(error => "No Koha 'Version'-system preference defined. Koha needs to be installed."); >+ } >+ >+ my $kohaversion = Koha::version(); >+ # remove the 3 last . to have a Perl number >+ $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/; >+ if ( $versionSyspref < $kohaversion ) { >+ Koha::Exception::VersionMismatch->throw(error => "Database update needed. Database is 'v$versionSyspref' and Koha is 'v$kohaversion'"); >+ } >+} >+ >+=head checkOPACMaintenance >+STATIC >+ >+ Koha::Auth::Component::checkOPACMaintenance(); >+ >+Checks if OPAC is under maintenance. >+ >+@THROWS Koha::Exception::ServiceTemporarilyUnavailable >+=cut >+ >+sub checkOPACMaintenance { >+ if ( C4::Context->preference('OpacMaintenance') ) { >+ Koha::Exception::ServiceTemporarilyUnavailable->throw(error => 'OPAC is under maintenance'); >+ } >+} >+ >+=head checkPassword >+STATIC >+ >+ Koha::Auth::Component::checkPassword(); >+ >+@RETURN Koha::Borrower-object if check succeedes, otherwise throws exceptions. >+@THROWS Koha::Exception::LoginFailed from Koha::AuthUtils password checks. >+=cut >+ >+sub checkPassword { >+ my ($userid, $password) = @_; >+ >+ my $borrower; >+ if (C4::Context->config('useldapserver')) { >+ $borrower = Koha::Auth::Component::Password::checkLDAPPassword($userid, $password); >+ return $borrower if $borrower; >+ } >+ if (C4::Context->preference('casAuthentication')) { >+ warn("Koha::Auth doesn't support CAS-authentication yet. Please refactor the CAS client implementation to work with Koha::Auth. It cant be too hard :)"); >+ } >+ if (C4::Context->config('useshibboleth')) { >+ warn("Koha::Auth doesn't support Shibboleth-authentication yet. Please refactor the Shibboleth client implementation to work with Koha::Auth. It cant be too hard :)"); >+ } >+ >+ return Koha::Auth::Component::Password::checkKohaPassword($userid, $password); >+} >+ >+=head checkPermissions >+STATIC >+ >+ Koha::Auth::Component::checkPermissions($borrower, $permissionsRequired); >+ >+@THROWS Koha::Exception::NoPermission with the missing permission if permissions >+ are inadequate >+=cut >+ >+sub checkPermissions { >+ my ($borrower, $permissionsRequired) = @_; >+ >+ my $permissionManager = Koha::Auth::PermissionManager->new(); >+ $permissionManager->hasPermissions($borrower, $permissionsRequired); >+} >+ >+=head checkCookie >+STATIC >+ >+ Koha::Auth::Component::checkCookie($cookieValue); >+ >+Checks if the given authentication cookie value matches a session, and checks if >+the session is still active. >+@PARAM1 String, hashed session key identifying a session in koha.sessions >+@RETURNS Koha::Borrower matching the verified and active session >+@THROWS Koha::Exception::LoginFailed, if no session is found, >+ if the session has expired, >+ if the session IP address changes, >+ if no borrower was found for the session >+=cut >+ >+sub checkCookie { >+ my ($cookie) = @_; >+ >+ my $session = C4::Auth::get_session($cookie); >+ Koha::Exception::LoginFailed->throw(error => "No session matching the given session identifier '$session'.") unless $session; >+ >+ # See if the given session is timed out >+ if ( ($session->param('lasttime') || 0) < (time()- C4::Auth::_timeout_syspref()) ) { >+ $session->delete(); >+ $session->flush; >+ C4::Context->_unset_userenv($cookie); >+ Koha::Exception::LoginFailed->throw(error => "Session expired, please login again."); >+ } >+ # Check if we still access using the same IP than when the session was initialized. >+ elsif ( C4::Context->preference('SessionRestrictionByIP') && $session->param('ip') ne $ENV{'REMOTE_ADDR'} ) { >+ $session->delete(); >+ $session->flush; >+ C4::Context->_unset_userenv($cookie); >+ Koha::Exception::LoginFailed->throw(error => "Session's client address changed, please login again."); >+ } >+ >+ #Get the Borrower-object >+ my $userid = $session->param('id'); >+ my $borrower = Koha::AuthUtils::checkKohaSuperuserFromUserid($userid); >+ $borrower = Koha::Borrowers->find({userid => $userid}) unless $borrower; >+ Koha::Exception::LoginFailed->throw(error => "Cookie authentication succeeded, but no borrower found with userid '$userid'.") >+ unless $borrower; >+ >+ $session->param( 'lasttime', time() ); >+ return $borrower; >+} >+ >+=head checkIndependentBranchesAutolocation >+ >+If sysprefs 'IndependentBranches' and 'Autolocation' are active, checks if the user >+is in the correct network region to login. >+@PARAM1 String, branchcode of the branch the current user is authenticating in to. >+@THROWS Koha::Exception::LoginFailed, if the user is in the wrong network segment. >+=cut >+ >+sub checkIndependentBranchesAutolocation { >+ my ($currentBranchcode) = @_; >+ >+ if ( $currentBranchcode && C4::Context->boolean_preference('IndependentBranches') && C4::Context->boolean_preference('Autolocation') ) { >+ my $ip = $ENV{'REMOTE_ADDR'}; >+ >+ my $branches = GetBranches(); >+ # we have to check they are coming from the right ip range >+ my $domain = $branches->{$currentBranchcode}->{'branchip'}; >+ if ( $ip !~ /^$domain/ ) { >+ Koha::Exception::LoginFailed->throw(error => "Branch '$currentBranchcode' is inaccessible from this network."); >+ } >+ } >+} >+ >+=head checkRESTV1 >+ >+ my $borrower = Koha::Auth::Component::checkRESTV1(); >+ >+For authentication to succeed, the client have to send 3 custom HTTP >+headers: >+ - X-Koha-Username: userid of borrower >+ - X-Koha-Timestamp: timestamp of the request >+ - X-Koha-Signature: signature of the request >+ >+The signature is a HMAC-SHA256 hash of several elements of the request, >+separated by spaces: >+ - HTTP method (uppercase) >+ - URL path and query string >+ - username >+ - timestamp of the request >+ >+The server then tries to rebuild the signature with each user's API key. >+If one matches the received X-Koha-Signature, then authentication is >+almost OK. >+ >+To avoid requests to be replayed, the last request's timestamp is stored >+in database and the authentication succeeds only if the stored timestamp >+is lesser than X-Koha-Timestamp. >+ >+@PARAM1 HASHRef of Header name => values >+@PARAM2 String, upper case request method name, eg. HTTP or HTTPS >+@PARAM3 String the request uri >+@RETURNS Koha::Borrower if authentication succeeded. >+@THROWS Koha::Exception::LoginFailed, if no user found with such name >+=cut >+ >+sub checkRESTV1 { >+ my ($headers, $method, $uri) = @_; >+ my $borrower = Koha::Borrowers->find({userid => $headers->{'X-Koha-Username'}}); >+ Koha::Exception::LoginFailed->throw(error => "No borrower from header X-Koha-Username, with value '".$headers->{'X-Koha-Username'}."'") >+ unless $borrower; >+ >+ my $req_username = $headers->{'X-Koha-Username'}; >+ my $req_timestamp = $headers->{'X-Koha-Timestamp'}; >+ my $req_signature = $headers->{'X-Koha-Signature'}; >+ >+ my @apikeys = Koha::ApiKeys->search({ >+ borrowernumber => $borrower->borrowernumber, >+ active => 1, >+ }); >+ Koha::Exception::LoginFailed->throw(error => "User has no API keys") unless @apikeys; >+ >+ my $message = "$method $uri $req_username $req_timestamp"; >+ my $signature = ''; >+ foreach my $apikey (@apikeys) { >+ $signature = Digest::SHA::hmac_sha256_hex($message, $apikey->api_key); >+ >+ last if $signature eq $req_signature; >+ } >+ >+ unless ($signature eq $req_signature) { >+ Koha::Exception::LoginFailed->throw(error => "API key authentication failed"); >+ } >+ >+ my $api_timestamp = Koha::ApiTimestamps->find($borrower->borrowernumber); >+ my $timestamp = $api_timestamp ? $api_timestamp->timestamp : 0; >+ unless ($timestamp < $req_timestamp) { >+ Koha::Exception::LoginFailed->throw(error => "Bad X-Koha-Timestamp, expected '$timestamp'"); >+ } >+ >+ unless ($api_timestamp) { >+ $api_timestamp = new Koha::ApiTimestamp; >+ $api_timestamp->borrowernumber($borrower->borrowernumber); >+ } >+ $api_timestamp->timestamp($req_timestamp); >+ $api_timestamp->store; >+ >+ return $borrower; >+} >+ >+1; >\ No newline at end of file >diff --git a/Koha/Auth/Component/Password.pm b/Koha/Auth/Component/Password.pm >new file mode 100644 >index 0000000..9ffbaa1 >--- /dev/null >+++ b/Koha/Auth/Component/Password.pm >@@ -0,0 +1,91 @@ >+package Koha::Auth::Component::Password; >+ >+# Copyright 2015 Vaara-kirjastot >+# >+# 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 3 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, see <http://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+ >+use Koha::AuthUtils; >+ >+our @usernameAliasColumns = ('userid', 'cardnumber'); #Possible columns to treat as the username when authenticating. Must be UNIQUE in DB. >+ >+=head NAME Koha::Auth::Component::Password >+ >+=head SYNOPSIS >+ >+This module implements the more specific behaviour of the password authentication component. >+ >+=head checkKohaPassword >+ >+ my $borrower = Koha::AuthUtils::checkKohaPassword($userid, $password); >+ >+Checks if the given username and password match anybody in the Koha DB >+@PARAM1 String, user identifier, either the koha.borrowers.userid, or koha.borrowers.cardnumber >+@PARAM2 String, clear text password from the authenticating user >+@RETURN Koha::Borrower, if login succeeded. >+ Sets Koha::Borrower->isSuperuser() if the user is a superuser. >+@THROWS Koha::Exception::LoginFailed, if no matching password was found for all username aliases in Koha. >+=cut >+ >+sub checkKohaPassword { >+ my ($userid, $password) = @_; >+ my $borrower; #Find the borrower to return >+ >+ $borrower = Koha::AuthUtils::checkKohaSuperuser($userid, $password); >+ return $borrower if $borrower; >+ >+ my $usernameFound = 0; #Report to the user if userid/barcode was found, even if the login failed. >+ #Check for each username alias if we can confirm a login with that. >+ for my $unameAlias (@usernameAliasColumns) { >+ my $borrower = Koha::Borrowers->find({$unameAlias => $userid}); >+ if ( $borrower ) { >+ $usernameFound = 1; >+ return $borrower if ( Koha::AuthUtils::checkHash( $password, $borrower->password ) ); >+ } >+ } >+ >+ Koha::Exception::LoginFailed->throw(error => "Password authentication failed for the given ".( ($usernameFound) ? "password" : "username and password")."."); >+} >+ >+=head checkLDAPPassword >+ >+Checks if the given username and password match anybody in the LDAP service >+@PARAM1 String, user identifier >+@PARAM2 String, clear text password from the authenticating user >+@RETURN Koha::Borrower, or >+ undef if we couldn't reliably contact the LDAP server so we should >+ fallback to local Koha Password authentication. >+@THROWS Koha::Exception::LoginFailed, if LDAP login failed >+=cut >+ >+sub checkLDAPPassword { >+ my ($userid, $password) = @_; >+ >+ #Lazy load dependencies because somebody might never need them. >+ require C4::Auth_with_ldap; >+ >+ my ($retval, $cardnumber, $local_userid) = C4::Auth_with_ldap::checkpw_ldap($userid, $password); # EXTERNAL AUTH >+ if ($retval == -1) { >+ Koha::Exception::LoginFailed->throw(error => "LDAP authentication failed for the given username and password"); >+ } >+ >+ if ($retval) { >+ my $borrower = Koha::Borrower->find({userid => $local_userid}); >+ return $borrower; >+ } >+ return undef; >+} >\ No newline at end of file >diff --git a/Koha/Auth/RequestNormalizer.pm b/Koha/Auth/RequestNormalizer.pm >new file mode 100644 >index 0000000..baa2bb6 >--- /dev/null >+++ b/Koha/Auth/RequestNormalizer.pm >@@ -0,0 +1,153 @@ >+package Koha::Auth::RequestNormalizer; >+ >+# Copyright 2015 Vaara-kirjastot >+# >+# 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 3 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, see <http://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+ >+use Scalar::Util qw(blessed); >+ >+ >+=head normalizeCGI >+ >+Takes a CGI-object and finds the authentication markers from it. >+@PARAM1 CGI-object. >+@PARAM2 ARRAYRef, authentication headers that should be extracted for authentication >+@PARAM3 ARRAYRef, authentication POST parameters that should be extracted for authentication >+@PARAM4 ARRAYRef, authentication cookies that should be extracted for authentication >+@RETURNS List of : HASHRef of headers required for authentication, or undef >+ HASHRef of POST parameters required for authentication, or undef >+ HASHRef of the authenticaton cookie name => value, or undef >+=cut >+ >+sub normalizeCGI { >+ my ($controller, $authenticationHeaders, $authenticationPOSTparams, $authenticationCookies) = @_; >+ >+ my ($headers, $postParams, $cookies) = ({}, {}, {}); >+ foreach my $authHeader (@$authenticationHeaders) { >+ if (my $val = $controller->http($authHeader)) { >+ $headers->{$authHeader} = $val; >+ } >+ } >+ foreach my $authParam (@$authenticationPOSTparams) { >+ if (my $val = $controller->param($authParam)) { >+ $postParams->{$authParam} = $val; >+ } >+ } >+ foreach my $authCookie (@$authenticationCookies) { >+ if (my $val = $controller->cookie($authCookie)) { >+ $cookies->{$authCookie} = $val; >+ } >+ } >+ my $method = $1 if ($ENV{SERVER_PROTOCOL} =~ /^(.+?)\//); >+ my $requestAuthElements = { #Collect the authentication elements here. >+ headers => $headers, >+ postParams => $postParams, >+ cookies => $cookies, >+ method => $method, >+ url => $ENV{REQUEST_URI}, >+ }; >+ return $requestAuthElements; >+} >+ >+=head normalizeMojolicious >+ >+Takes a Mojolicious::Controller-object and finds the authentication markers from it. >+@PARAM1 Mojolicious::Controller-object. >+@PARAM2-4 See normalizeCGI() >+@RETURNS HASHRef of the request's authentication elements marked for extraction, eg: >+ { >+ headers => { X-Koha-Signature => '32rFrFw3iojsev34AS', >+ X-Koha-Username => 'pavlov'}, >+ POSTparams => { password => '1234', >+ userid => 'pavlov'}, >+ cookies => { CGISESSID => '233FADFEV3as1asS' }, >+ method => 'https', >+ url => '/borrower/12/holds' >+ } >+=cut >+ >+sub normalizeMojolicious { >+ my ($controller, $authenticationHeaders, $authenticationPOSTparams, $authenticationCookies) = @_; >+ >+ my $request = $controller->req(); >+ my ($headers, $postParams, $cookies) = ({}, {}, {}); >+ if (blessed($controller) && $controller->isa('CGI')) { >+ my $headers = $request->headers(); >+ foreach my $authHeader (@$authenticationHeaders) { >+ if (my $val = $request->headers()->$authHeader()) { >+ $headers->{$authHeader} = $val; >+ } >+ } >+ foreach my $authParam (@$authenticationPOSTparams) { >+ if (my $val = $request->param($authParam)) { >+ $postParams->{$authParam} = $val; >+ } >+ } >+ foreach my $authCookie (@$authenticationCookies) { >+ if (my $val = $request->cookies($authCookie)) { >+ $cookies->{$authCookie} = $val; >+ } >+ } >+ } >+ >+ my $requestAuthElements = { #Collect the authentication elements here. >+ headers => $headers, >+ postParams => $postParams, >+ cookies => $cookies, >+ method => $controller->req->method, >+ url => '/' . $controller->req->url->path_query, >+ }; >+ return $requestAuthElements; >+} >+ >+=head getSessionCookie >+ >+@PARAM1 CGI- or Mojolicious::Controller-object, this is used to identify which web framework to use. >+@PARAM2 CGI::Session. >+@RETURNS a Mojolicious cookie or a CGI::Cookie. >+=cut >+ >+sub getSessionCookie { >+ my ($controller, $session) = @_; >+ >+ my $cookie = { >+ name => 'CGISESSID', >+ value => $session->id, >+ }; >+ my $cookieOk; >+ >+ if (blessed($controller) && $controller->isa('CGI')) { >+ $cookie->{HttpOnly} = 1; >+ $cookieOk = $controller->cookie( $cookie ); >+ } >+ elsif (blessed($controller) && $controller->isa('Mojolicious::Controller')) { >+ $controller->res->cookies($cookie); >+ foreach my $c (@{$controller->res->cookies}) { >+ if ($c->name eq 'CGISESSID') { >+ $cookieOk = $c; >+ last; >+ } >+ } >+ } >+ unless ($cookieOk) { >+ Koha::Exception::UnknownProgramState->throw(error => __PACKAGE__."::getSessionCookie():> Unable to get a proper cookie?"); >+ } >+ return $cookieOk; >+} >+ >+1; >\ No newline at end of file >diff --git a/Koha/Auth/Route.pm b/Koha/Auth/Route.pm >new file mode 100644 >index 0000000..297dca1 >--- /dev/null >+++ b/Koha/Auth/Route.pm >@@ -0,0 +1,69 @@ >+package Koha::Auth::Route; >+ >+# Copyright 2015 Vaara-kirjastot >+# >+# 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 3 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, see <http://www.gnu.org/licenses>. >+ >+=head >+ >+=NAME Koha::Auth::Route >+ >+=SYNOPSIS >+ >+This is an interface definition for Koha::Auth::Route::* -subclasses. >+This documentation explains how to subclass different routes. >+ >+=USAGE >+ >+ if ($userid && $password) { >+ $borrower = Koha::Auth::Route::<RouteName>::challenge($requestAuthElements, $permissionsRequired, $routeParams); >+ } >+ >+=head INPUT >+ >+Each Route gets three parameters: >+ $requestAuthElements, HASHRef of HASHRefs: >+ headers => HASHRef of HTTP Headers matching the @authenticationHeaders-package >+ variable in Koha::Auth, >+ Eg. { 'X-Koha-Signature' => "23in4ow2gas2opcnpa", ... } >+ postParams => HASHRef of HTTP POST parameters matching the >+ @authenticationPOSTparams-package variable in Koha::Auth, >+ Eg. { password => '1234', 'userid' => 'admin'} >+ cookies => HASHRef of HTTP Cookies matching the >+ @authenticationPOSTparams-package variable in Koha::Auth, >+ EG. { CGISESSID => '9821rj1kn3tr9ff2of2ln1' } >+ $permissionsRequired: >+ HASHRef of Koha permissions. >+ See C4::Auth::haspermission() for example. >+ $routeParams: HASHRef of special Route-related data >+ {inOPAC => 1, authnotrequired => 0, ...} >+ >+=head OUTPUT >+ >+Each route must return a Koha::Borrower-object representing the authenticated user. >+Even if the login succeeds with a superuser or similar virtual user, like >+anonymous login, a mock Borrower-object must be returned. >+If the login fails, each route must throw Koha::Exceptions to notify the cause >+of the failure. >+ >+=head ROUTE STRUCTURE >+ >+Each route consists of Koha::Auth::Component-subroutine calls to test for various >+authentication challenges. >+ >+=cut >+ >+1; >\ No newline at end of file >diff --git a/Koha/Auth/Route/Cookie.pm b/Koha/Auth/Route/Cookie.pm >new file mode 100644 >index 0000000..3ced599 >--- /dev/null >+++ b/Koha/Auth/Route/Cookie.pm >@@ -0,0 +1,41 @@ >+package Koha::Auth::Route::Cookie; >+ >+# Copyright 2015 Vaara-kirjastot >+# >+# 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 3 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, see <http://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+ >+use Koha::Auth::Component; >+ >+use base qw(Koha::Auth::Route); >+ >+=head challenge >+See Koha::Auth::Route, for usage documentation. >+@THROWS Koha::Exceptions from authentication components. >+=cut >+ >+sub challenge { >+ my ($rae, $permissionsRequired, $routeParams) = @_; >+ >+ Koha::Auth::Component::checkOPACMaintenance() if $routeParams->{inOPAC}; >+ Koha::Auth::Component::checkVersion(); >+ my $borrower = Koha::Auth::Component::checkCookie($rae->{cookies}->{CGISESSID}); >+ Koha::Auth::Component::checkPermissions($borrower, $permissionsRequired) if $permissionsRequired; >+ return $borrower; >+} >+ >+1; >\ No newline at end of file >diff --git a/Koha/Auth/Route/Password.pm b/Koha/Auth/Route/Password.pm >new file mode 100644 >index 0000000..44cc1fa >--- /dev/null >+++ b/Koha/Auth/Route/Password.pm >@@ -0,0 +1,42 @@ >+package Koha::Auth::Route::Password; >+ >+# Copyright 2015 Vaara-kirjastot >+# >+# 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 3 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, see <http://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+ >+use Koha::Auth::Component; >+ >+use base qw(Koha::Auth::Route); >+ >+=head challenge >+See Koha::Auth::Route, for usage documentation. >+@THROWS Koha::Exceptions from authentication components. >+=cut >+ >+sub challenge { >+ my ($rae, $permissionsRequired, $routeParams) = @_; >+ >+ Koha::Auth::Component::checkOPACMaintenance() if $routeParams->{inOPAC}; >+ Koha::Auth::Component::checkVersion(); >+ Koha::Auth::Component::checkIndependentBranchesAutolocation($routeParams->{branch}); >+ my $borrower = Koha::Auth::Component::checkPassword($rae->{postParams}->{userid}, $rae->{postParams}->{password}); >+ Koha::Auth::Component::checkPermissions($borrower, $permissionsRequired) if $permissionsRequired; >+ return $borrower; >+} >+ >+1; >\ No newline at end of file >diff --git a/Koha/Auth/Route/RESTV1.pm b/Koha/Auth/Route/RESTV1.pm >new file mode 100644 >index 0000000..fd945e1 >--- /dev/null >+++ b/Koha/Auth/Route/RESTV1.pm >@@ -0,0 +1,41 @@ >+package Koha::Auth::Route::RESTV1; >+ >+# Copyright 2015 Vaara-kirjastot >+# >+# 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 3 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, see <http://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+ >+use Koha::Auth::Component; >+ >+use base qw(Koha::Auth::Route); >+ >+=head challenge >+See Koha::Auth::Route, for usage documentation. >+@THROWS Koha::Exceptions from authentication components. >+=cut >+ >+sub challenge { >+ my ($rae, $permissionsRequired, $routeParams) = @_; >+ >+ #Koha::Auth::Component::checkRESTMaintenance() if $routeParams->{inREST}; #NOT IMPLEMENTED YET >+ Koha::Auth::Component::checkVersion(); >+ my $borrower = Koha::Auth::Component::checkRESTV1($rae->{headers}); >+ Koha::Auth::Component::checkPermissions($borrower, $permissionsRequired) if $permissionsRequired; >+ return $borrower; >+} >+ >+1; >\ No newline at end of file >diff --git a/Koha/AuthUtils.pm b/Koha/AuthUtils.pm >index 7d01e28..8845664 100644 >--- a/Koha/AuthUtils.pm >+++ b/Koha/AuthUtils.pm >@@ -24,6 +24,8 @@ use Fcntl qw/O_RDONLY/; # O_RDONLY is used in generate_salt > > use Koha::Borrower; > >+use Koha::Exception::LoginFailed; >+ > use base 'Exporter'; > > our $VERSION = '1.01'; >@@ -136,6 +138,66 @@ sub generate_salt { > return $string; > } > >+=head checkHash >+ >+ my $passwordOk = Koha::AuthUtils::checkHash($password1, $password2) >+ >+Checks if a clear-text String/password matches the given hash when >+MD5 or Bcrypt hashing algorith is applied to it. >+ >+Bcrypt is applied if @PARAM2 starts with '$2' >+MD5 otherwise >+ >+@PARAM1 String, clear text passsword or any other String >+@PARAM2 String, hashed text password or any other String. >+@RETURN Boolean, 1 if given parameters match >+ , 0 if not >+=cut >+ >+sub checkHash { >+ my ( $password, $stored_hash ) = @_; >+ >+ $password = Encode::encode( 'UTF-8', $password ) >+ if Encode::is_utf8($password); >+ >+ return if $stored_hash eq '!'; >+ >+ my $hash; >+ if ( substr( $stored_hash, 0, 2 ) eq '$2' ) { >+ $hash = hash_password( $password, $stored_hash ); >+ } else { >+ #@DEPRECATED Digest::MD5, don't use it or you will get hurt. >+ require Digest::MD5; >+ $hash = Digest::MD5::md5_base64($password); >+ } >+ return $hash eq $stored_hash; >+} >+ >+=head checkKohaSuperuser >+ >+ my $borrower = Koha::AuthUtils::checkKohaSuperuser($userid, $password); >+ >+Check if the userid and password match the ones in the $KOHA_CONF >+@PARAM1 String, user identifier, either the koha.borrowers.userid, or koha.borrowers.cardnumber >+@PARAM2 String, clear text password from the authenticating user >+@RETURNS Koha::Borrower branded as superuser with ->isSuperuser() >+ or undef if user logging in is not a superuser. >+@THROWS Koha::Exception::LoginFailed if user identifier matches, but password doesn't >+=cut >+ >+sub checkKohaSuperuser { >+ my ($userid, $password) = @_; >+ >+ if ( $userid && $userid eq C4::Context->config('user') ) { >+ if ( $password && $password eq C4::Context->config('pass') ) { >+ return _createTemporarySuperuser(); >+ } >+ else { >+ Koha::Exception::LoginFailed->throw(error => "Password authentication failed"); >+ } >+ } >+} >+ > =head checkKohaSuperuserFromUserid > See checkKohaSuperuser(), with only the "user identifier"-@PARAM. > @THROWS nothing. >@@ -153,13 +215,14 @@ sub checkKohaSuperuserFromUserid { > > Create a temporary superuser which should be instantiated only to the environment > and then discarded. So do not ->store() it! >-@RETURN Koha::Borrower >+@RETURN Koha::Borrower, stamped as superuser. > =cut > > sub _createTemporarySuperuser { > my $borrower = Koha::Borrower->new(); > > my $superuserName = C4::Context->config('user'); >+ $borrower->isSuperuser(1); > $borrower->set({borrowernumber => 0, > userid => $superuserName, > cardnumber => $superuserName, >diff --git a/Koha/Borrower.pm b/Koha/Borrower.pm >index 93426bf..4ceae11 100644 >--- a/Koha/Borrower.pm >+++ b/Koha/Borrower.pm >@@ -43,6 +43,32 @@ sub type { > return 'Borrower'; > } > >+=head isSuperuser >+ >+ $borrower->isSuperuser(1); #Set this borrower to be a superuser >+ if ($borrower->isSuperuser()) { >+ #All your base are belong to us >+ } >+ >+Should be used from the authentication modules to mark this $borrower-object to >+have unlimited access to all Koha-features. >+This $borrower-object is the Koha DB user. >+@PARAM1 Integer, 1 means this borrower is the super/DB user. >+ "0" disables the previously set superuserness. >+=cut >+ >+sub isSuperuser { >+ my ($self, $Iam) = @_; >+ >+ if (defined $Iam && $Iam == 1) { >+ $self->{superuser} = 1; >+ } >+ elsif (defined $Iam && $Iam eq "0") { #Dealing with zero is special in Perl >+ $self->{superuser} = undef; >+ } >+ return (exists($self->{superuser}) && $self->{superuser}) ? 1 : undef; >+} >+ > =head1 AUTHOR > > Kyle M Hall <kyle@bywatersolutions.com> >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/auth.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/auth.tt >index 9ae147e..d3dc043 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/auth.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/auth.tt >@@ -43,7 +43,9 @@ > <form action="[% url %]" method="post" name="loginform" id="loginform"> > <input type="hidden" name="koha_login_context" value="intranet" /> > [% FOREACH INPUT IN INPUTS %] >- <input type="hidden" name="[% INPUT.name |html %]" value="[% INPUT.value |html %]" /> >+ [% UNLESS INPUT.name == 'logout.x' #No reason to send the logout-signal again %] >+ <input type="hidden" name="[% INPUT.name |html %]" value="[% INPUT.value |html %]" /> >+ [% END %] > [% END %] > <p><label for="userid">Username:</label> > <input type="text" name="userid" id="userid" class="input focus" value="[% userid %]" size="20" tabindex="1" /> >diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-auth.tt b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-auth.tt >index c474791..81f8c10 100644 >--- a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-auth.tt >+++ b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-auth.tt >@@ -142,7 +142,9 @@ > <input type="hidden" name="koha_login_context" value="opac" /> > <fieldset class="brief"> > [% FOREACH INPUT IN INPUTS %] >- <input type="hidden" name="[% INPUT.name |html %]" value="[% INPUT.value |html %]" /> >+ [% UNLESS INPUT.name == 'logout.x' #No reason to send the logout-signal again %] >+ <input type="hidden" name="[% INPUT.name |html %]" value="[% INPUT.value |html %]" /> >+ [% END %] > [% END %] > <label for="userid">Login</label> > <input type="text" size="25" id="userid" name="userid" /> >diff --git a/opac/opac-user.pl b/opac/opac-user.pl >index d4fbbe3..c59931d 100755 >--- a/opac/opac-user.pl >+++ b/opac/opac-user.pl >@@ -349,7 +349,7 @@ foreach my $res (@reserves) { > $template->param( WAITING => \@waiting ); > > # current alert subscriptions >-my $alerts = getalert($borrowernumber); >+my $alerts = getalert($borrowernumber) if $borrowernumber; #Superuser has no borrowernumber > foreach ( @$alerts ) { > $_->{ $_->{type} } = 1; > $_->{relatedto} = findrelatedto( $_->{type}, $_->{externalid} ); >diff --git a/t/db_dependent/Auth_with_ldap.t b/t/db_dependent/Auth_with_ldap.t >index 4982248..5c648f5 100755 >--- a/t/db_dependent/Auth_with_ldap.t >+++ b/t/db_dependent/Auth_with_ldap.t >@@ -74,7 +74,7 @@ subtest "checkpw_ldap tests" => sub { > > ## Connection fail tests > $desired_connection_result = 'error'; >- warning_is { $ret = C4::Auth_with_ldap::checkpw_ldap( $dbh, 'hola', password => 'hey' ) } >+ warning_is { $ret = C4::Auth_with_ldap::checkpw_ldap( 'hola', password => 'hey' ) } > "LDAP connexion failed", > "checkpw_ldap prints correct warning if LDAP conexion fails"; > is( $ret, 0, "checkpw_ldap returns 0 if LDAP conexion fails"); >@@ -96,7 +96,7 @@ subtest "checkpw_ldap tests" => sub { > > > warning_like { $ret = C4::Auth_with_ldap::checkpw_ldap( >- $dbh, 'hola', password => 'hey' ) } >+ 'hola', password => 'hey' ) } > qr/Anonymous LDAP bind failed: LDAP error #1: error_name/, > "checkpw_ldap prints correct warning if LDAP anonymous bind fails"; > is( $ret, 0, "checkpw_ldap returns 0 if LDAP anonymous bind fails"); >@@ -108,14 +108,14 @@ subtest "checkpw_ldap tests" => sub { > $desired_count_result = 0; # user auth problem > $non_anonymous_bind_result = 'success'; > reload_ldap_module(); >- is ( C4::Auth_with_ldap::checkpw_ldap( $dbh, 'hola', password => 'hey' ), >+ is ( C4::Auth_with_ldap::checkpw_ldap( 'hola', password => 'hey' ), > 0, "checkpw_ldap returns 0 if user lookup returns 0"); > > $non_anonymous_bind_result = 'error'; > reload_ldap_module(); > > warning_like { $ret = C4::Auth_with_ldap::checkpw_ldap( >- $dbh, 'hola', password => 'hey' ) } >+ 'hola', password => 'hey' ) } > qr/LDAP bind failed as kohauser hola: LDAP error #1: error_name/, > "checkpw_ldap prints correct warning if LDAP bind fails"; > is ( $ret, -1, "checkpw_ldap returns -1 LDAP bind fails for user (Bug 8148)"); >@@ -130,7 +130,7 @@ subtest "checkpw_ldap tests" => sub { > reload_ldap_module(); > > warning_like { $ret = C4::Auth_with_ldap::checkpw_ldap( >- $dbh, 'hola', password => 'hey' ) } >+ 'hola', password => 'hey' ) } > qr/LDAP bind failed as kohauser hola: LDAP error #1: error_name/, > "checkpw_ldap prints correct warning if LDAP bind fails"; > is ( $ret, 0, "checkpw_ldap returns 0 LDAP bind fails for user (Bug 12831)"); >@@ -150,7 +150,7 @@ subtest "checkpw_ldap tests" => sub { > reload_ldap_module(); > > warning_like { $ret = C4::Auth_with_ldap::checkpw_ldap( >- $dbh, 'hola', password => 'hey' ) } >+ 'hola', password => 'hey' ) } > qr/LDAP bind failed as ldapuser cn=Manager,dc=metavore,dc=com: LDAP error #1: error_name/, > "checkpw_ldap prints correct warning if LDAP bind fails"; > is ( $ret, 0, "checkpw_ldap returns 0 if bind fails"); >@@ -162,7 +162,7 @@ subtest "checkpw_ldap tests" => sub { > reload_ldap_module(); > > warning_like { $ret = C4::Auth_with_ldap::checkpw_ldap( >- $dbh, 'hola', password => 'hey' ) } >+ 'hola', password => 'hey' ) } > qr/LDAP Auth rejected : invalid password for user 'hola'. LDAP error #1: error_name/, > "checkpw_ldap prints correct warning if LDAP bind fails"; > is ( $ret, -1, "checkpw_ldap returns -1 if bind fails (Bug 8148)"); >@@ -175,7 +175,7 @@ subtest "checkpw_ldap tests" => sub { > reload_ldap_module(); > > warning_like { $ret = C4::Auth_with_ldap::checkpw_ldap( >- $dbh, 'hola', password => 'hey' ) } >+ 'hola', password => 'hey' ) } > qr/LDAP bind failed as ldapuser cn=Manager,dc=metavore,dc=com: LDAP error #1: error_name/, > "checkpw_ldap prints correct warning if LDAP bind fails"; > is ( $ret, 0, "checkpw_ldap returns 0 if bind fails"); >@@ -187,7 +187,7 @@ subtest "checkpw_ldap tests" => sub { > reload_ldap_module(); > > warning_like { $ret = C4::Auth_with_ldap::checkpw_ldap( >- $dbh, 'hola', password => 'hey' ) } >+ 'hola', password => 'hey' ) } > qr/LDAP Auth rejected : invalid password for user 'hola'. LDAP error #1: error_name/, > "checkpw_ldap prints correct warning if LDAP bind fails"; > is ( $ret, -1, "checkpw_ldap returns -1 if bind fails (Bug 8148)"); >diff --git a/t/db_dependent/Koha/Borrower.t b/t/db_dependent/Koha/Borrower.t >new file mode 100644 >index 0000000..75f985a >--- /dev/null >+++ b/t/db_dependent/Koha/Borrower.t >@@ -0,0 +1,55 @@ >+#!/usr/bin/perl >+ >+# Copyright 2015 Open Source Freedom Fighters >+# >+# 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 3 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, see <http://www.gnu.org/licenses>. >+ >+use Test::More; #Please don't set the test count here. It is nothing but trouble when rebasing against master and is of dubious help. >+ >+use Koha::Borrower; >+ >+ >+ >+testIsSuperuser(); >+ >+ >+ >+ >+ >+################################################################################ >+#### Define test subroutines here ############################################## >+################################################################################ >+ >+=head testIsSuperuser >+@UNIT_TEST >+Tests Koha::Borrower->isSuperuser() >+=cut >+ >+sub testIsSuperuser { >+ my $borrower = Koha::Borrower->new(); >+ ok((not(defined($borrower->isSuperuser()))), "isSuperuser(): By default user is not defined as superuser."); >+ ok(($borrower->isSuperuser(1) == 1), "isSuperuser(): Setting user as superuser returns 1."); >+ ok(($borrower->isSuperuser() == 1), "isSuperuser(): Getting superuser status from a superuser returns 1."); >+ ok((not(defined($borrower->isSuperuser(0)))), "isSuperuser(): Removing superuser status from a superuser OK and returns undef"); >+ ok((not(defined($borrower->isSuperuser()))), "isSuperuser(): Ex-superuser superuser status is undef"); >+} >+ >+ >+ >+ >+####################### >+done_testing(); #YAY!! >+####################### >\ No newline at end of file >-- >1.9.1
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 7174
:
40672
|
40678
|
40680
|
40723
|
40739
|
41117
|
41124
|
41159
|
41256
|
41311
|
41312
|
41413
|
41536
|
41537
|
63255