|
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 858-863
sub checkauth {
Link Here
|
| 858 |
my $q_userid = $query->param('userid') // ''; |
859 |
my $q_userid = $query->param('userid') // ''; |
| 859 |
|
860 |
|
| 860 |
my $session; |
861 |
my $session; |
|
|
862 |
my $invalid_otp_token; |
| 863 |
my $require_2FA = ( C4::Context->preference('TwoFactorAuthentication') && $type ne "OPAC" ) ? 1 : 0; |
| 864 |
my $auth_challenge_complete; |
| 861 |
|
865 |
|
| 862 |
# Basic authentication is incompatible with the use of Shibboleth, |
866 |
# Basic authentication is incompatible with the use of Shibboleth, |
| 863 |
# as Shibboleth may return REMOTE_USER as a Shibboleth attribute, |
867 |
# as Shibboleth may return REMOTE_USER as a Shibboleth attribute, |
|
Lines 889-901
sub checkauth {
Link Here
|
| 889 |
{ remote_addr => $ENV{REMOTE_ADDR}, skip_version_check => 1 } |
893 |
{ remote_addr => $ENV{REMOTE_ADDR}, skip_version_check => 1 } |
| 890 |
); |
894 |
); |
| 891 |
|
895 |
|
|
|
896 |
if ( $return eq 'ok' || $return eq 'additional-auth-needed' ) { |
| 897 |
$userid = $session->param('id'); |
| 898 |
} |
| 899 |
|
| 900 |
$additional_auth_needed = ( $return eq 'additional-auth-needed' ) ? 1 : 0; |
| 901 |
|
| 902 |
# We are at the second screen if the waiting-for-2FA is set in session |
| 903 |
# and otp_token param has been passed |
| 904 |
if ( $require_2FA |
| 905 |
&& $additional_auth_needed |
| 906 |
&& ( my $otp_token = $query->param('otp_token') ) ) |
| 907 |
{ |
| 908 |
my $patron = Koha::Patrons->find( { userid => $userid } ); |
| 909 |
my $auth = Koha::Auth::TwoFactorAuth::get_auth( { patron => $patron } ); |
| 910 |
my $verified = $auth->verify($otp_token); |
| 911 |
$auth->clear; |
| 912 |
if ( $verified ) { |
| 913 |
# The token is correct, the user is fully logged in! |
| 914 |
$additional_auth_needed = 0; |
| 915 |
$session->param( 'waiting-for-2FA', 0 ); |
| 916 |
$return = "ok"; |
| 917 |
$auth_challenge_complete = 1; |
| 918 |
|
| 919 |
# This is an ugly trick to pass the test |
| 920 |
# $query->param('koha_login_context') && ( $q_userid ne $userid ) |
| 921 |
# few lines later |
| 922 |
$q_userid = $userid; |
| 923 |
} |
| 924 |
else { |
| 925 |
$invalid_otp_token = 1; |
| 926 |
} |
| 927 |
} |
| 928 |
|
| 892 |
if ( $return eq 'ok' ) { |
929 |
if ( $return eq 'ok' ) { |
| 893 |
Koha::Logger->get->debug(sprintf "AUTH_SESSION: (%s)\t%s %s - %s", map { $session->param($_) || q{} } qw(cardnumber firstname surname branch)); |
930 |
Koha::Logger->get->debug(sprintf "AUTH_SESSION: (%s)\t%s %s - %s", map { $session->param($_) || q{} } qw(cardnumber firstname surname branch)); |
| 894 |
|
931 |
|
| 895 |
my $s_userid = $session->param('id'); |
932 |
if ( ( $query->param('koha_login_context') && ( $q_userid ne $userid ) ) |
| 896 |
$userid = $s_userid; |
|
|
| 897 |
|
| 898 |
if ( ( $query->param('koha_login_context') && ( $q_userid ne $s_userid ) ) |
| 899 |
|| ( $cas && $query->param('ticket') && !C4::Context->userenv->{'id'} ) |
933 |
|| ( $cas && $query->param('ticket') && !C4::Context->userenv->{'id'} ) |
| 900 |
|| ( $shib && $shib_login && !$logout && !C4::Context->userenv->{'id'} ) |
934 |
|| ( $shib && $shib_login && !$logout && !C4::Context->userenv->{'id'} ) |
| 901 |
) { |
935 |
) { |
|
Lines 905-933
sub checkauth {
Link Here
|
| 905 |
$anon_search_history = $session->param('search_history'); |
939 |
$anon_search_history = $session->param('search_history'); |
| 906 |
$session->delete(); |
940 |
$session->delete(); |
| 907 |
$session->flush; |
941 |
$session->flush; |
| 908 |
C4::Context::_unset_userenv($sessionID); |
|
|
| 909 |
$sessionID = undef; |
| 910 |
} |
| 911 |
elsif ($logout) { |
| 912 |
|
| 913 |
# voluntary logout the user |
| 914 |
# check wether the user was using their shibboleth session or a local one |
| 915 |
my $shibSuccess = C4::Context->userenv->{'shibboleth'}; |
| 916 |
$session->delete(); |
| 917 |
$session->flush; |
| 918 |
$cookie = $cookie_mgr->clear_unless( $query->cookie, @$cookie ); |
942 |
$cookie = $cookie_mgr->clear_unless( $query->cookie, @$cookie ); |
| 919 |
C4::Context::_unset_userenv($sessionID); |
943 |
C4::Context::_unset_userenv($sessionID); |
| 920 |
$sessionID = undef; |
944 |
$sessionID = undef; |
| 921 |
|
945 |
} elsif (!$logout) { |
| 922 |
if ($cas and $caslogout) { |
|
|
| 923 |
logout_cas($query, $type); |
| 924 |
} |
| 925 |
|
| 926 |
# If we are in a shibboleth session (shibboleth is enabled, a shibboleth match attribute is set and matches koha matchpoint) |
| 927 |
if ( $shib and $shib_login and $shibSuccess) { |
| 928 |
logout_shib($query); |
| 929 |
} |
| 930 |
} else { |
| 931 |
|
946 |
|
| 932 |
$cookie = $cookie_mgr->replace_in_list( $cookie, $query->cookie( |
947 |
$cookie = $cookie_mgr->replace_in_list( $cookie, $query->cookie( |
| 933 |
-name => 'CGISESSID', |
948 |
-name => 'CGISESSID', |
|
Lines 954-963
sub checkauth {
Link Here
|
| 954 |
} |
969 |
} |
| 955 |
} |
970 |
} |
| 956 |
|
971 |
|
| 957 |
unless ( $loggedin ) { |
972 |
if ( ( !$loggedin && !$additional_auth_needed ) || $logout ) { |
|
|
973 |
$sessionID = undef; |
| 958 |
$userid = undef; |
974 |
$userid = undef; |
| 959 |
} |
975 |
} |
| 960 |
|
976 |
|
|
|
977 |
if ($logout) { |
| 978 |
|
| 979 |
# voluntary logout the user |
| 980 |
# check wether the user was using their shibboleth session or a local one |
| 981 |
my $shibSuccess = C4::Context->userenv->{'shibboleth'}; |
| 982 |
if ( $session ) { |
| 983 |
$session->delete(); |
| 984 |
$session->flush; |
| 985 |
} |
| 986 |
C4::Context::_unset_userenv($sessionID); |
| 987 |
$cookie = $cookie_mgr->clear_unless( $query->cookie, @$cookie ); |
| 988 |
|
| 989 |
if ($cas and $caslogout) { |
| 990 |
logout_cas($query, $type); |
| 991 |
} |
| 992 |
|
| 993 |
# If we are in a shibboleth session (shibboleth is enabled, a shibboleth match attribute is set and matches koha matchpoint) |
| 994 |
if ( $shib and $shib_login and $shibSuccess) { |
| 995 |
logout_shib($query); |
| 996 |
} |
| 997 |
|
| 998 |
$session = undef; |
| 999 |
$additional_auth_needed = 0; |
| 1000 |
} |
| 1001 |
|
| 961 |
unless ( $userid ) { |
1002 |
unless ( $userid ) { |
| 962 |
#we initiate a session prior to checking for a username to allow for anonymous sessions... |
1003 |
#we initiate a session prior to checking for a username to allow for anonymous sessions... |
| 963 |
if( !$session or !$sessionID ) { # if we cleared sessionID, we need a new session |
1004 |
if( !$session or !$sessionID ) { # if we cleared sessionID, we need a new session |
|
Lines 1257-1265
sub checkauth {
Link Here
|
| 1257 |
$session->flush; |
1298 |
$session->flush; |
| 1258 |
} # END unless ($userid) |
1299 |
} # END unless ($userid) |
| 1259 |
|
1300 |
|
|
|
1301 |
if ( $require_2FA && ( $loggedin && !$auth_challenge_complete)) { |
| 1302 |
my $patron = Koha::Patrons->find({userid => $userid}); |
| 1303 |
if ( $patron->auth_method eq 'two-factor' ) { |
| 1304 |
# Ask for the OTP token |
| 1305 |
$additional_auth_needed = 1; |
| 1306 |
$session->param('waiting-for-2FA', 1); |
| 1307 |
%info = ();# We remove the warnings/errors we may have set incorrectly before |
| 1308 |
} |
| 1309 |
} |
| 1310 |
|
| 1260 |
# finished authentification, now respond |
1311 |
# finished authentification, now respond |
| 1261 |
if ( $loggedin || $authnotrequired ) |
1312 |
if ( ( $loggedin || $authnotrequired ) && !$additional_auth_needed ) { |
| 1262 |
{ |
|
|
| 1263 |
# successful login |
1313 |
# successful login |
| 1264 |
unless (@$cookie) { |
1314 |
unless (@$cookie) { |
| 1265 |
$cookie = $cookie_mgr->replace_in_list( $cookie, $query->cookie( |
1315 |
$cookie = $cookie_mgr->replace_in_list( $cookie, $query->cookie( |
|
Lines 1293-1308
sub checkauth {
Link Here
|
| 1293 |
# |
1343 |
# |
| 1294 |
# |
1344 |
# |
| 1295 |
|
1345 |
|
|
|
1346 |
my $patron = Koha::Patrons->find({ userid => $q_userid }); # Not necessary logged in! |
| 1347 |
|
| 1296 |
# get the inputs from the incoming query |
1348 |
# get the inputs from the incoming query |
| 1297 |
my @inputs = (); |
1349 |
my @inputs = (); |
|
|
1350 |
my @inputs_to_clean = qw( userid password ticket logout.x otp_token ); |
| 1298 |
foreach my $name ( param $query) { |
1351 |
foreach my $name ( param $query) { |
| 1299 |
(next) if ( $name eq 'userid' || $name eq 'password' || $name eq 'ticket' ); |
1352 |
next if grep { $name eq $_ } @inputs_to_clean; |
| 1300 |
my @value = $query->multi_param($name); |
1353 |
my @value = $query->multi_param($name); |
| 1301 |
push @inputs, { name => $name, value => $_ } for @value; |
1354 |
push @inputs, { name => $name, value => $_ } for @value; |
| 1302 |
} |
1355 |
} |
| 1303 |
|
1356 |
|
| 1304 |
my $patron = Koha::Patrons->find({ userid => $q_userid }); # Not necessary logged in! |
|
|
| 1305 |
|
| 1306 |
my $LibraryNameTitle = C4::Context->preference("LibraryName"); |
1357 |
my $LibraryNameTitle = C4::Context->preference("LibraryName"); |
| 1307 |
$LibraryNameTitle =~ s/<(?:\/?)(?:br|p)\s*(?:\/?)>/ /sgi; |
1358 |
$LibraryNameTitle =~ s/<(?:\/?)(?:br|p)\s*(?:\/?)>/ /sgi; |
| 1308 |
$LibraryNameTitle =~ s/<(?:[^<>'"]|'(?:[^']*)'|"(?:[^"]*)")*>//sg; |
1359 |
$LibraryNameTitle =~ s/<(?:[^<>'"]|'(?:[^']*)'|"(?:[^"]*)")*>//sg; |
|
Lines 1350-1355
sub checkauth {
Link Here
|
| 1350 |
$template->param( SCI_login => 1 ) if ( $query->param('sci_user_login') ); |
1401 |
$template->param( SCI_login => 1 ) if ( $query->param('sci_user_login') ); |
| 1351 |
$template->param( OpacPublic => C4::Context->preference("OpacPublic") ); |
1402 |
$template->param( OpacPublic => C4::Context->preference("OpacPublic") ); |
| 1352 |
$template->param( loginprompt => 1 ) unless $info{'nopermission'}; |
1403 |
$template->param( loginprompt => 1 ) unless $info{'nopermission'}; |
|
|
1404 |
if ( $additional_auth_needed ) { |
| 1405 |
$template->param( |
| 1406 |
TwoFA_prompt => 1, |
| 1407 |
invalid_otp_token => $invalid_otp_token, |
| 1408 |
); |
| 1409 |
} |
| 1353 |
|
1410 |
|
| 1354 |
if ( $type eq 'opac' ) { |
1411 |
if ( $type eq 'opac' ) { |
| 1355 |
require Koha::Virtualshelves; |
1412 |
require Koha::Virtualshelves; |
|
Lines 1458-1463
Possible return values in C<$status> are:
Link Here
|
| 1458 |
|
1515 |
|
| 1459 |
=item "restricted" -- The IP has changed (if SessionRestrictionByIP) |
1516 |
=item "restricted" -- The IP has changed (if SessionRestrictionByIP) |
| 1460 |
|
1517 |
|
|
|
1518 |
=item "additional-auth-needed -- User is in an authentication process that is not finished |
| 1519 |
|
| 1461 |
=back |
1520 |
=back |
| 1462 |
|
1521 |
|
| 1463 |
=cut |
1522 |
=cut |
|
Lines 1733-1738
sub check_cookie_auth {
Link Here
|
| 1733 |
$session->param('desk_id'), $session->param('desk_name'), |
1792 |
$session->param('desk_id'), $session->param('desk_name'), |
| 1734 |
$session->param('register_id'), $session->param('register_name') |
1793 |
$session->param('register_id'), $session->param('register_name') |
| 1735 |
); |
1794 |
); |
|
|
1795 |
return ( "additional-auth-needed", $session ) |
| 1796 |
if $session->param('waiting-for-2FA'); |
| 1797 |
|
| 1736 |
return ( "ok", $session ); |
1798 |
return ( "ok", $session ); |
| 1737 |
} else { |
1799 |
} else { |
| 1738 |
$session->delete(); |
1800 |
$session->delete(); |