@@ -, +, @@ chew bubblegum and kick ass... --- C4/Auth.pm | 121 ++++++++++++++++++++++++++++++++++++----------------- Koha/AuthUtils.pm | 80 +++++++++++++++++++++++++++++++++++ 2 files changed, 163 insertions(+), 38 deletions(-) --- a/C4/Auth.pm +++ a/C4/Auth.pm @@ -19,11 +19,12 @@ 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; use Scalar::Util qw(blessed); +use Try::Tiny; require Exporter; use C4::Context; @@ -34,6 +35,8 @@ use C4::Search::History; use Koha; use Koha::Borrowers; 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); @@ -639,50 +642,63 @@ has authenticated. =cut +=head _version_check +#@DEPRECATED See Bug 7174 +use Koha::Auth::Component::* instead +=cut + sub _version_check { 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 $_; #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 { @@ -702,7 +718,31 @@ sub _timeout_syspref { return $timeout; } +=head checkauth +@DEPRECATED See Bug 7174 + +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() + #Koha::Auth::checkAuth(@params); + return checkauth_legacy(@params); +} + +=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 { my $query = shift; $debug and warn "Checking Auth"; @@ -1682,6 +1722,7 @@ sub get_session { return $session; } +#@DEPRECATED See Bug 7174 sub checkpw { my ( $dbh, $userid, $password, $query, $type ) = @_; $type = 'opac' unless $type; @@ -1726,6 +1767,7 @@ sub checkpw { return checkpw_internal(@_) } +#@DEPRECATED See Bug 7174 sub checkpw_internal { my ( $dbh, $userid, $password ) = @_; @@ -1778,6 +1820,7 @@ sub checkpw_internal { return 1, $cardnumber, $userid; } } + if ( $userid && $userid eq 'demo' && "$password" eq 'demo' && C4::Context->config('demo') ) @@ -1790,6 +1833,7 @@ sub checkpw_internal { return 0; } +#@DEPRECATED See Bug 7174 sub checkpw_hash { my ( $password, $stored_hash ) = @_; @@ -1800,6 +1844,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; --- a/Koha/AuthUtils.pm +++ a/Koha/AuthUtils.pm @@ -22,6 +22,10 @@ use Crypt::Eksblowfish::Bcrypt qw(bcrypt en_base64); use Encode qw( encode is_utf8 ); use Fcntl qw/O_RDONLY/; # O_RDONLY is used in generate_salt +use Koha::Borrowers; + +use Koha::Exception::LoginFailed; + use base 'Exporter'; our $VERSION = '1.01'; @@ -133,6 +137,82 @@ sub generate_salt { close SOURCE; return $string; } + +=head checkKohaPassword + + my $isSuperuser = Koha::AuthUtils::checkKohaPassword($userid, $password); + +Checks of 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 String 'superuser', if user is the Koha DB user (superuser account) + undef, if the user is but a man. +@THROWS Koha::Exception::LoginFailed, if no matching password was found for all username aliases in Koha. +=cut + +sub checkKohaPassword { + my ($userid, $password) = @_; + + if ( $userid && $userid eq C4::Context->config('user') ) { + if ( $password && $password eq C4::Context->config('pass') ) { + # Koha superuser account + # C4::Context->set_userenv(0,0,C4::Context->config('user'),C4::Context->config('user'),C4::Context->config('user'),"",1); + return 'superuser'; + } + else { + Koha::Exception::LoginFailed->throw(error => "Password authentication failed"); + } + } + + 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. + my @usernameAliasColumns = ('userid', 'cardnumber'); + for my $unameAlias (@usernameAliasColumns) { + my $borrower = Koha::Borrowers->find({$unameAlias => $userid}); + if ( $borrower ) { + $usernameFound = 1; + return if ( checkHash( $password, $borrower->password ) ); + } + } + + Koha::Exception::LoginFailed->throw(error => "Password authentication failed for the given ".( ($usernameFound) ? "password" : "username and password")."."); +} + +=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; +} + 1; __END__ --