From b558033b3be39ec252f0fa225bbd6f9d01eaf825 Mon Sep 17 00:00:00 2001 From: Dobrica Pavlinusic Date: Sat, 13 Jul 2013 18:33:34 +0200 Subject: [PATCH] SAML auth for phpSimpleSAML --- C4/Auth.pm | 169 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) diff --git a/C4/Auth.pm b/C4/Auth.pm index 1bbc0bdb37..8e3553a52e 100644 --- a/C4/Auth.pm +++ b/C4/Auth.pm @@ -35,6 +35,8 @@ use Koha::AuthUtils qw(hash_password); use POSIX qw/strftime/; use List::MoreUtils qw/ any /; +use Cache::Memcached; + # use utf8; use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $debug $ldap $cas $caslogout); @@ -644,6 +646,17 @@ sub _timeout_syspref { return $timeout; } +sub clear_saml { + my $query = shift; + my $cookie = [ + $query->cookie( 'CGISESSID' => '' ), + $query->cookie( 'SimpleSAMLAuthToken' => '' ), + $query->cookie( 'AuthMemCookie' => '' ), + $query->cookie( 'PHPSESSID' => '' ), + ]; + return ( undef, $cookie, undef ); # FIXME +} + sub checkauth { my $query = shift; $debug and warn "Checking Auth"; @@ -671,6 +684,7 @@ sub checkauth { my $casparam = $query->param('cas'); my $q_userid = $query->param('userid') // ''; +=for remove-for-saml if ( $userid = $ENV{'REMOTE_USER'} ) { # Using Basic Authentication, no cookies required $cookie = $query->cookie( @@ -681,6 +695,146 @@ sub checkauth { ); $loggedin = 1; } +=cut + + $userid = $ENV{'REMOTE_USER'}; + $sessionID = $query->cookie("CGISESSID"); + + if ( $sessionID && $userid ) { + my $s = get_session($sessionID); + if ( $s->param('sessiontype') eq 'anon' ) { + undef $sessionID; # remove anonymous session if we have SAML user + } + } + +# ($userid,$sessionID) = () if $userid eq '_everyone'; + return clear_saml($query) if $userid && $userid eq '_everyone'; + + if ( ! $sessionID && $userid ) { # anonymous SAML user + warn "# userid: $userid"; + + # create new user from SAML data + if ( my $token = $query->cookie('AuthMemCookie') ) { + + my $memd = new Cache::Memcached { 'servers' => [ '127.0.0.1:11211' ], 'compress_threshold' => 10_000 }; + if ( my $data = $memd->get($token) ) { + + my $saml; + foreach ( split(/[\n\r]+/,$data) ) { + my ($n,$v) = split /=/, $_; + $saml->{$n} = $v; + } + + my $categorycode = + $saml->{ATTR_code} =~ m/^\d{10}$/ ? 'S' : # JMBAG + $saml->{ATTR_code} =~ m/^\w\w\d+/ ? 'D' : + 'O'; + + my $cardnumber = $categorycode . $saml->{ATTR_code}; + + if ( my $borrowernumber = getborrowernumber($saml->{ATTR_nick}) ) { + warn "SAML login OK $borrowernumber using ATTR_nick: ", $saml->{ATTR_nick}; + } elsif ( $borrowernumber = getborrowernumber( $cardnumber ) ) { + warn "SAML login OK $borrowernumber using cardnumber: $cardnumber update userid: $userid"; + my $sth = $dbh->prepare(qq{ update borrowers set userid = ? where userid = cardnumber and cardnumber = ? }); + $sth->execute( $userid, $cardnumber ); + } else { + my $borrower = { + cardnumber => $cardnumber, + categorycode => $categorycode, + + userid => $saml->{ATTR_nick}, + firstname => $saml->{ATTR_first_name}, + surname => $saml->{ATTR_last_name}, + branchcode => 'SRE', # FIXME + email => $saml->{ATTR_email}, + dateexpiry => '2020-12-13', + password => $token, # required so AddMember won't erase userid + }; + + AddMember( %$borrower ); + + warn "ADDED $data"; + + } + + # Create session for SAML user + + my $sql = qq{ + SELECT + borrowernumber as number, + userid as id, + cardnumber, + firstname, + surname, + borrowers.branchcode as branch, + branches.branchname as branchname, + flags, + email as emailaddress + FROM borrowers + LEFT JOIN branches on borrowers.branchcode=branches.branchcode + where userid=? + }; + my $sth = $dbh->prepare($sql); + $sth->execute( $userid ); + die "can't find $userid" unless $sth->rows; + + my $session = get_session('') or die "can't create session"; + my $sessionID = $session->id; + C4::Context->_new_userenv($sessionID); + $cookie = $query->cookie(CGISESSID => $sessionID); + + my $row = $sth->fetchrow_hashref; + + $session->param( $_ => $row->{$_} ) foreach keys %$row; + + $session->param('ip', $ENV{'REMOTE_ADDR'}); + $session->param('lasttime',time()); + + $session->param('AuthMemCookie', $token); + + 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') + ); + +=for removed + my $row_count = 10; # FIXME:This probably should be a syspref + my ($total, $totshelves, $barshelves, $pubshelves); + ($barshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(1, $row_count, $session->param('number')); + $total->{'bartotal'} = $totshelves; + ($pubshelves, $totshelves) = C4::VirtualShelves::GetRecentShelves(2, $row_count, undef); + $total->{'pubtotal'} = $totshelves; + $session->param('barshelves', $barshelves); + $session->param('pubshelves', $pubshelves); + $session->param('totshelves', $total); + + C4::Context::set_shelves_userenv('bar',$barshelves); + C4::Context::set_shelves_userenv('pub',$pubshelves); + C4::Context::set_shelves_userenv('tot',$total); +=cut + + $loggedin = 1; + + if ( $type eq 'opac' ) { + # FIXME 2011-12-20 dpavlin -- redirect logged in users to http + print $query->redirect( -uri => 'http://' . $query->virtual_host, -status => 302, -cookie => $cookie ); +# exit; + warn "XXX redirect to ", $query->virtual_host; + } + + } else { + die "Can't find SAML token $token for user $userid\n"; + } + } else { + die "Can't find SAML token for user $userid\n"; + } + + } # XXX SAML anon user + elsif ( $persona ){ # we dont want to set a session because we are being called by a persona callback } @@ -708,6 +862,12 @@ sub checkauth { $lasttime = $session->param('lasttime'); $userid = $s_userid; $sessiontype = $session->param('sessiontype') || ''; + + # XXX dpavlin - SAML + # http://groups.google.com/group/simplesamlphp/browse_thread/thread/a1603f9cff5007e1/bfc487ee88981ac0 +# print $query->redirect('https://lib.fer.hr/simplesaml/saml2/sp/initSLO.php?RelayState=http://lib.fer.hr'); +# exit; + } if ( ( $query->param('koha_login_context') && ($q_userid ne $s_userid) ) || ( $cas && $query->param('ticket') ) ) { @@ -722,6 +882,13 @@ sub checkauth { $userid = undef; } elsif ($logout) { + if ( my $token = $query->cookie('AuthMemCookie') ) { + my $memd = new Cache::Memcached { 'servers' => [ '127.0.0.1:11211' ], 'compress_threshold' => 10_000 }; + #$memd->set( $token => '' ); # invalidate AuthMemCookie and leave anonymous user logged in + $memd->delete( $token ); + warn "# nuked $token from memcache"; + } + # voluntary logout the user $session->delete(); $session->flush; @@ -730,6 +897,8 @@ sub checkauth { $sessionID = undef; $userid = undef; + return clear_saml($query); # FIXME + if ($cas and $caslogout) { logout_cas($query); } -- 2.11.0