From 9a3950f673c28688a2ead6514c3320e34ec0cc4c Mon Sep 17 00:00:00 2001 From: Chris Cormack Date: Thu, 31 Mar 2011 10:06:46 +1300 Subject: [PATCH] Bug 5630 CAS improvements Squashed commit of the following: commit 0e13a5278e11b288e48190dc26f31e96d06598dd Author: Henri-Damien LAURENT Date: Wed Jan 19 21:24:39 2011 +0100 Bug 5630 : fixing C4/Auth.pm commit b55abc7a0dc1ca43b2610a27246293e9a9346e18 Author: Matthias Meusburger Date: Wed Jan 19 21:24:38 2011 +0100 Bug 5630 : Adds CAS documentation commit df0098a6a65465e6e734f99f65fb453dd3fa11d1 Author: Henri-Damien LAURENT Date: Wed Jan 19 21:24:37 2011 +0100 Bug 5630 : ilsdi service AuthenticatePatron doesn't with CAS syspref on Signed-off-by: Henri-Damien LAURENT commit 31c8f0c0facfafae011ad24c9d458c50f2fad296 Author: Matthias Meusburger Date: Wed Jan 19 21:24:36 2011 +0100 Bug 5630 : Adds the ability to authenticate against multiple CAS servers commit 9d0def826135d5756533dc0dcf8e0a107d1ac8fc Author: Henri-Damien LAURENT Date: Wed Jan 19 21:24:34 2011 +0100 Auth_with_cas : removing a warning $sth was defined twice in a function Removing the second definition commit 5ee550e9a2bb7ab6bc09f14fced6ce0df8011eb0 Author: Matthias Meusburger Date: Wed Jan 19 21:24:33 2011 +0100 Bug 6012 : MT 2270: CAS proxy CAS Proxy Examples included are now really usable Signed-off-by: Chris Cormack Signed-off-by: Paul Poulain Signed-off-by: Chris Cormack --- C4/Auth.pm | 127 +++++++----- C4/Auth_cas_servers.yaml.orig | 12 ++ C4/Auth_with_cas.pm | 181 ++++++++++++++---- docs/CAS/CAS/README | 8 + docs/CAS/CASProxy/README | 9 + docs/CAS/CASProxy/examples/casSession.tmp | Bin 0 -> 134 bytes docs/CAS/CASProxy/examples/koha_webservice.pl | 58 ++++++ docs/CAS/CASProxy/examples/proxy_cas.pl | 91 +++++++++ .../CASProxy/examples/proxy_cas_callback.pl | 62 ++++++ docs/CAS/CASProxy/examples/proxy_cas_data.pl | 80 ++++++++ .../opac-tmpl/prog/en/modules/opac-auth.tt | 14 +- 11 files changed, 550 insertions(+), 92 deletions(-) create mode 100644 C4/Auth_cas_servers.yaml.orig create mode 100644 docs/CAS/CAS/README create mode 100644 docs/CAS/CASProxy/README create mode 100644 docs/CAS/CASProxy/examples/casSession.tmp create mode 100755 docs/CAS/CASProxy/examples/koha_webservice.pl create mode 100755 docs/CAS/CASProxy/examples/proxy_cas.pl create mode 100755 docs/CAS/CASProxy/examples/proxy_cas_callback.pl create mode 100755 docs/CAS/CASProxy/examples/proxy_cas_data.pl diff --git a/C4/Auth.pm b/C4/Auth.pm index 495f04c716..16e908a399 100644 --- a/C4/Auth.pm +++ b/C4/Auth.pm @@ -32,27 +32,34 @@ use C4::Koha; use C4::Branch; # GetBranches use C4::VirtualShelves; use POSIX qw/strftime/; +use List::MoreUtils qw/ any /; # use utf8; use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $debug $ldap $cas $caslogout $servers $memcached); BEGIN { - $VERSION = 3.02; # set version for version checking - $debug = $ENV{DEBUG}; - @ISA = qw(Exporter); - @EXPORT = qw(&checkauth &get_template_and_user &haspermission &get_user_subpermissions); - @EXPORT_OK = qw(&check_api_auth &get_session &check_cookie_auth &checkpw &get_all_subpermissions &get_user_subpermissions); - %EXPORT_TAGS = (EditPermissions => [qw(get_all_subpermissions get_user_subpermissions)]); - $ldap = C4::Context->config('useldapserver') || 0; - $cas = C4::Context->preference('casAuthentication'); - $caslogout = C4::Context->preference('casLogout'); + sub psgi_env { any { /^psgi\./ } keys %ENV } + sub safe_exit { + if ( psgi_env ) { die 'psgi:exit' } + else { exit } + } + + $VERSION = 3.02; # set version for version checking + $debug = $ENV{DEBUG}; + @ISA = qw(Exporter); + @EXPORT = qw(&checkauth &get_template_and_user &haspermission &get_user_subpermissions); + @EXPORT_OK = qw(&check_api_auth &get_session &check_cookie_auth &checkpw &get_all_subpermissions &get_user_subpermissions); + %EXPORT_TAGS = ( EditPermissions => [qw(get_all_subpermissions get_user_subpermissions)] ); + $ldap = C4::Context->config('useldapserver') || 0; + $cas = C4::Context->preference('casAuthentication'); + $caslogout = C4::Context->preference('casLogout'); + require C4::Auth_with_cas; # no import if ($ldap) { - require C4::Auth_with_ldap; # no import - import C4::Auth_with_ldap qw(checkpw_ldap); + require C4::Auth_with_ldap; + # no import import C4::Auth_with_ldap qw(checkpw_ldap); } if ($cas) { - require C4::Auth_with_cas; # no import - import C4::Auth_with_cas qw(checkpw_cas login_cas logout_cas login_cas_url); + import C4::Auth_with_cas qw(check_api_auth_cas checkpw_cas login_cas logout_cas login_cas_url); } $servers = C4::Context->config('memcached_servers'); if ($servers) { @@ -130,8 +137,8 @@ Output.pm module. =cut my $SEARCH_HISTORY_INSERT_SQL =<prepare($SEARCH_HISTORY_INSERT_SQL); - $sth->execute( $borrowernumber, - $in->{'query'}->cookie("CGISESSID"), - $_->{'query_desc'}, - $_->{'query_cgi'}, - $_->{'total'}, - $_->{'time'}, - ) foreach @recentSearches; + + $sth->execute( $borrowernumber, $in->{'query'}->cookie("CGISESSID"), $_->{'query_desc'}, $_->{'query_cgi'}, $_->{'limit_desc'}, $_->{'limit_cgi'}, $_->{'total'}, $_->{'time'}, ) + foreach @recentSearches; # And then, delete the cookie's content my $newsearchcookie = $in->{'query'}->cookie( @@ -572,19 +575,18 @@ sub _version_check ($$) { # and so we must redirect to OPAC maintenance page or to the WebInstaller # also, if OpacMaintenance is ON, OPAC should redirect to maintenance if (C4::Context->preference('OpacMaintenance') && $type eq 'opac') { - warn "OPAC Install required, redirecting to maintenance"; - print $query->redirect("/cgi-bin/koha/maintenance.pl"); - } - unless ($version = C4::Context->preference('Version')) { # assignment, not comparison - if ($type ne 'opac') { - warn "Install required, redirecting to Installer"; - print $query->redirect("/cgi-bin/koha/installer/install.pl"); - } - else { warn "OPAC Install required, redirecting to maintenance"; print $query->redirect("/cgi-bin/koha/maintenance.pl"); - } - exit; + } + unless ( $version = C4::Context->preference('Version') ) { # assignment, not comparison + if ( $type ne 'opac' ) { + warn "Install required, redirecting to Installer"; + print $query->redirect("/cgi-bin/koha/installer/install.pl"); + } else { + warn "OPAC Install required, redirecting to maintenance"; + print $query->redirect("/cgi-bin/koha/maintenance.pl"); + } + safe_exit; } # check that database and koha version are the same @@ -604,7 +606,7 @@ sub _version_check ($$) { warn sprintf("OPAC: " . $warning, 'maintenance'); print $query->redirect("/cgi-bin/koha/maintenance.pl"); } - exit; + safe_exit; } } @@ -639,6 +641,10 @@ sub checkauth { my ( $userid, $cookie, $sessionID, $flags, $barshelves, $pubshelves ); my $logout = $query->param('logout.x'); + # This parameter is the name of the CAS server we want to authenticate against, + # when using authentication against multiple CAS servers, as configured in Auth_cas_servers.yaml + my $casparam = $query->param('cas'); + if ( $userid = $ENV{'REMOTE_USER'} ) { # Using Basic Authentication, no cookies required $cookie = $query->cookie( @@ -986,11 +992,28 @@ sub checkauth { $template->param( OpacPublic => C4::Context->preference("OpacPublic")); $template->param( loginprompt => 1 ) unless $info{'nopermission'}; - if ($cas) { + if ($cas) { + + # Is authentication against multiple CAS servers enabled? + if (C4::Auth_with_cas::multipleAuth && !$casparam) { + my $casservers = C4::Auth_with_cas::getMultipleAuth(); + my @tmplservers; + foreach my $key (keys %$casservers) { + push @tmplservers, {name => $key, value => login_cas_url($query, $key) . "?cas=$key" }; + } + #warn Data::Dumper::Dumper(\@tmplservers); + $template->param( + casServersLoop => \@tmplservers + ); + } else { + $template->param( + casServerUrl => login_cas_url($query), + ); + } + $template->param( - casServerUrl => login_cas_url(), - invalidCasLogin => $info{'invalidCasLogin'} - ); + invalidCasLogin => $info{'invalidCasLogin'} + ); } my $self_url = $query->url( -absolute => 1 ); @@ -1007,7 +1030,7 @@ sub checkauth { -cookie => $cookie ), $template->output; - exit; + safe_exit; } =head2 check_api_auth @@ -1080,7 +1103,7 @@ sub check_api_auth { unless ($query->param('userid')) { $sessionID = $query->cookie("CGISESSID"); } - if ($sessionID) { + if ($sessionID && not $cas) { my $session = get_session($sessionID); C4::Context->_new_userenv($sessionID); if ($session) { @@ -1130,18 +1153,24 @@ sub check_api_auth { # 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); - if ($cas && $query->param('ticket')) { + my ($return, $cardnumber); + + # Proxy CAS auth + if ($cas && $query->param('PT')) { my $retuserid; - ( $return, $cardnumber, $retuserid ) = checkpw( $dbh, $userid, $password, $query ); - $userid = $retuserid; + $debug and print STDERR "## check_api_auth - checking CAS\n"; + # In case of a CAS authentication, we use the ticket instead of the password + my $PT = $query->param('PT'); + ($return,$cardnumber,$userid) = check_api_auth_cas($dbh, $PT, $query); # EXTERNAL AUTH } else { + # User / password auth + unless ($userid and $password) { + # caller did something wrong, fail the authenticateion + return ("failed", undef, undef); + } ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password, $query ); } + if ($return and haspermission( $userid, $flagsrequired)) { my $session = get_session(""); return ("failed", undef, undef) unless $session; @@ -1398,7 +1427,7 @@ sub checkpw { ($retval) and return ($retval,$retcard); } - if ($cas && $query->param('ticket')) { + if ($cas && $query && $query->param('ticket')) { $debug and print STDERR "## checkpw - checking CAS\n"; # In case of a CAS authentication, we use the ticket instead of the password my $ticket = $query->param('ticket'); diff --git a/C4/Auth_cas_servers.yaml.orig b/C4/Auth_cas_servers.yaml.orig new file mode 100644 index 0000000000..8ad171e0c9 --- /dev/null +++ b/C4/Auth_cas_servers.yaml.orig @@ -0,0 +1,12 @@ +# This file is used for authenticating against multiple CAS servers +# If the file Auth_cas_servers.yaml exists, then the casServerUrl syspref will be ignored + +# If you have to authenticate against only one CAS server, which is usually the case, +# don't use this file, but the casServerUrl syspref instead + +default: ServerName +--- + ServerName: "https://example.com/cas" + OtherServerName: "https://example.org/cas" + ThirdServerName: "https://example.edu/cas" + diff --git a/C4/Auth_with_cas.pm b/C4/Auth_with_cas.pm index f9a03a3f17..b09623295c 100644 --- a/C4/Auth_with_cas.pm +++ b/C4/Auth_with_cas.pm @@ -25,6 +25,7 @@ use C4::Context; use C4::Utils qw( :all ); use Authen::CAS::Client; use CGI; +use FindBin; use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $debug); @@ -34,31 +35,70 @@ BEGIN { $VERSION = 3.03; # set the version for version checking $debug = $ENV{DEBUG}; @ISA = qw(Exporter); - @EXPORT = qw(checkpw_cas login_cas logout_cas login_cas_url); + @EXPORT = qw(check_api_auth_cas checkpw_cas login_cas logout_cas login_cas_url); +} +my $context = C4::Context->new() or die 'C4::Context->new failed'; +my $defaultcasserver; +my $casservers; +my $yamlauthfile = "../C4/Auth_cas_servers.yaml"; + + +# If there's a configuration for multiple cas servers, then we get it +if (multipleAuth()) { + ($defaultcasserver, $casservers) = YAML::LoadFile(qq($FindBin::Bin/$yamlauthfile)); + $defaultcasserver = $defaultcasserver->{'default'}; +} else { +# Else, we fall back to casServerUrl syspref + $defaultcasserver = 'default'; + $casservers = { 'default' => C4::Context->preference('casServerUrl') }; } +# Is there a configuration file for multiple cas servers? +sub multipleAuth { + return (-e qq($FindBin::Bin/$yamlauthfile)); +} -my $context = C4::Context->new() or die 'C4::Context->new failed'; -my $casserver = C4::Context->preference('casServerUrl'); +# Returns configured CAS servers' list if multiple authentication is enabled +sub getMultipleAuth { + return $casservers; +} # Logout from CAS sub logout_cas { my ($query) = @_; - my $cas = Authen::CAS::Client->new($casserver); - print $query->redirect($cas->logout_url(url => $ENV{'SCRIPT_URI'})); + my $uri = $ENV{'SCRIPT_URI'}; + my $casparam = $query->param('cas'); + # FIXME: This should be more generic and handle whatever parameters there might be + $uri .= "?cas=" . $casparam if (defined $casparam); + $casparam = $defaultcasserver if (not defined $casparam); + my $cas = Authen::CAS::Client->new($casservers->{$casparam}); + print $query->redirect( $cas->logout_url($uri)); } # Login to CAS sub login_cas { my ($query) = @_; - my $cas = Authen::CAS::Client->new($casserver); - print $query->redirect($cas->login_url($ENV{'SCRIPT_URI'})); + my $uri = $ENV{'SCRIPT_URI'}; + my $casparam = $query->param('cas'); + # FIXME: This should be more generic and handle whatever parameters there might be + $uri .= "?cas=" . $casparam if (defined $casparam); + $casparam = $defaultcasserver if (not defined $casparam); + my $cas = Authen::CAS::Client->new($casservers->{$casparam}); + print $query->redirect( $cas->login_url($uri)); } # Returns CAS login URL with callback to the requesting URL sub login_cas_url { - my $cas = Authen::CAS::Client->new($casserver); - return $cas->login_url($ENV{'SCRIPT_URI'}); + + my ($query, $key) = @_; + my $uri = $ENV{'SCRIPT_URI'}; + my $casparam = $query->param('cas'); + # FIXME: This should be more generic and handle whatever parameters there might be + $uri .= "?cas=" . $casparam if (defined $casparam); + $casparam = $defaultcasserver if (not defined $casparam); + $casparam = $key if (defined $key); + my $cas = Authen::CAS::Client->new($casservers->{$casparam}); + return $cas->login_url($uri); } # Checks for password correctness @@ -67,46 +107,103 @@ sub checkpw_cas { $debug and warn "checkpw_cas"; my ($dbh, $ticket, $query) = @_; my $retnumber; - my $cas = Authen::CAS::Client->new($casserver); + my $uri = $ENV{'SCRIPT_URI'}; + my $casparam = $query->param('cas'); + # FIXME: This should be more generic and handle whatever parameters there might be + $uri .= "?cas=" . $casparam if (defined $casparam); + $casparam = $defaultcasserver if (not defined $casparam); + my $cas = Authen::CAS::Client->new($casservers->{$casparam}); # If we got a ticket if ($ticket) { - $debug and warn "Got ticket : $ticket"; - - # We try to validate it - my $val = $cas->service_validate($ENV{'SCRIPT_URI'}, $ticket); - - # If it's valid - if( $val->is_success() ) { - - my $userid = $val->user(); - $debug and warn "User CAS authenticated as: $userid"; - - # 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); - } - $sth = $dbh->prepare("select userid from borrowers where cardnumber=?"); - $sth->execute($userid); - if ( $sth->rows ) { - $retnumber = $sth->fetchrow; - return (1, $retnumber, $userid); - } - - # 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"; - - } else { - $debug and warn "Invalid session ticket : $ticket"; - return 0; - } + $debug and warn "Got ticket : $ticket"; + + # We try to validate it + my $val = $cas->service_validate($uri, $ticket ); + + # If it's valid + if ( $val->is_success() ) { + + my $userid = $val->user(); + $debug and warn "User CAS authenticated as: $userid"; + + # 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 ); + } + $sth = $dbh->prepare("select userid from borrowers where cardnumber=?"); + $sth->execute($userid); + if ( $sth->rows ) { + $retnumber = $sth->fetchrow; + return ( 1, $retnumber, $userid ); + } + + # 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"; + + } else { + $debug and warn "Invalid session ticket : $ticket"; + return 0; + } + } + return 0; +} + +# Proxy CAS auth +sub check_api_auth_cas { + $debug and warn "check_api_auth_cas"; + my ($dbh, $PT, $query) = @_; + my $retnumber; + my $url = $query->url(); + + my $casparam = $query->param('cas'); + $casparam = $defaultcasserver if (not defined $casparam); + my $cas = Authen::CAS::Client->new($casservers->{$casparam}); + + # If we have a Proxy Ticket + if ($PT) { + my $r = $cas->proxy_validate( $url, $PT ); + + # If the PT is valid + 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; + + # 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 ); + } + $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 ); + } + + # 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"; + + } else { + $debug and warn "Proxy Ticket authentication failed"; + return 0; + } } return 0; } + 1; __END__ diff --git a/docs/CAS/CAS/README b/docs/CAS/CAS/README new file mode 100644 index 0000000000..503692ec4a --- /dev/null +++ b/docs/CAS/CAS/README @@ -0,0 +1,8 @@ +CAS authentication is available through CasAuthentication and casServerUrl sysprefs. + +However, if you plan to use multi-CAS (ie: authentication against multiple CAS server, +you'll have to use C4/Auth_cas_servers.yaml. + +Anyway, please keep in mind that if you want to authenticate through https against the +CAS serveur, you have to install Crypt::SSLeay package, or the authentication will +silently fail. diff --git a/docs/CAS/CASProxy/README b/docs/CAS/CASProxy/README new file mode 100644 index 0000000000..b1c882cd4c --- /dev/null +++ b/docs/CAS/CASProxy/README @@ -0,0 +1,9 @@ +As CAS Proxying is not an obvious authentication to set up, here are +some documented examples showing how a foreign application can query +koha webservices, being CAS authenticated. + +The starting point is proxy_cas.pl + +To find more about how CAS Proxy works : +http://afs.berkeley.edu/~lr/presentations/cas-auth/ +http://www.ja-sig.org/wiki/display/CAS/Proxy+CAS+Walkthrough diff --git a/docs/CAS/CASProxy/examples/casSession.tmp b/docs/CAS/CASProxy/examples/casSession.tmp new file mode 100644 index 0000000000000000000000000000000000000000..ca78c338972f592560a68146fc7c534e3ebca685 GIT binary patch literal 134 zcmXRYE-_$bXJ%kvVC1q4a1YTnwA78Ts7!To^a=9!DoZId3JUY_NzO6IG&L^>H#V&Z zb1@0^H8ApwvIz1|E;KevODZ!p0BW}ZYWDOG)it!xO{($^NzODch|0@z^Y#c2u?TPv jjVgCbO06i2NX<*JOiCVF_e=Av3dsWinw}@# literal 0 HcmV?d00001 diff --git a/docs/CAS/CASProxy/examples/koha_webservice.pl b/docs/CAS/CASProxy/examples/koha_webservice.pl new file mode 100755 index 0000000000..cb161f75ea --- /dev/null +++ b/docs/CAS/CASProxy/examples/koha_webservice.pl @@ -0,0 +1,58 @@ +#!/usr/bin/perl + +# Copyright 2009 SARL BibLibre +# +# 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 + +=head1 DESCRIPTION + +# Here is an exemple of a simple phony webservice, returning "Hello World" if the user is authenticated +# The purpose is to show how CAS Proxy can work with koha +# In this configuration, this page acts as a CAS Client, instead of the user's browser. +# This page is meant to be called from a foreign application + +=head1 CGI PARAMETERS + +=item PT +The Proxy Ticket, needed for check_api_auth, that will try to make the CAS Server validate it. + +=cut + +use utf8; +use strict; +use warnings; +binmode(STDOUT, ":utf8"); + +use C4::Auth qw(check_api_auth); +use C4::Output; +use C4::Context; +use CGI; + +my $cgi = new CGI; + +print CGI::header('-type'=>'text/plain', '-charset'=>'utf-8'); + +# The authentication : if $cgi contains a PT parameter, and CAS is enabled (casAuthentication syspref), +# a CAS Proxy authentication will take place +my ( $status, $cookie_, $sessionID ) = check_api_auth( $cgi, {circulate => 'override_renewals'}); + +if ($status ne 'ok') { + print "Authentication failed : $status"; +} else { + print "Hello World!"; +} +exit 0; + diff --git a/docs/CAS/CASProxy/examples/proxy_cas.pl b/docs/CAS/CASProxy/examples/proxy_cas.pl new file mode 100755 index 0000000000..85085d7d1f --- /dev/null +++ b/docs/CAS/CASProxy/examples/proxy_cas.pl @@ -0,0 +1,91 @@ +#!/usr/bin/perl + +# Copyright 2009 SARL BibLibre +# +# 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 + +=head1 DESCRIPTION + +# Here is an exemple of a CAS Proxy +# The proxy is a foreign application that will authenticate the user against CAS +# Once authenticated as a proxy, the foreign application will be able to call some +# Koha webservices, proving authentication only by giving a proxy ticket + +# Note: please keep in mind that all url's must be https and their certificates must be trusted + +=cut + +use strict; +use warnings; +use CGI; +use Authen::CAS::Client; + +# URL Of the CAS Server +my $casServerUrl = 'https://localhost:8443/cas/'; +my $cas = Authen::CAS::Client->new($casServerUrl); +my $cgi = new CGI; + +# URL of the service we're requesting a Service Ticket for (typically this very same page) +my $proxy_service = $cgi->url; + + +# Callback URL (this is an URL the CAS Server will query, providing the Proxy Ticket we'll need +# to query the koha webservice). It can be this page or another. In this example, another page will be +# called back +my $pgtUrl = "https://.../proxy_cas_callback.pl"; + +print $cgi->header({-type => 'text/html'}); +print $cgi->start_html("proxy cas"); + +# If we already have a service ticket +if ($cgi->param('ticket')) { + + print "Got a ticket :" . $cgi->param('ticket') . "
\n"; + + # We validate it against the CAS Server, providing the callback URL + my $r = $cas->service_validate( $proxy_service, $cgi->param('ticket'), pgtUrl => $pgtUrl); + + # If it is sucessful, we are authenticated + if( $r->is_success() ) { + print "User authenticated as: ", $r->user(), "
\n"; + } else { + print "User authentication failed
\n"; + } + + # If we have a PGTIou ticket, the proxy validation was sucessful + if (defined $r->iou) { + print "Proxy granting ticket IOU: ", $r->iou, "
\n"; + my $pgtIou = $r->iou; + + print 'Next'; + + + + } else { + print "Service validation for proxying failed\n"; + } + +# If we don't have a Service Ticket, we ask for one (ie : the user will be redirected to the CAS Server for authentication) +} else { + + my $url = $cas->login_url($proxy_service); + print "Please log in"; +} + +print $cgi->end_html; + + + diff --git a/docs/CAS/CASProxy/examples/proxy_cas_callback.pl b/docs/CAS/CASProxy/examples/proxy_cas_callback.pl new file mode 100755 index 0000000000..3c2c9efa40 --- /dev/null +++ b/docs/CAS/CASProxy/examples/proxy_cas_callback.pl @@ -0,0 +1,62 @@ +#!/usr/bin/perl + +# Copyright 2009 SARL BibLibre +# +# 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 + +=head1 DESCRIPTION + +# Here is an exemple of a callback page for a CAS Proxy +# This is the page the CAS server will call back with a Proxy Ticket, allowing us (the foreign application) +# to query koha webservices, being CAS authenticated + +=cut + +use strict; +use warnings; +use CGI; +use Authen::CAS::Client; +use Storable qw(nstore_fd); + +my $casServerUrl = 'https://localhost:8443/cas/'; +my $cas = Authen::CAS::Client->new($casServerUrl); + +my $cgi = new CGI; + +my $proxy_service = $cgi->url; + +print $cgi->header({-type => 'text/html'}); +print $cgi->start_html("proxy cas callback"); + +# If we have a pgtId, it means the cas server called us back +if ($cgi->param('pgtId')) { + warn "Got a pgtId :" . $cgi->param('pgtId'); + warn "Got a pgtIou :" . $cgi->param('pgtIou'); + my $pgtIou = $cgi->param('pgtIou'); + my $pgtId = $cgi->param('pgtId'); + + # Now we store the pgtIou and the pgtId in the application vars (in our case a storable object in a file), + # so that the page requesting the webservice can retrieve the pgtId matching it's PgtIou + open FILE, ">", "casSession.tmp" or die "Unable to open file"; + nstore_fd({$pgtIou => $pgtId}, \*FILE); + close FILE; + +} else { + warn "Failed to get a Proxy Ticket\n"; +} + +print $cgi->end_html; + diff --git a/docs/CAS/CASProxy/examples/proxy_cas_data.pl b/docs/CAS/CASProxy/examples/proxy_cas_data.pl new file mode 100755 index 0000000000..92c61a5392 --- /dev/null +++ b/docs/CAS/CASProxy/examples/proxy_cas_data.pl @@ -0,0 +1,80 @@ +#!/usr/bin/perl + +# Copyright 2009 SARL BibLibre +# +# 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 + +=head1 DESCRIPTION + +# This page will display the result of the call to the koha webservice + +=head1 CGI PARAMETERS + +=item PGTIOU + +The Proxy Granting Ticket IOU the CAS Server returned to us when we gave him the Service Ticket +This PGTIOU will allow us to retrive the matching PGTID + +=cut + +use strict; +use warnings; +use CGI; +use Authen::CAS::Client; +use Storable qw(fd_retrieve); +use LWP::Simple; +use URI::Escape; + +my $casServerUrl = 'https://localhost:8443/cas/'; +my $cas = Authen::CAS::Client->new($casServerUrl); + +# URL of the service we'd like to be proxy for (typically the Koha webservice we want to query) +my $target_service = "https://.../koha_webservice.pl"; + +my $cgi = new CGI; + +print $cgi->header({-type => 'text/html'}); +print $cgi->start_html("proxy cas"); + + +if ($cgi->param('PGTIOU')) { + + # At this point, we must retrieve the PgtId by matching the PgtIou we + # just received and the PgtIou given by the CAS Server to the callback URL + # The callback page stored it in the application vars (in our case a storable object in a file) + open FILE, "casSession.tmp" or die "Unable to open file"; + my $hashref = fd_retrieve(\*FILE); + my $pgtId = %$hashref->{$cgi->param('PGTIOU')}; + close FILE; + + # Now that we have a PgtId, we can ask the cas server for a proxy ticket... + my $rp = $cas->proxy( $pgtId, $target_service ); + if( $rp->is_success ) { + print "Proxy Ticket issued: ", $rp->proxy_ticket, "
\n"; + + # ...which we will provide to the target service (the koha webservice) for authentication ! + my $data = get($target_service . "?PT=" . $rp->proxy_ticket); + + # And finally, we can display the data gathered from the koha webservice ! + print "This is the output of the koha webservice we just queried, CAS authenticated :
"; + print "$data"; + + } else { + print "Cannot get Proxy Ticket"; + } + + +} diff --git a/koha-tmpl/opac-tmpl/prog/en/modules/opac-auth.tt b/koha-tmpl/opac-tmpl/prog/en/modules/opac-auth.tt index dfa7ed90a8..febe4d27ae 100644 --- a/koha-tmpl/opac-tmpl/prog/en/modules/opac-auth.tt +++ b/koha-tmpl/opac-tmpl/prog/en/modules/opac-auth.tt @@ -53,7 +53,19 @@

Sorry, the CAS login failed.

[% END %] -

If you have a CAS account, please click here to login.

+

If you have a CAS account, +[% IF ( casServerUrl ) %] + please click here to login.

+[% END %] + +[% IF ( casServersLoop ) %] +please choose against which one you would like to authenticate:

+

Local Login

If you do not have a CAS account, but a local account, you can still log in :

-- 2.20.1