| 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(); |