X-Git-Url: http://koha-dev.rot13.org:8081/gitweb/?a=blobdiff_plain;f=C4%2FAuth.pm;h=00eeeef84243fa77a317c283278990d89177341b;hb=e52b493611766b61a0018350adc590ab07e9c94b;hp=748cca6d6255a26a3651d61058bc51e2baf821a8;hpb=537c66403855437d2ce52eeb758fc009c7105633;p=koha_fer diff --git a/C4/Auth.pm b/C4/Auth.pm index 748cca6d62..00eeeef842 100644 --- a/C4/Auth.pm +++ b/C4/Auth.pm @@ -20,7 +20,7 @@ package C4::Auth; use strict; use warnings; use Digest::MD5 qw(md5_base64); -use Storable qw(thaw freeze); +use JSON qw/encode_json decode_json/; use URI::Escape; use CGI::Session; @@ -29,6 +29,7 @@ use C4::Context; use C4::Templates; # to get the template use C4::Branch; # GetBranches use C4::VirtualShelves; +use Koha::AuthUtils qw(hash_password); use POSIX qw/strftime/; use List::MoreUtils qw/ any /; @@ -46,7 +47,10 @@ BEGIN { $debug = $ENV{DEBUG}; @ISA = qw(Exporter); @EXPORT = qw(&checkauth &get_template_and_user &haspermission &get_user_subpermissions); - @EXPORT_OK = qw(&check_api_auth &get_session &check_cookie_auth &checkpw &get_all_subpermissions &get_user_subpermissions); + @EXPORT_OK = qw(&check_api_auth &get_session &check_cookie_auth &checkpw &checkpw_internal &checkpw_hash + &get_all_subpermissions &get_user_subpermissions + ParseSearchHistoryCookie + ); %EXPORT_TAGS = ( EditPermissions => [qw(get_all_subpermissions get_user_subpermissions)] ); $ldap = C4::Context->config('useldapserver') || 0; $cas = C4::Context->preference('casAuthentication'); @@ -131,10 +135,17 @@ VALUES ( ?, ?, ?, ?, ?, EOQ sub get_template_and_user { + my $in = shift; - my $template = - C4::Templates::gettemplate( $in->{'template_name'}, $in->{'type'}, $in->{'query'}, $in->{'is_plugin'} ); my ( $user, $cookie, $sessionID, $flags ); + + my $template = C4::Templates::gettemplate( + $in->{'template_name'}, + $in->{'type'}, + $in->{'query'}, + $in->{'is_plugin'} + ); + if ( $in->{'template_name'} !~m/maintenance/ ) { ( $user, $cookie, $sessionID, $flags ) = checkauth( $in->{'query'}, @@ -203,6 +214,7 @@ sub get_template_and_user { $template->param( CAN_user_reports => 1 ); $template->param( CAN_user_staffaccess => 1 ); $template->param( CAN_user_plugins => 1 ); + $template->param( CAN_user_coursereserves => 1 ); foreach my $module (keys %$all_perms) { foreach my $subperm (keys %{ $all_perms->{$module} }) { $template->param( "CAN_user_${module}_${subperm}" => 1 ); @@ -250,29 +262,25 @@ sub get_template_and_user { # And if there's a cookie with searches performed when the user was not logged in, # we add them to the logged-in search history - my $searchcookie = $in->{'query'}->cookie('KohaOpacRecentSearches'); - if ($searchcookie){ - $searchcookie = uri_unescape($searchcookie); - my @recentSearches = @{thaw($searchcookie) || []}; - if (@recentSearches) { - my $sth = $dbh->prepare($SEARCH_HISTORY_INSERT_SQL); - $sth->execute( $borrowernumber, - $in->{'query'}->cookie("CGISESSID"), - $_->{'query_desc'}, - $_->{'query_cgi'}, - $_->{'total'}, - $_->{'time'}, - ) foreach @recentSearches; - - # And then, delete the cookie's content - my $newsearchcookie = $in->{'query'}->cookie( - -name => 'KohaOpacRecentSearches', - -value => freeze([]), - -HttpOnly => 1, - -expires => '' - ); - $cookie = [$cookie, $newsearchcookie]; - } + my @recentSearches = ParseSearchHistoryCookie($in->{'query'}); + if (@recentSearches) { + my $sth = $dbh->prepare($SEARCH_HISTORY_INSERT_SQL); + $sth->execute( $borrowernumber, + $in->{'query'}->cookie("CGISESSID"), + $_->{'query_desc'}, + $_->{'query_cgi'}, + $_->{'total'}, + $_->{'time'}, + ) foreach @recentSearches; + + # And then, delete the cookie's content + my $newsearchcookie = $in->{'query'}->cookie( + -name => 'KohaOpacRecentSearches', + -value => encode_json([]), + -HttpOnly => 1, + -expires => '' + ); + $cookie = [$cookie, $newsearchcookie]; } } } @@ -281,22 +289,17 @@ sub get_template_and_user { $template->param( sessionID => $sessionID ); my ($total, $pubshelves) = C4::VirtualShelves::GetSomeShelfNames(undef, 'MASTHEAD'); - $template->param( - pubshelves => $total->{pubtotal}, - pubshelvesloop => $pubshelves, - ); + $template->param( + pubshelves => $total->{pubtotal}, + pubshelvesloop => $pubshelves, + ); } # Anonymous opac search history # If opac search history is enabled and at least one search has already been performed if (C4::Context->preference('EnableOpacSearchHistory')) { - my $searchcookie = $in->{'query'}->cookie('KohaOpacRecentSearches'); - if ($searchcookie){ - $searchcookie = uri_unescape($searchcookie); - my @recentSearches = @{thaw($searchcookie) || []}; - # We show the link in opac - if (@recentSearches) { - $template->param(ShowOpacRecentSearchLink => 1); - } + my @recentSearches = ParseSearchHistoryCookie($in->{'query'}); + if (@recentSearches) { + $template->param(ShowOpacRecentSearchLink => 1); } } @@ -336,7 +339,7 @@ sub get_template_and_user { CalendarFirstDayOfWeek => (C4::Context->preference("CalendarFirstDayOfWeek") eq "Sunday")?0:1, CircAutocompl => C4::Context->preference("CircAutocompl"), FRBRizeEditions => C4::Context->preference("FRBRizeEditions"), - IndependantBranches => C4::Context->preference("IndependantBranches"), + IndependentBranches => C4::Context->preference("IndependentBranches"), IntranetNav => C4::Context->preference("IntranetNav"), IntranetmainUserblock => C4::Context->preference("IntranetmainUserblock"), LibraryName => C4::Context->preference("LibraryName"), @@ -359,6 +362,7 @@ sub get_template_and_user { AllowMultipleCovers => C4::Context->preference('AllowMultipleCovers'), EnableBorrowerFiles => C4::Context->preference('EnableBorrowerFiles'), UseKohaPlugins => C4::Context->preference('UseKohaPlugins'), + UseCourseReserves => C4::Context->preference("UseCourseReserves"), ); } else { @@ -367,10 +371,15 @@ sub get_template_and_user { my $LibraryNameTitle = C4::Context->preference("LibraryName"); $LibraryNameTitle =~ s/<(?:\/?)(?:br|p)\s*(?:\/?)>/ /sgi; $LibraryNameTitle =~ s/<(?:[^<>'"]|'(?:[^']*)'|"(?:[^"]*)")*>//sg; - # clean up the busc param in the session if the page is not opac-detail - if (C4::Context->preference("OpacBrowseResults") && $in->{'template_name'} =~ /opac-(.+)\.(?:tt|tmpl)$/ && $1 !~ /^(?:MARC|ISBD)?detail$/) { - my $sessionSearch = get_session($sessionID || $in->{'query'}->cookie("CGISESSID")); - $sessionSearch->clear(["busc"]) if ($sessionSearch->param("busc")); + # clean up the busc param in the session if the page is not opac-detail and not the "add to list" page + if ( C4::Context->preference("OpacBrowseResults") + && $in->{'template_name'} =~ /opac-(.+)\.(?:tt|tmpl)$/ ) { + my $pagename = $1; + unless ( $pagename =~ /^(?:MARC|ISBD)?detail$/ + or $pagename =~ /^addbybiblionumber$/ ) { + my $sessionSearch = get_session($sessionID || $in->{'query'}->cookie("CGISESSID")); + $sessionSearch->clear(["busc"]) if ($sessionSearch->param("busc")); + } } # variables passed from CGI: opac_css_override and opac_search_limits. my $opac_search_limit = $ENV{'OPAC_SEARCH_LIMIT'}; @@ -388,7 +397,7 @@ sub get_template_and_user { AnonSuggestions => "" . C4::Context->preference("AnonSuggestions"), AuthorisedValueImages => C4::Context->preference("AuthorisedValueImages"), BranchesLoop => GetBranchesLoop($opac_name), - BranchCategoriesLoop => GetBranchCategories( undef, undef, 1, $opac_name ), + BranchCategoriesLoop => GetBranchCategories( 'searchdomain', 1, $opac_name ), CalendarFirstDayOfWeek => (C4::Context->preference("CalendarFirstDayOfWeek") eq "Sunday")?0:1, LibraryName => "" . C4::Context->preference("LibraryName"), LibraryNameTitle => "" . $LibraryNameTitle, @@ -398,7 +407,6 @@ sub get_template_and_user { OpacHighlightedWords => C4::Context->preference("OpacHighlightedWords"), OPACItemHolds => C4::Context->preference("OPACItemHolds"), OPACShelfBrowser => "". C4::Context->preference("OPACShelfBrowser"), - OpacShowRecentComments => C4::Context->preference("OpacShowRecentComments"), OPACURLOpenInNewWindow => "" . C4::Context->preference("OPACURLOpenInNewWindow"), OPACUserCSS => "". C4::Context->preference("OPACUserCSS"), OPACMobileUserCSS => "". C4::Context->preference("OPACMobileUserCSS"), @@ -438,13 +446,11 @@ sub get_template_and_user { opacsmallimage => "" . C4::Context->preference("opacsmallimage"), opacuserjs => C4::Context->preference("opacuserjs"), opacuserlogin => "" . C4::Context->preference("opacuserlogin"), - reviewson => C4::Context->preference("reviewson"), ShowReviewer => C4::Context->preference("ShowReviewer"), ShowReviewerPhoto => C4::Context->preference("ShowReviewerPhoto"), suggestion => "" . C4::Context->preference("suggestion"), virtualshelves => "" . C4::Context->preference("virtualshelves"), OPACSerialIssueDisplayCount => C4::Context->preference("OPACSerialIssueDisplayCount"), - OpacAddMastheadLibraryPulldown => C4::Context->preference("OpacAddMastheadLibraryPulldown"), OPACXSLTDetailsDisplay => C4::Context->preference("OPACXSLTDetailsDisplay"), OPACXSLTResultsDisplay => C4::Context->preference("OPACXSLTResultsDisplay"), SyndeticsClientCode => C4::Context->preference("SyndeticsClientCode"), @@ -466,6 +472,20 @@ sub get_template_and_user { $template->param(OpacPublic => '1') if ($user || C4::Context->preference("OpacPublic")); } + + # Check if we were asked using parameters to force a specific language + if ( defined $in->{'query'}->param('language') ) { + # Extract the language, let C4::Templates::getlanguage choose + # what to do + my $language = C4::Templates::getlanguage($in->{'query'},$in->{'type'}); + my $languagecookie = C4::Templates::getlanguagecookie($in->{'query'},$language); + if ( ref $cookie eq 'ARRAY' ) { + push @{ $cookie }, $languagecookie; + } else { + $cookie = [$cookie, $languagecookie]; + } + } + return ( $template, $borrowernumber, $cookie, $flags); } @@ -630,6 +650,7 @@ sub checkauth { # 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'); + my $q_userid = $query->param('userid') // ''; if ( $userid = $ENV{'REMOTE_USER'} ) { # Using Basic Authentication, no cookies required @@ -649,9 +670,11 @@ sub checkauth { my $session = get_session($sessionID); C4::Context->_new_userenv($sessionID); my ($ip, $lasttime, $sessiontype); + my $s_userid = ''; if ($session){ + $s_userid = $session->param('id') // ''; C4::Context::set_userenv( - $session->param('number'), $session->param('id'), + $session->param('number'), $s_userid, $session->param('cardnumber'), $session->param('firstname'), $session->param('surname'), $session->param('branch'), $session->param('branchname'), $session->param('flags'), @@ -664,14 +687,14 @@ sub checkauth { $debug and printf STDERR "AUTH_SESSION: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ; $ip = $session->param('ip'); $lasttime = $session->param('lasttime'); - $userid = $session->param('id'); + $userid = $s_userid; $sessiontype = $session->param('sessiontype') || ''; } - if ( ( ($query->param('koha_login_context')) && ($query->param('userid') ne $session->param('id')) ) + if ( ( $query->param('koha_login_context') && ($q_userid ne $s_userid) ) || ( $cas && $query->param('ticket') ) ) { #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 = " . $query->param('userid') . " but session id = " . $session->param('id'); + $debug and warn "query id = $q_userid but session id = $s_userid"; $session->flush; $session->delete(); C4::Context->_unset_userenv($sessionID); @@ -691,7 +714,7 @@ sub checkauth { logout_cas($query); } } - elsif ( $lasttime < time() - $timeout ) { + elsif ( !$lasttime || ($lasttime < time() - $timeout) ) { # timed logout $info{'timed_out'} = 1; $session->delete() if $session; @@ -739,11 +762,16 @@ sub checkauth { -value => $session->id, -HttpOnly => 1 ); - $userid = $query->param('userid'); + $userid = $q_userid; + my $pki_field = C4::Context->preference('AllowPKIAuth'); + if (! defined($pki_field) ) { + print STDERR "ERROR: Missing system preference AllowPKIAuth.\n"; + $pki_field = 'None'; + } if ( ( $cas && $query->param('ticket') ) || $userid - || ( my $pki_field = C4::Context->preference('AllowPKIAuth') ) ne - 'None' || $persona ) + || $pki_field ne 'None' + || $persona ) { my $password = $query->param('password'); @@ -814,7 +842,7 @@ sub checkauth { my $retuserid; ( $return, $cardnumber, $retuserid ) = checkpw( $dbh, $userid, $password, $query ); - $userid = $retuserid if ( $retuserid ne '' ); + $userid = $retuserid if ( $retuserid ); } if ($return) { #_session_log(sprintf "%20s from %16s logged in at %30s.\n", $userid,$ENV{'REMOTE_ADDR'},(strftime '%c', localtime)); @@ -871,7 +899,7 @@ sub checkauth { $branchname = GetBranchName($branchcode); } my $branches = GetBranches(); - if (C4::Context->boolean_preference('IndependantBranches') && C4::Context->boolean_preference('Autolocation')){ + if (C4::Context->boolean_preference('IndependentBranches') && C4::Context->boolean_preference('Autolocation')){ # we have to check they are coming from the right ip range my $domain = $branches->{$branchcode}->{'branchip'}; if ($ip !~ /^$domain/){ @@ -980,6 +1008,10 @@ sub checkauth { push @inputs, { name => $name, value => $value }; } + my $LibraryNameTitle = C4::Context->preference("LibraryName"); + $LibraryNameTitle =~ s/<(?:\/?)(?:br|p)\s*(?:\/?)>/ /sgi; + $LibraryNameTitle =~ s/<(?:[^<>'"]|'(?:[^']*)'|"(?:[^"]*)")*>//sg; + my $template_name = ( $type eq 'opac' ) ? 'opac-auth.tmpl' : 'auth.tmpl'; my $template = C4::Templates::gettemplate($template_name, $type, $query ); $template->param( @@ -991,7 +1023,8 @@ sub checkauth { casAuthentication => C4::Context->preference("casAuthentication"), suggestion => C4::Context->preference("suggestion"), virtualshelves => C4::Context->preference("virtualshelves"), - LibraryName => C4::Context->preference("LibraryName"), + LibraryName => "" . C4::Context->preference("LibraryName"), + LibraryNameTitle => "" . $LibraryNameTitle, opacuserlogin => C4::Context->preference("opacuserlogin"), OpacNav => C4::Context->preference("OpacNav"), OpacNavRight => C4::Context->preference("OpacNavRight"), @@ -1016,17 +1049,26 @@ sub checkauth { IntranetNav => C4::Context->preference("IntranetNav"), IntranetFavicon => C4::Context->preference("IntranetFavicon"), intranetuserjs => C4::Context->preference("intranetuserjs"), - IndependantBranches=> C4::Context->preference("IndependantBranches"), + IndependentBranches=> C4::Context->preference("IndependentBranches"), AutoLocation => C4::Context->preference("AutoLocation"), wrongip => $info{'wrongip'}, PatronSelfRegistration => C4::Context->preference("PatronSelfRegistration"), PatronSelfRegistrationDefaultCategory => C4::Context->preference("PatronSelfRegistrationDefaultCategory"), persona => C4::Context->preference("Persona"), + opac_css_override => $ENV{'OPAC_CSS_OVERRIDE'}, ); $template->param( OpacPublic => C4::Context->preference("OpacPublic")); $template->param( loginprompt => 1 ) unless $info{'nopermission'}; + if($type eq 'opac'){ + my ($total, $pubshelves) = C4::VirtualShelves::GetSomeShelfNames(undef, 'MASTHEAD'); + $template->param( + pubshelves => $total->{pubtotal}, + pubshelvesloop => $pubshelves, + ); + } + if ($cas) { # Is authentication against multiple CAS servers enabled? @@ -1459,8 +1501,8 @@ sub get_session { } sub checkpw { - my ( $dbh, $userid, $password, $query ) = @_; + if ($ldap) { $debug and print STDERR "## checkpw - checking LDAP\n"; my ($retval,$retcard,$retuserid) = checkpw_ldap(@_); # EXTERNAL AUTH @@ -1470,23 +1512,29 @@ sub checkpw { if ($cas && $query && $query->param('ticket')) { $debug and print STDERR "## checkpw - checking CAS\n"; # In case of a CAS authentication, we use the ticket instead of the password - my $ticket = $query->param('ticket'); + my $ticket = $query->param('ticket'); my ($retval,$retcard,$retuserid) = checkpw_cas($dbh, $ticket, $query); # EXTERNAL AUTH ($retval) and return ($retval,$retcard,$retuserid); - return 0; + return 0; } - # INTERNAL AUTH + return checkpw_internal(@_) +} + +sub checkpw_internal { + my ( $dbh, $userid, $password ) = @_; + my $sth = $dbh->prepare( "select password,cardnumber,borrowernumber,userid,firstname,surname,branchcode,flags from borrowers where userid=?" ); $sth->execute($userid); if ( $sth->rows ) { - my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname, + my ( $stored_hash, $cardnumber, $borrowernumber, $userid, $firstname, $surname, $branchcode, $flags ) = $sth->fetchrow; - if ( md5_base64($password) eq $md5password and $md5password ne "!") { + + if ( checkpw_hash($password, $stored_hash) ) { C4::Context->set_userenv( "$borrowernumber", $userid, $cardnumber, $firstname, $surname, $branchcode, $flags ); @@ -1499,10 +1547,11 @@ sub checkpw { ); $sth->execute($userid); if ( $sth->rows ) { - my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname, + my ( $stored_hash, $cardnumber, $borrowernumber, $userid, $firstname, $surname, $branchcode, $flags ) = $sth->fetchrow; - if ( md5_base64($password) eq $md5password ) { + + if ( checkpw_hash($password, $stored_hash) ) { C4::Context->set_userenv( $borrowernumber, $userid, $cardnumber, $firstname, $surname, $branchcode, $flags ); @@ -1529,6 +1578,21 @@ sub checkpw { return 0; } +sub checkpw_hash { + my ( $password, $stored_hash ) = @_; + + return if $stored_hash eq '!'; + + # check what encryption algorithm was implemented: Bcrypt - if the hash starts with '$2' it is Bcrypt else md5 + my $hash; + if ( substr($stored_hash,0,2) eq '$2') { + $hash = hash_password($password, $stored_hash); + } else { + $hash = md5_base64($password); + } + return $hash eq $stored_hash; +} + =head2 getuserflags my $authflags = getuserflags($flags, $userid, [$dbh]); @@ -1564,7 +1628,6 @@ sub getuserflags { $userflags->{$flag} = 0; } } - # get subpermissions and merge with top-level permissions my $user_subperms = get_user_subpermissions($userid); foreach my $module (keys %$user_subperms) { @@ -1660,7 +1723,8 @@ sub haspermission { my ($userid, $flagsrequired) = @_; my $sth = C4::Context->dbh->prepare("SELECT flags FROM borrowers WHERE userid=?"); $sth->execute($userid); - my $flags = getuserflags($sth->fetchrow(), $userid); + my $row = $sth->fetchrow(); + my $flags = getuserflags($row, $userid); if ( $userid eq C4::Context->config('user') ) { # Super User Account from /etc/koha.conf $flags->{'superlibrarian'} = 1; @@ -1709,6 +1773,15 @@ sub getborrowernumber { return 0; } +sub ParseSearchHistoryCookie { + my $input = shift; + my $search_cookie = $input->cookie('KohaOpacRecentSearches'); + return () unless $search_cookie; + my $obj = eval { decode_json(uri_unescape($search_cookie)) }; + return () unless defined $obj; + return () unless ref $obj eq 'ARRAY'; + return @{ $obj }; +} END { } # module clean-up code here (global destructor) 1; @@ -1720,6 +1793,8 @@ CGI(3) C4::Output(3) +Crypt::Eksblowfish::Bcrypt(3) + Digest::MD5(3) =cut