SAML auth for phpSimpleSAML
authorDobrica Pavlinusic <dpavlin@rot13.org>
Sat, 13 Jul 2013 16:33:34 +0000 (18:33 +0200)
committerDobrica Pavlinusic <dpavlin@rot13.org>
Wed, 16 Jul 2014 12:17:27 +0000 (14:17 +0200)
C4/Auth.pm

index 1bbc0bd..8e3553a 100644 (file)
@@ -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);
         }