From cd55420152aab36a8d2791ec4a4e6a1f1b43289a Mon Sep 17 00:00:00 2001 From: Robin Sheat Date: Mon, 30 Mar 2015 11:29:22 +1300 Subject: [PATCH] Bug 13932: add support for a trusted HTTP header This adds support for a 'trusted_header' option in koha-conf.xml that specified an HTTP header that you trust that contains the userid. This is to allow Koha to be behind a reverse proxy (for example, running plack fronted by apache) that does user authentication/authorisation. Note: for reasons I can't really tell, this doesn't work when apache is running Koha as CGI, but does work under plack. Test plan: * have a koha-plack setup. * configure apache to send it a header: RequestHeader set X_REMOTE_USER "testuserid" * add X_REMOTE_USER to koha-conf.xml * verify that the user behaves as though they're logged in. --- C4/Auth.pm | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 66 insertions(+), 3 deletions(-) diff --git a/C4/Auth.pm b/C4/Auth.pm index f5e3316..653b601 100644 --- a/C4/Auth.pm +++ b/C4/Auth.pm @@ -678,6 +678,27 @@ sub _version_check { } } +=head2 get_header + + my $header_val = get_header($header); + +This retrieves the HTTP header from the CGI request. Note that this seems to +not work with regular CGI (those values end up in the environment anyway), but +it does work with plack. + +If there is no matching header, C is returned. + +=cut + +sub get_header { + my ($header) = @_; + + my $q = CGI->new(); + # Prepend HTTP_ as that's how they come through + my $h_val = $q->http('HTTP_' . $header); + return $h_val; +} + sub _session_log { (@_) or return 0; open my $fh, '>>', "/tmp/sessionlog" or warn "ERROR: Cannot append to /tmp/sessionlog"; @@ -723,6 +744,8 @@ sub checkauth { # 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') // ''; + my $trusted_header = C4::Context->config('trusted_header'); + my $trust_head_val = get_header($trusted_header) if $trusted_header; # Basic authentication is incompatible with the use of Shibboleth, # as Shibboleth may return REMOTE_USER as a Shibboleth attribute, @@ -744,12 +767,29 @@ sub checkauth { ); $loggedin = 1; } + elsif ( $userid = $trust_head_val ) { + + # This uses something like + # X_REMOTE_USER + # in koha-conf.xml, and checks that header on the incoming request. + # If it is there and contains a user ID, we believe it and log the + # user in with that. This is intended for things like plack behind a + # reverse proxy that does auth, and puts the user ID into a header. + # + # Basically, we treat it just like basic auth. + $cookie = $query->cookie( + -name => 'CGISESSID', + -value => '', + -expires => '', + -HttpOnly => 1, + ); + $loggedin = check_user_exists($userid); + } elsif ($persona) { # we dont want to set a session because we are being called by a persona callback } - elsif ( $sessionID = $query->cookie("CGISESSID") ) - { # assignment, not comparison + elsif ( $sessionID = $query->cookie("CGISESSID") ) { # assignment, not comparison my $session = get_session($sessionID); C4::Context->_new_userenv($sessionID); my ( $ip, $lasttime, $sessiontype ); @@ -884,7 +924,8 @@ sub checkauth { || $userid || ( $shib && $shib_login ) || $pki_field ne 'None' - || $persona ) + || $persona + || $trusted_header ) { my $password = $query->param('password'); my $shibSuccess = 0; @@ -1798,6 +1839,28 @@ sub checkpw_hash { return $hash eq $stored_hash; } +=head2 check_user_exists + + my $exists = check_user_exists($userid); + +This does a very simple check to see if a user could log in with the provided +userid. That is, does the userid exist as a person's userid or cardnumber +in the database. Returns a true value if it does, false otherwise. + +This is to allow koha to ensure that third party auth systems give us a real +user ID for a user, not just assuming they're real. + +=cut + +sub check_user_exists { + my ($userid) = @_; + + my $dbh = C4::Context->dbh(); + my $sth = $dbh->prepare('SELECT userid FROM borrowers WHERE userid=? OR cardnumber=? LIMIT 1'); + $sth->execute($userid, $userid); + return !!$sth->fetchrow_arrayref(); # coerce to boolean +} + =head2 getuserflags my $authflags = getuserflags($flags, $userid, [$dbh]); -- 2.1.0