Lines 35-40
use Koha;
Link Here
|
35 |
use Koha::Logger; |
35 |
use Koha::Logger; |
36 |
use Koha::Caches; |
36 |
use Koha::Caches; |
37 |
use Koha::AuthUtils qw( get_script_name hash_password ); |
37 |
use Koha::AuthUtils qw( get_script_name hash_password ); |
|
|
38 |
use Koha::Auth::TwoFactorAuth; |
38 |
use Koha::Checkouts; |
39 |
use Koha::Checkouts; |
39 |
use Koha::DateUtils qw( dt_from_string ); |
40 |
use Koha::DateUtils qw( dt_from_string ); |
40 |
use Koha::Library::Groups; |
41 |
use Koha::Library::Groups; |
Lines 856-861
sub checkauth {
Link Here
|
856 |
my $q_userid = $query->param('userid') // ''; |
857 |
my $q_userid = $query->param('userid') // ''; |
857 |
|
858 |
|
858 |
my $session; |
859 |
my $session; |
|
|
860 |
my $invalid_otp_token; |
861 |
my $require_2FA = ( C4::Context->preference('TwoFactorAuthentication') && $type ne "OPAC" ) ? 1 : 0; |
862 |
my $auth_challenge_complete; |
859 |
|
863 |
|
860 |
# Basic authentication is incompatible with the use of Shibboleth, |
864 |
# Basic authentication is incompatible with the use of Shibboleth, |
861 |
# as Shibboleth may return REMOTE_USER as a Shibboleth attribute, |
865 |
# as Shibboleth may return REMOTE_USER as a Shibboleth attribute, |
Lines 888-900
sub checkauth {
Link Here
|
888 |
{ remote_addr => $ENV{REMOTE_ADDR}, skip_version_check => 1 } |
892 |
{ remote_addr => $ENV{REMOTE_ADDR}, skip_version_check => 1 } |
889 |
); |
893 |
); |
890 |
|
894 |
|
|
|
895 |
if ( $return eq 'ok' || $return eq 'additional-auth-needed' ) { |
896 |
$userid = $session->param('id'); |
897 |
} |
898 |
|
899 |
$additional_auth_needed = ( $return eq 'additional-auth-needed' ) ? 1 : 0; |
900 |
|
901 |
# We are at the second screen if the waiting-for-2FA is set in session |
902 |
# and otp_token param has been passed |
903 |
if ( $require_2FA |
904 |
&& $additional_auth_needed |
905 |
&& ( my $otp_token = $query->param('otp_token') ) ) |
906 |
{ |
907 |
my $patron = Koha::Patrons->find( { userid => $userid } ); |
908 |
my $auth = Koha::Auth::TwoFactorAuth::get_auth( { patron => $patron } ); |
909 |
my $verified = $auth->verify($otp_token); |
910 |
$auth->clear; |
911 |
if ( $verified ) { |
912 |
# The token is correct, the user is fully logged in! |
913 |
$additional_auth_needed = 0; |
914 |
$session->param( 'waiting-for-2FA', 0 ); |
915 |
$return = "ok"; |
916 |
$auth_challenge_complete = 1; |
917 |
|
918 |
# This is an ugly trick to pass the test |
919 |
# $query->param('koha_login_context') && ( $q_userid ne $userid ) |
920 |
# few lines later |
921 |
$q_userid = $userid; |
922 |
} |
923 |
else { |
924 |
$invalid_otp_token = 1; |
925 |
} |
926 |
} |
927 |
|
891 |
if ( $return eq 'ok' ) { |
928 |
if ( $return eq 'ok' ) { |
892 |
Koha::Logger->get->debug(sprintf "AUTH_SESSION: (%s)\t%s %s - %s", map { $session->param($_) || q{} } qw(cardnumber firstname surname branch)); |
929 |
Koha::Logger->get->debug(sprintf "AUTH_SESSION: (%s)\t%s %s - %s", map { $session->param($_) || q{} } qw(cardnumber firstname surname branch)); |
893 |
|
930 |
|
894 |
my $s_userid = $session->param('id'); |
931 |
if ( ( $query->param('koha_login_context') && ( $q_userid ne $userid ) ) |
895 |
$userid = $s_userid; |
|
|
896 |
|
897 |
if ( ( $query->param('koha_login_context') && ( $q_userid ne $s_userid ) ) |
898 |
|| ( $cas && $query->param('ticket') && !C4::Context->userenv->{'id'} ) |
932 |
|| ( $cas && $query->param('ticket') && !C4::Context->userenv->{'id'} ) |
899 |
|| ( $shib && $shib_login && !$logout && !C4::Context->userenv->{'id'} ) |
933 |
|| ( $shib && $shib_login && !$logout && !C4::Context->userenv->{'id'} ) |
900 |
) { |
934 |
) { |
Lines 904-931
sub checkauth {
Link Here
|
904 |
$anon_search_history = $session->param('search_history'); |
938 |
$anon_search_history = $session->param('search_history'); |
905 |
$session->delete(); |
939 |
$session->delete(); |
906 |
$session->flush; |
940 |
$session->flush; |
907 |
C4::Context::_unset_userenv($sessionID); |
941 |
C4::Context->_unset_userenv($sessionID); |
908 |
$sessionID = undef; |
|
|
909 |
} |
910 |
elsif ($logout) { |
911 |
|
912 |
# voluntary logout the user |
913 |
# check wether the user was using their shibboleth session or a local one |
914 |
my $shibSuccess = C4::Context->userenv->{'shibboleth'}; |
915 |
$session->delete(); |
916 |
$session->flush; |
917 |
C4::Context::_unset_userenv($sessionID); |
918 |
$sessionID = undef; |
942 |
$sessionID = undef; |
919 |
|
943 |
} elsif (!$logout) { |
920 |
if ($cas and $caslogout) { |
|
|
921 |
logout_cas($query, $type); |
922 |
} |
923 |
|
924 |
# If we are in a shibboleth session (shibboleth is enabled, a shibboleth match attribute is set and matches koha matchpoint) |
925 |
if ( $shib and $shib_login and $shibSuccess) { |
926 |
logout_shib($query); |
927 |
} |
928 |
} else { |
929 |
|
944 |
|
930 |
$cookie = $query->cookie( |
945 |
$cookie = $query->cookie( |
931 |
-name => 'CGISESSID', |
946 |
-name => 'CGISESSID', |
Lines 952-964
sub checkauth {
Link Here
|
952 |
} |
967 |
} |
953 |
} |
968 |
} |
954 |
|
969 |
|
955 |
unless ( $loggedin ) { |
970 |
if ( ( !$loggedin && !$additional_auth_needed ) || $logout ) { |
|
|
971 |
$sessionID = undef; |
956 |
$userid = undef; |
972 |
$userid = undef; |
957 |
} |
973 |
} |
958 |
|
974 |
|
|
|
975 |
if ($logout) { |
976 |
|
977 |
# voluntary logout the user |
978 |
# check wether the user was using their shibboleth session or a local one |
979 |
my $shibSuccess = C4::Context->userenv->{'shibboleth'}; |
980 |
if ( $session ) { |
981 |
$session->delete(); |
982 |
$session->flush; |
983 |
} |
984 |
C4::Context->_unset_userenv($sessionID); |
985 |
|
986 |
if ($cas and $caslogout) { |
987 |
logout_cas($query, $type); |
988 |
} |
989 |
|
990 |
# If we are in a shibboleth session (shibboleth is enabled, a shibboleth match attribute is set and matches koha matchpoint) |
991 |
if ( $shib and $shib_login and $shibSuccess) { |
992 |
logout_shib($query); |
993 |
} |
994 |
|
995 |
$session = undef; |
996 |
$additional_auth_needed = 0; |
997 |
} |
998 |
|
959 |
unless ( $userid ) { |
999 |
unless ( $userid ) { |
960 |
#we initiate a session prior to checking for a username to allow for anonymous sessions... |
1000 |
#we initiate a session prior to checking for a username to allow for anonymous sessions... |
961 |
$session ||= get_session("") or die "Auth ERROR: Cannot get_session()"; |
1001 |
$session = get_session("") or die "Auth ERROR: Cannot get_session()"; |
962 |
|
1002 |
|
963 |
# Save anonymous search history in new session so it can be retrieved |
1003 |
# Save anonymous search history in new session so it can be retrieved |
964 |
# by get_template_and_user to store it in user's search history after |
1004 |
# by get_template_and_user to store it in user's search history after |
Lines 1253-1261
sub checkauth {
Link Here
|
1253 |
$session->flush; |
1293 |
$session->flush; |
1254 |
} # END unless ($userid) |
1294 |
} # END unless ($userid) |
1255 |
|
1295 |
|
|
|
1296 |
if ( $require_2FA && ( $loggedin && !$auth_challenge_complete)) { |
1297 |
my $patron = Koha::Patrons->find({userid => $userid}); |
1298 |
if ( $patron->auth_method eq 'two-factor' ) { |
1299 |
# Ask for the OTP token |
1300 |
$additional_auth_needed = 1; |
1301 |
$session->param('waiting-for-2FA', 1); |
1302 |
%info = ();# We remove the warnings/errors we may have set incorrectly before |
1303 |
} |
1304 |
} |
1305 |
|
1256 |
# finished authentification, now respond |
1306 |
# finished authentification, now respond |
1257 |
if ( $loggedin || $authnotrequired ) |
1307 |
if ( ( $loggedin || $authnotrequired ) && !$additional_auth_needed ) { |
1258 |
{ |
|
|
1259 |
# successful login |
1308 |
# successful login |
1260 |
unless ($cookie) { |
1309 |
unless ($cookie) { |
1261 |
$cookie = $query->cookie( |
1310 |
$cookie = $query->cookie( |
Lines 1289-1304
sub checkauth {
Link Here
|
1289 |
# |
1338 |
# |
1290 |
# |
1339 |
# |
1291 |
|
1340 |
|
|
|
1341 |
my $patron = Koha::Patrons->find({ userid => $q_userid }); # Not necessary logged in! |
1342 |
|
1292 |
# get the inputs from the incoming query |
1343 |
# get the inputs from the incoming query |
1293 |
my @inputs = (); |
1344 |
my @inputs = (); |
|
|
1345 |
my @inputs_to_clean = qw( userid password ticket logout.x otp_token ); |
1294 |
foreach my $name ( param $query) { |
1346 |
foreach my $name ( param $query) { |
1295 |
(next) if ( $name eq 'userid' || $name eq 'password' || $name eq 'ticket' ); |
1347 |
next if grep { $name eq $_ } @inputs_to_clean; |
1296 |
my @value = $query->multi_param($name); |
1348 |
my @value = $query->multi_param($name); |
1297 |
push @inputs, { name => $name, value => $_ } for @value; |
1349 |
push @inputs, { name => $name, value => $_ } for @value; |
1298 |
} |
1350 |
} |
1299 |
|
1351 |
|
1300 |
my $patron = Koha::Patrons->find({ userid => $q_userid }); # Not necessary logged in! |
|
|
1301 |
|
1302 |
my $LibraryNameTitle = C4::Context->preference("LibraryName"); |
1352 |
my $LibraryNameTitle = C4::Context->preference("LibraryName"); |
1303 |
$LibraryNameTitle =~ s/<(?:\/?)(?:br|p)\s*(?:\/?)>/ /sgi; |
1353 |
$LibraryNameTitle =~ s/<(?:\/?)(?:br|p)\s*(?:\/?)>/ /sgi; |
1304 |
$LibraryNameTitle =~ s/<(?:[^<>'"]|'(?:[^']*)'|"(?:[^"]*)")*>//sg; |
1354 |
$LibraryNameTitle =~ s/<(?:[^<>'"]|'(?:[^']*)'|"(?:[^"]*)")*>//sg; |
Lines 1346-1351
sub checkauth {
Link Here
|
1346 |
$template->param( SCI_login => 1 ) if ( $query->param('sci_user_login') ); |
1396 |
$template->param( SCI_login => 1 ) if ( $query->param('sci_user_login') ); |
1347 |
$template->param( OpacPublic => C4::Context->preference("OpacPublic") ); |
1397 |
$template->param( OpacPublic => C4::Context->preference("OpacPublic") ); |
1348 |
$template->param( loginprompt => 1 ) unless $info{'nopermission'}; |
1398 |
$template->param( loginprompt => 1 ) unless $info{'nopermission'}; |
|
|
1399 |
if ( $additional_auth_needed ) { |
1400 |
$template->param( |
1401 |
TwoFA_prompt => 1, |
1402 |
invalid_otp_token => $invalid_otp_token, |
1403 |
); |
1404 |
} |
1349 |
|
1405 |
|
1350 |
if ( $type eq 'opac' ) { |
1406 |
if ( $type eq 'opac' ) { |
1351 |
require Koha::Virtualshelves; |
1407 |
require Koha::Virtualshelves; |
Lines 1454-1459
Possible return values in C<$status> are:
Link Here
|
1454 |
|
1510 |
|
1455 |
=item "restricted" -- The IP has changed (if SessionRestrictionByIP) |
1511 |
=item "restricted" -- The IP has changed (if SessionRestrictionByIP) |
1456 |
|
1512 |
|
|
|
1513 |
=item "additional-auth-needed -- User is in an authentication process that is not finished |
1514 |
|
1457 |
=back |
1515 |
=back |
1458 |
|
1516 |
|
1459 |
=cut |
1517 |
=cut |
Lines 1718-1734
sub check_cookie_auth {
Link Here
|
1718 |
$session->param( 'lasttime', time() ); |
1776 |
$session->param( 'lasttime', time() ); |
1719 |
my $flags = defined($flagsrequired) ? haspermission( $userid, $flagsrequired ) : 1; |
1777 |
my $flags = defined($flagsrequired) ? haspermission( $userid, $flagsrequired ) : 1; |
1720 |
if ($flags) { |
1778 |
if ($flags) { |
1721 |
C4::Context->_new_userenv($sessionID); |
1779 |
return ( "additional-auth-needed", $session ) |
1722 |
C4::Context->interface($session->param('interface')); |
1780 |
if $session->param('waiting-for-2FA'); |
1723 |
C4::Context->set_userenv( |
1781 |
|
1724 |
$session->param('number'), $session->param('id') // '', |
|
|
1725 |
$session->param('cardnumber'), $session->param('firstname'), |
1726 |
$session->param('surname'), $session->param('branch'), |
1727 |
$session->param('branchname'), $session->param('flags'), |
1728 |
$session->param('emailaddress'), $session->param('shibboleth'), |
1729 |
$session->param('desk_id'), $session->param('desk_name'), |
1730 |
$session->param('register_id'), $session->param('register_name') |
1731 |
); |
1732 |
return ( "ok", $session ); |
1782 |
return ( "ok", $session ); |
1733 |
} else { |
1783 |
} else { |
1734 |
$session->delete(); |
1784 |
$session->delete(); |