From 6eaf0d6a6eaa6403066d7b3ba9f4fd4fdd28d2d7 Mon Sep 17 00:00:00 2001 From: Galen Charlton Date: Tue, 20 Nov 2007 17:08:59 -0600 Subject: [PATCH] bibllios integration: added authentication Signed-off-by: Chris Cormack Signed-off-by: Joshua Ferraro --- C4/Auth.pm | 250 +++++++++++++++++++++++++++++++++++++++++++++++++ biblios/authentication | 51 ++++++++++ 2 files changed, 301 insertions(+) create mode 100755 biblios/authentication diff --git a/C4/Auth.pm b/C4/Auth.pm index d77b574894..e3226f8700 100755 --- a/C4/Auth.pm +++ b/C4/Auth.pm @@ -87,6 +87,9 @@ C4::Auth - Authenticates Koha users &checkauth &get_template_and_user ); +@EXPORT_OK = qw( + &check_api_auth +); =item get_template_and_user @@ -752,6 +755,253 @@ sub checkauth { exit; } +=item check_api_auth + + ($status, $cookie, $sessionId) = check_api_auth($query, $userflags); + +Given a CGI query containing the parameters 'userid' and 'password' and/or a session +cookie, determine if the user has the privileges specified by C<$userflags>. + +C is is meant for authenticating users of web services, and +consequently will always return and will not attempt to redirect the user +agent. + +If a valid session cookie is already present, check_api_auth will return a status +of "ok", the cookie, and the Koha session ID. + +If no session cookie is present, check_api_auth will check the 'userid' and 'password +parameters and create a session cookie and Koha session if the supplied credentials +are OK. + +Possible return values in C<$status> are: + +=over 4 + +=item "ok" -- user authenticated; C<$cookie> and C<$sessionid> have valid values. + +=item "failed" -- credentials are not correct; C<$cookie> and C<$sessionid> are undef + +=item "maintenance" -- DB is in maintenance mode; no login possible at the moment + +=item "expired -- session cookie has expired; API user should resubmit userid and password + +=back + +=cut + +sub check_api_auth { + my $query = shift; + my $flagsrequired = shift; + + my $dbh = C4::Context->dbh; + my $timeout = C4::Context->preference('timeout'); + $timeout = 600 unless $timeout; + + unless (C4::Context->preference('Version')) { + # database has not been installed yet + return ("maintenance", undef, undef); + } + my $kohaversion=C4::Context::KOHAVERSION; + $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/; + if (C4::Context->preference('Version') < $kohaversion) { + # database in need of version update; assume that + # no API should be called while databsae is in + # this condition. + return ("maintenance", undef, undef); + } + + # FIXME -- most of what follows is a copy-and-paste + # of code from checkauth. There is an obvious need + # for refactoring to separate the various parts of + # the authentication code, but as of 2007-11-19 this + # is deferred so as to not introduce bugs into the + # regular authentication code for Koha 3.0. + + # see if we have a valid session cookie already + # however, if a userid parameter is present (i.e., from + # a form submission, assume that any current cookie + # is to be ignored + my $sessionID = undef; + unless ($query->param('userid')) { + $sessionID = $query->cookie("CGISESSID"); + } + if ($sessionID) { + my $storage_method = C4::Context->preference('SessionStorage'); + my $session; + if ($storage_method eq 'mysql'){ + $session = new CGI::Session("driver:MySQL", $sessionID, {Handle=>$dbh}); + } + elsif ($storage_method eq 'Pg') { + $session = new CGI::Session("driver:PostgreSQL", $sessionID, {Handle=>$dbh}); + } + else { + # catch all defaults to tmp should work on all systems + $session = new CGI::Session("driver:File", $sessionID, {Directory=>'/tmp'}); + } + C4::Context->_new_userenv($sessionID); + if ($session) { + C4::Context::set_userenv( + $session->param('number'), $session->param('id'), + $session->param('cardnumber'), $session->param('firstname'), + $session->param('surname'), $session->param('branch'), + $session->param('branchname'), $session->param('flags'), + $session->param('emailaddress'), $session->param('branchprinter') + ); + + my $ip = $session->param('ip'); + my $lasttime = $session->param('lasttime'); + my $userid = $session->param('id'); + if ( $lasttime < time() - $timeout ) { + # time out + $session->delete(); + C4::Context->_unset_userenv($sessionID); + $userid = undef; + $sessionID = undef; + return ("expired", undef, undef); + } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) { + # IP address changed + $session->delete(); + C4::Context->_unset_userenv($sessionID); + $userid = undef; + $sessionID = undef; + return ("expired", undef, undef); + } else { + my $cookie = $query->cookie( CGISESSID => $session->id ); + $session->param('lasttime',time()); + my $flags = haspermission( $dbh, $userid, $flagsrequired ); + if ($flags) { + return ("ok", $cookie, $sessionID); + } else { + $session->delete(); + C4::Context->_unset_userenv($sessionID); + $userid = undef; + $sessionID = undef; + return ("failed", undef, undef); + } + } + } else { + return ("expired", undef, undef); + } + } else { + # new login + my $userid = $query->param('userid'); + my $password = $query->param('password'); + unless ($userid and $password) { + # caller did something wrong, fail the authenticateion + return ("failed", undef, undef); + } + my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password ); + if ($return and haspermission( $dbh, $userid, $flagsrequired)) { + my $storage_method = C4::Context->preference('SessionStorage'); + my $session; + if ($storage_method eq 'mysql'){ + $session = new CGI::Session("driver:MySQL", $sessionID, {Handle=>$dbh}); + } elsif ($storage_method eq 'Pg') { + $session = new CGI::Session("driver:PostgreSQL", $sessionID, {Handle=>$dbh}); + } else { + # catch all defaults to tmp should work on all systems + $session = new CGI::Session("driver:File", $sessionID, {Directory=>'/tmp'}); + } + return ("failed", undef, undef) unless $session; + + my $sessionID = $session->id; + C4::Context->_new_userenv($sessionID); + my $cookie = $query->cookie(CGISESSID => $sessionID); + if ( $return == 1 ) { + my ( + $borrowernumber, $firstname, $surname, + $userflags, $branchcode, $branchname, + $branchprinter, $emailaddress + ); + my $sth = + $dbh->prepare( +"select borrowernumber, firstname, surname, flags, borrowers.branchcode, branches.branchname as branchname,branches.branchprinter as branchprinter, email from borrowers left join branches on borrowers.branchcode=branches.branchcode where userid=?" + ); + $sth->execute($userid); + ( + $borrowernumber, $firstname, $surname, + $userflags, $branchcode, $branchname, + $branchprinter, $emailaddress + ) = $sth->fetchrow if ( $sth->rows ); + + unless ($sth->rows ) { + my $sth = $dbh->prepare( +"select borrowernumber, firstname, surname, flags, borrowers.branchcode, branches.branchname as branchname, branches.branchprinter as branchprinter, email from borrowers left join branches on borrowers.branchcode=branches.branchcode where cardnumber=?" + ); + $sth->execute($cardnumber); + ( + $borrowernumber, $firstname, $surname, + $userflags, $branchcode, $branchname, + $branchprinter, $emailaddress + ) = $sth->fetchrow if ( $sth->rows ); + + unless ( $sth->rows ) { + $sth->execute($userid); + ( + $borrowernumber, $firstname, $surname, $userflags, + $branchcode, $branchname, $branchprinter, $emailaddress + ) = $sth->fetchrow if ( $sth->rows ); + } + } + + my $ip = $ENV{'REMOTE_ADDR'}; + # if they specify at login, use that + if ($query->param('branch')) { + $branchcode = $query->param('branch'); + $branchname = GetBranchName($branchcode); + } + my $branches = GetBranches(); + my @branchesloop; + foreach my $br ( keys %$branches ) { + # now we work with the treatment of ip + my $domain = $branches->{$br}->{'branchip'}; + if ( $domain && $ip =~ /^$domain/ ) { + $branchcode = $branches->{$br}->{'branchcode'}; + + # new op dev : add the branchprinter and branchname in the cookie + $branchprinter = $branches->{$br}->{'branchprinter'}; + $branchname = $branches->{$br}->{'branchname'}; + } + } + $session->param('number',$borrowernumber); + $session->param('id',$userid); + $session->param('cardnumber',$cardnumber); + $session->param('firstname',$firstname); + $session->param('surname',$surname); + $session->param('branch',$branchcode); + $session->param('branchname',$branchname); + $session->param('flags',$userflags); + $session->param('emailaddress',$emailaddress); + $session->param('ip',$session->remote_addr()); + $session->param('lasttime',time()); + } elsif ( $return == 2 ) { + #We suppose the user is the superlibrarian + $session->param('number',0); + $session->param('id',C4::Context->config('user')); + $session->param('cardnumber',C4::Context->config('user')); + $session->param('firstname',C4::Context->config('user')); + $session->param('surname',C4::Context->config('user')); + $session->param('branch','NO_LIBRARY_SET'); + $session->param('branchname','NO_LIBRARY_SET'); + $session->param('flags',1); + $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress')); + $session->param('ip',$session->remote_addr()); + $session->param('lasttime',time()); + } + C4::Context::set_userenv( + $session->param('number'), $session->param('id'), + $session->param('cardnumber'), $session->param('firstname'), + $session->param('surname'), $session->param('branch'), + $session->param('branchname'), $session->param('flags'), + $session->param('emailaddress'), $session->param('branchprinter') + ); + return ("ok", $cookie, $sessionID); + } else { + return ("failed", undef, undef); + } + } +} + sub checkpw { my ( $dbh, $userid, $password ) = @_; diff --git a/biblios/authentication b/biblios/authentication new file mode 100755 index 0000000000..f36d1335a5 --- /dev/null +++ b/biblios/authentication @@ -0,0 +1,51 @@ +#!/usr/bin/perl + +# Copyright 2007 LibLime +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with +# Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place, +# Suite 330, Boston, MA 02111-1307 USA +# + +use strict; +use CGI; +use C4::Auth qw/check_api_auth/; +use XML::Simple; + +my $query = new CGI; + +# The authentication strategy for the biblios web +# services is as follows. +# +# 1. biblios POSTs to the authenticate API with URL-encoded +# form parameters 'userid' and 'password'. If the credentials +# belong to a valid user with the 'editcatalogue' privilege, +# a session cookie is returned and a Koha session created. Otherwise, an +# appropriate error is returned. +# 2. For subsequent calls to the biblios APIs, the user agent +# should submit the same session cookie. If the cookie is +# not supplied or does not correspond to a valid session, the API +# will redirect to this authentication API. +# 3. The session cookie should not be (directly) sent back to the user's +# web browser, but instead should be stored and submitted by biblios. + + +my ($status, $cookie, $sessionID) = check_api_auth($query, { editcatalogue => 1} ); + +if ($status eq "ok") { + print $query->header(-type => 'text/xml', cookie => $cookie); +} else { + print $query->header(-type => 'text/xml'); +} +print XMLout({ status => $status }, NoAttr => 1, RootName => 'response', XMLDecl => 1); -- 2.11.0