Bug 17600: Standardize our EXPORT_OK
[srvgit] / C4 / Auth_with_cas.pm
index e14c6e6..8c922ec 100644 (file)
@@ -20,22 +20,20 @@ package C4::Auth_with_cas;
 use strict;
 use warnings;
 
-use C4::Debug;
 use C4::Context;
-use Koha::AuthUtils qw(get_script_name);
+use Koha::AuthUtils qw( get_script_name );
 use Authen::CAS::Client;
 use CGI qw ( -utf8 );
-use FindBin;
-use YAML;
+use YAML::XS;
 
+use Koha::Logger;
 
-use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $debug);
+our (@ISA, @EXPORT_OK);
 
 BEGIN {
-       require Exporter;
-       $debug = $ENV{DEBUG};
-       @ISA    = qw(Exporter);
-       @EXPORT = qw(check_api_auth_cas checkpw_cas login_cas logout_cas login_cas_url);
+    require Exporter;
+    @ISA   = qw(Exporter);
+    @EXPORT_OK = qw(check_api_auth_cas checkpw_cas login_cas logout_cas login_cas_url logout_if_required);
 }
 my $defaultcasserver;
 my $casservers;
@@ -44,7 +42,7 @@ my $yamlauthfile = C4::Context->config('intranetdir') . "/C4/Auth_cas_servers.ya
 
 # If there's a configuration for multiple cas servers, then we get it
 if (multipleAuth()) {
-    ($defaultcasserver, $casservers) = YAML::LoadFile($yamlauthfile);
+    ($defaultcasserver, $casservers) = YAML::XS::LoadFile($yamlauthfile);
     $defaultcasserver = $defaultcasserver->{'default'};
 } else {
 # Else, we fall back to casServerUrl syspref
@@ -52,6 +50,10 @@ if (multipleAuth()) {
     $casservers = { 'default' => C4::Context->preference('casServerUrl') };
 }
 
+=head1 Subroutines
+
+=cut
+
 # Is there a configuration file for multiple cas servers?
 sub multipleAuth {
     return (-e qq($yamlauthfile));
@@ -64,12 +66,21 @@ sub getMultipleAuth {
 
 # Logout from CAS
 sub logout_cas {
-    my ($query, $type) = @_;
-    my ( $cas, $uri ) = _get_cas_and_service($query, undef, $type);
-    $uri =~ s/\?logout\.x=1//; # We don't want to keep triggering a logout, if we got here, the borrower is already logged out of Koha
-    print $query->redirect( $cas->logout_url(url => $uri));
+    my ( $query, $type ) = @_;
+    my ( $cas,   $uri )  = _get_cas_and_service( $query, undef, $type );
+
+    # We don't want to keep triggering a logout, if we got here,
+    # the borrower is already logged out of Koha
+    $uri =~ s/\?logout\.x=1//;
+
+    my $logout_url = $cas->logout_url( url => $uri );
+    $logout_url =~ s/url=/service=/
+      if C4::Context->preference('casServerVersion') eq '3';
+
+    print $query->redirect($logout_url);
 }
 
+
 # Login to CAS
 sub login_cas {
     my ($query, $type) = @_;
@@ -87,14 +98,12 @@ sub login_cas_url {
 # Checks for password correctness
 # In our case : is there a ticket, is it valid and does it match one of our users ?
 sub checkpw_cas {
-    $debug and warn "checkpw_cas";
     my ($dbh, $ticket, $query, $type) = @_;
     my $retnumber;
     my ( $cas, $uri ) = _get_cas_and_service($query, undef, $type);
 
     # If we got a ticket
     if ($ticket) {
-        $debug and warn "Got ticket : $ticket";
 
         # We try to validate it
         my $val = $cas->service_validate($uri, $ticket );
@@ -103,30 +112,32 @@ sub checkpw_cas {
         if ( $val->is_success() ) {
 
             my $userid = $val->user();
-            $debug and warn "User CAS authenticated as: $userid";
+
+            # we should store the CAS ticekt too, we need this for single logout https://apereo.github.io/cas/4.2.x/protocol/CAS-Protocol-Specification.html#233-single-logout
 
             # Does it match one of our users ?
             my $sth = $dbh->prepare("select cardnumber from borrowers where userid=?");
             $sth->execute($userid);
             if ( $sth->rows ) {
                 $retnumber = $sth->fetchrow;
-                return ( 1, $retnumber, $userid );
+                return ( 1, $retnumber, $userid, $ticket );
             }
             $sth = $dbh->prepare("select userid from borrowers where cardnumber=?");
             $sth->execute($userid);
             if ( $sth->rows ) {
                 $retnumber = $sth->fetchrow;
-                return ( 1, $retnumber, $userid );
+                return ( 1, $retnumber, $userid, $ticket );
             }
 
             # If we reach this point, then the user is a valid CAS user, but not a Koha user
-            $debug and warn "User $userid is not a valid Koha user";
+            Koha::Logger->get->info("User $userid is not a valid Koha user");
 
         } else {
-            $debug and warn "Problem when validating ticket : $ticket";
-            $debug and warn "Authen::CAS::Client::Response::Error: " . $val->error() if $val->is_error();
-            $debug and warn "Authen::CAS::Client::Response::Failure: " . $val->message() if $val->is_failure();
-            $debug and warn Data::Dumper::Dumper($@) if $val->is_error() or $val->is_failure();
+            my $logger = Koha::Logger->get;
+            $logger->debug("Problem when validating ticket : $ticket");
+            $logger->debug("Authen::CAS::Client::Response::Error: " . $val->error()) if $val->is_error();
+            $logger->debug("Authen::CAS::Client::Response::Failure: " . $val->message()) if $val->is_failure();
+            $logger->debug(Data::Dumper::Dumper($@)) if $val->is_error() or $val->is_failure();
             return 0;
         }
     }
@@ -135,7 +146,6 @@ sub checkpw_cas {
 
 # Proxy CAS auth
 sub check_api_auth_cas {
-    $debug and warn "check_api_auth_cas";
     my ($dbh, $PT, $query, $type) = @_;
     my $retnumber;
     my ( $cas, $uri ) = _get_cas_and_service($query, undef, $type);
@@ -148,32 +158,30 @@ sub check_api_auth_cas {
         if ( $r->is_success ) {
 
             # We've got a username !
-            $debug and warn "User authenticated as: ", $r->user, "\n";
-            $debug and warn "Proxied through:\n";
-            $debug and warn "  $_\n" for $r->proxies;
-
             my $userid = $r->user;
 
+            # we should store the CAS ticket too, we need this for single logout https://apereo.github.io/cas/4.2.x/protocol/CAS-Protocol-Specification.html#233-single-logout
+
             # Does it match one of our users ?
             my $sth = $dbh->prepare("select cardnumber from borrowers where userid=?");
             $sth->execute($userid);
             if ( $sth->rows ) {
                 $retnumber = $sth->fetchrow;
-                return ( 1, $retnumber, $userid );
+                return ( 1, $retnumber, $userid, $PT );
             }
             $sth = $dbh->prepare("select userid from borrowers where cardnumber=?");
             return $r->user;
             $sth->execute($userid);
             if ( $sth->rows ) {
                 $retnumber = $sth->fetchrow;
-                return ( 1, $retnumber, $userid );
+                return ( 1, $retnumber, $userid, $PT );
             }
 
             # If we reach this point, then the user is a valid CAS user, but not a Koha user
-            $debug and warn "User $userid is not a valid Koha user";
+            Koha::Logger->get->info("User $userid is not a valid Koha user");
 
         } else {
-            $debug and warn "Proxy Ticket authentication failed";
+            Koha::Logger->get->debug("Proxy Ticket authentication failed");
             return 0;
         }
     }
@@ -202,9 +210,11 @@ sub _url_with_get_params {
     my $query = shift;
     my $type = shift;
 
-    my $uri_base_part = ($type eq 'opac') ?
-                        C4::Context->preference('OPACBaseURL') . get_script_name() :
-                        C4::Context->preference('staffClientBaseURL');
+    my $uri_base_part =
+      ( $type eq 'opac' )
+      ? C4::Context->preference('OPACBaseURL')
+      : C4::Context->preference('staffClientBaseURL');
+    $uri_base_part .= get_script_name();
 
     my $uri_params_part = '';
     foreach my $param ( $query->url_param() ) {
@@ -222,6 +232,44 @@ sub _url_with_get_params {
     return $uri_base_part . $uri_params_part;
 }
 
+=head2 logout_if_required
+
+    If using CAS, this subroutine will trigger single-signout of the CAS server.
+
+=cut
+
+sub logout_if_required {
+    my ( $query ) = @_;
+    # Check we havent been hit by a logout call
+    my $xml = $query->param('logoutRequest');
+    return 0 unless $xml;
+
+    my $dom = XML::LibXML->load_xml(string => $xml);
+    my $ticket;
+    foreach my $node ($dom->findnodes('/samlp:LogoutRequest')){
+        # We got a cas single logout request from a cas server;
+        $ticket = $node->findvalue('./samlp:SessionIndex');
+    }
+
+    return 0 unless $ticket;
+
+    # We've been called as part of the single logout destroy the session associated with the cas ticket
+    my $params = C4::Auth::_get_session_params();
+    my $success = CGI::Session->find( $params->{dsn}, sub {delete_cas_session(@_, $ticket)}, $params->{dsn_args} );
+
+    print $query->header;
+    exit;
+}
+
+sub delete_cas_session {
+    my $session = shift;
+    my $ticket = shift;
+    if ($session->param('cas_ticket') && $session->param('cas_ticket') eq $ticket ) {
+        $session->delete;
+        $session->flush;
+    }
+}
+
 1;
 __END__