From acaffcf53eb022db3f248dd284da34c15e7ac918 Mon Sep 17 00:00:00 2001 From: arensb Date: Mon, 7 Oct 2002 00:34:24 +0000 Subject: [PATCH] Added POD and some comments. --- C4/Auth.pm | 169 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 158 insertions(+), 11 deletions(-) diff --git a/C4/Auth.pm b/C4/Auth.pm index 42c81625d6..44cf5ddeea 100644 --- a/C4/Auth.pm +++ b/C4/Auth.pm @@ -30,12 +30,90 @@ use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); # set the version for version checking $VERSION = 0.01; +=head1 NAME + +C4::Auth - Authenticates Koha users + +=head1 SYNOPSIS + + use CGI; + use C4::Auth; + + $query = new CGI; + ($userid, $cookie, $sessionID) = &checkauth($query); + +=head1 DESCRIPTION + +This module provides authentication for Koha users. + +=head1 FUNCTIONS + +=over 2 + +=cut + @ISA = qw(Exporter); @EXPORT = qw( &checkauth ); +=item checkauth + ($userid, $cookie, $sessionID) = &checkauth($query, $noauth); + +Verifies that the user is authorized to run this script. Note that +C<&checkauth> will return if and only if the user is authorized, so it +should be called early on, before any unfinished operations (i.e., if +you've opened a file, then C<&checkauth> won't close it for you). + +C<$query> is the CGI object for the script calling C<&checkauth>. + +The C<$noauth> argument is optional. If it is set, then no +authorization is required for the script. + +C<&checkauth> fetches user and session information from C<$query> and +ensures that the user is authorized to run scripts that require +authorization. + +If C<$query> does not have a valid session ID associated with it +(i.e., the user has not logged in) or if the session has expired, +C<&checkauth> presents the user with a login page (from the point of +view of the original script, C<&checkauth> does not return). Once the +user has authenticated, C<&checkauth> restarts the original script +(this time, C<&checkauth> returns). + +C<&checkauth> returns a user ID, a cookie, and a session ID. The +cookie should be sent back to the browser; it verifies that the user +has authenticated. + +=cut +#' +# FIXME - (Or rather, proofreadme) +# As I understand it, the 'sessionqueries' table in the Koha database +# is supposed to save state while the user authenticates. If +# (re-)authentication is required, &checkauth saves the browser's +# original call to a new entry in sessionqueries, then presents a form +# for the user to authenticate. Once the user has authenticated +# visself, &checkauth retrieves the stored information from +# sessionqueries and allows the original request to proceed. +# +# One problem, however, is that sessionqueries only stores the URL, +# not the various values passed along from an HTML form. Thus, if the +# request came from a form and contains information on stuff to change +# (e.g., modify the contents of a virtual bookshelf), but the session +# has timed out, then when &checkauth finally allows the request to +# proceed, it will not contain the user's modifications. This is bad. +# +# Another problem is that entries in sessionqueries are supposed to be +# temporary, but there's no mechanism for removing them in case of +# error (e.g., the user can't remember vis password and walks away, or +# if the user's machine crashes in the middle of authentication). +# +# Perhaps a better implementation would be to use $query->param to get +# the parameter with which the original script was invoked, and pass +# that along through all of the authentication pages. That way, all of +# the pertinent information would be preserved, and the sessionqueries +# table could be removed. sub checkauth { my $query=shift; @@ -48,33 +126,59 @@ sub checkauth { -expires => '+1y'); return ($userid, $cookie, ''); } + + # Get session ID from cookie. my $sessionID=$query->cookie('sessionID'); + # FIXME - Error-checking: if the user isn't allowing cookies, + # $sessionID will be undefined. Don't confuse this with an + # expired cookie. + my $message=''; + # Make sure the session ID is (still) good. my $dbh = C4::Context->dbh; my $sth=$dbh->prepare("select userid,ip,lasttime from sessions where sessionid=?"); $sth->execute($sessionID); if ($sth->rows) { my ($userid, $ip, $lasttime) = $sth->fetchrow; + # FIXME - Back door for tonnensen if ($lasttime45 seconds, and + # doesn't belong to user tonnensen. It has expired. $message="You have been logged out due to inactivity."; + + # Remove this session ID from the list of active sessions. + # FIXME - Ought to have a cron job clean this up as well. my $sti=$dbh->prepare("delete from sessions where sessionID=?"); $sti->execute($sessionID); - my $scriptname=$ENV{'SCRIPT_NAME'}; + + # Add an entry to sessionqueries, so that we can restart + # the script once the user has authenticated. + my $scriptname=$ENV{'SCRIPT_NAME'}; # FIXME - Unused my $selfurl=$query->self_url(); $sti=$dbh->prepare("insert into sessionqueries (sessionID, userid, value) values (?, ?, ?)"); $sti->execute($sessionID, $userid, $selfurl); + + # Log the fact that someone tried to use an expired session ID. + # FIXME - Ought to have a better logging mechanism, + # ideally some wrapper that logs either to a + # user-specified file, or to syslog, as determined by + # either an entry in /etc/koha.conf, or a system + # preference. open L, ">>/tmp/sessionlog"; my $time=localtime(time()); printf L "%20s from %16s logged out at %30s (inactivity).\n", $userid, $ip, $time; close L; } elsif ($ip ne $ENV{'REMOTE_ADDR'}) { - # Different ip than originally logged in from + # This session is coming from an IP address other than the + # one where it was set. The user might be doing something + # naughty. my $newip=$ENV{'REMOTE_ADDR'}; $message="ERROR ERROR ERROR ERROR
Attempt to re-use a cookie from a different ip address.
(authenticated from $ip, this request from $newip)"; } else { + # This appears to be a valid session. Update the time + # stamp on it and return. my $cookie=$query->cookie(-name => 'sessionID', -value => $sessionID, -expires => '+1y'); @@ -84,23 +188,42 @@ sub checkauth { } } - + # If we get this far, it's because we haven't received a cookie + # with a valid session ID. Need to start a new session and set a + # new cookie. if ($authnotrequired) { + # This script doesn't require the user to be logged in. Return + # just the cookie, without user ID or session ID information. my $cookie=$query->cookie(-name => 'sessionID', -value => '', -expires => '+1y'); return('', $cookie, ''); } else { + # This script requires authorization. Assume that we were + # given user and password information; generate a new session. + + # Generate a new session ID. ($sessionID) || ($sessionID=int(rand()*100000).'-'.time()); my $userid=$query->param('userid'); my $password=$query->param('password'); if (checkpw($dbh, $userid, $password)) { + # The given password is valid + + # Delete any old copies of this session. my $sti=$dbh->prepare("delete from sessions where sessionID=? and userid=?"); $sti->execute($sessionID, $userid); + + # Add this new session to the 'sessions' table. $sti=$dbh->prepare("insert into sessions (sessionID, userid, ip,lasttime) values (?, ?, ?, ?)"); $sti->execute($sessionID, $userid, $ENV{'REMOTE_ADDR'}, time()); + + # See if there's an entry for this session ID and user in + # the 'sessionqueries' table. If so, then use that entry + # to generate an HTTP redirect that'll take the user to + # where ve wanted to go in the first place. $sti=$dbh->prepare("select value from sessionqueries where sessionID=? and userid=?"); + # FIXME - There is no sessionqueries.value $sti->execute($sessionID, $userid); if ($sti->rows) { my $stj=$dbh->prepare("delete from sessionqueries where sessionID=?"); @@ -118,6 +241,9 @@ sub checkauth { -expires => '+1y'); return ($userid, $cookie, $sessionID); } else { + # Either we weren't given a user id and password, or else + # the password was invalid. + if ($userid) { $message="Invalid userid or password entered."; } @@ -174,32 +300,53 @@ sub checkauth { } } - +# checkpw +# Takes a database handle, user ID, and password, and verifies that +# the password is good. The user ID may be either a user ID or a card +# number. +# Returns 1 if the password is good, or 0 otherwise. sub checkpw { # This should be modified to allow a select of authentication schemes (ie LDAP) # as well as local authentication through the borrowers tables passwd field # my ($dbh, $userid, $password) = @_; - my $sth=$dbh->prepare("select password from borrowers where userid=?"); + my $sth; + + # Try the user ID. + $sth = $dbh->prepare("select password from borrowers where userid=?"); $sth->execute($userid); if ($sth->rows) { my ($md5password) = $sth->fetchrow; if (md5_base64($password) eq $md5password) { - return 1; + return 1; # The password matches } } - # FIXME - There's already a $sth in this scope. - my $sth=$dbh->prepare("select password from borrowers where cardnumber=?"); + + # Try the card number. + $sth = $dbh->prepare("select password from borrowers where cardnumber=?"); $sth->execute($userid); if ($sth->rows) { my ($md5password) = $sth->fetchrow; if (md5_base64($password) eq $md5password) { - return 1; + return 1; # The password matches } } - return 0; + return 0; # Either there's no such user, or the password + # doesn't match. } END { } # module clean-up code here (global destructor) + +1; +__END__ +=back + +=head1 SEE ALSO + +L + +L + +=cut -- 2.39.5