From 11afc4ec96e996dd1fbfe15a77589a59ccf858fe Mon Sep 17 00:00:00 2001 From: Martin Renvoize Date: Wed, 11 Mar 2026 10:05:52 +0000 Subject: [PATCH] Bug 40596: Update C4::Auth to use CAS identity providers Removes the syspref-driven $cas/$caslogout module globals and the conditional loading of C4::Auth_with_cas from the BEGIN block. C4::Auth_with_cas is now imported unconditionally. CAS presence is detected at runtime from the identity_providers table rather than from the casAuthentication system preference: - The casAuthentication template variable is now set from a live DB count of enabled CAS providers. - Login-page CAS URL rendering iterates over identity provider codes instead of Auth_cas_servers.yaml keys; the old ?cas= query parameter is replaced by the cas_provider= parameter embedded in the service URL. - The ticket-validation condition in checkauth and checkpw checks for the cas_provider CGI parameter rather than the $cas global. - cas_provider is stored in the user session alongside cas_ticket so logout_cas receives the correct provider code on sign-out. - The forced-SSO redirect for CAS providers uses login_cas_url() directly with the provider's code. - Proxy-ticket (PT) handling in check_api_auth no longer requires the casAuthentication syspref to be enabled. --- C4/Auth.pm | 64 +++++++++++++++++++++++++----------------------------- 1 file changed, 29 insertions(+), 35 deletions(-) diff --git a/C4/Auth.pm b/C4/Auth.pm index 78152373569..f21fa8dcd32 100644 --- a/C4/Auth.pm +++ b/C4/Auth.pm @@ -58,6 +58,7 @@ use Koha::Patron::Consents; use List::MoreUtils qw( any ); use Encode; use C4::Auth_with_shibboleth qw( shib_ok get_login_shib login_shib_url logout_shib checkpw_shib ); +use Koha::Auth::Identity::Providers; use Net::CIDR; use C4::Log qw( logaction ); use Koha::CookieManager; @@ -68,20 +69,13 @@ use Koha::Session; # use utf8; -use vars qw($ldap $cas $caslogout); +use vars qw($ldap); + +use C4::Auth_with_cas + qw(check_api_auth_cas checkpw_cas login_cas logout_cas login_cas_url logout_if_required multipleAuth getMultipleAuth); BEGIN { C4::Context->set_remote_address; - - $cas = C4::Context->preference('casAuthentication'); - $caslogout = C4::Context->preference('casLogout'); - - if ($cas) { - require C4::Auth_with_cas; # no import - import C4::Auth_with_cas - qw(check_api_auth_cas checkpw_cas login_cas logout_cas login_cas_url logout_if_required multipleAuth getMultipleAuth); - } - } =head1 NAME @@ -943,7 +937,7 @@ sub checkauth { ); if ( ( $query->param('koha_login_context') && ( $q_userid ne $userid ) ) - || ( $cas && $query->param('ticket') && !C4::Context->userenv->{'id'} ) + || ( $query->param('cas_provider') && $query->param('ticket') && !C4::Context->userenv->{'id'} ) || ( $shib && $shib_login && !$logout && !C4::Context->userenv->{'id'} ) ) { @@ -1000,7 +994,8 @@ sub checkauth { # voluntary logout the user # check whether the user was using their shibboleth session or a local one - my $shibSuccess = C4::Context->userenv ? C4::Context->userenv->{'shibboleth'} : undef; + my $shibSuccess = C4::Context->userenv ? C4::Context->userenv->{'shibboleth'} : undef; + my $cas_provider_code = $session ? $session->param('cas_provider') : undef; if ($session) { $session->delete(); $session->flush; @@ -1008,8 +1003,8 @@ sub checkauth { C4::Context::unset_userenv(); $cookie = $cookie_mgr->clear_unless( $query->cookie, @$cookie ); - if ( $cas and $caslogout ) { - logout_cas( $query, $type ); + if ( C4::Context->preference('casLogout') && $cas_provider_code ) { + logout_cas( $query, $type, $cas_provider_code ); } # If we are in a shibboleth session (shibboleth is enabled, a shibboleth match attribute is set and matches koha matchpoint) @@ -1058,7 +1053,7 @@ sub checkauth { print STDERR "ERROR: Missing system preference AllowPKIAuth.\n"; $pki_field = 'None'; } - if ( ( $cas && $query->param('ticket') ) + if ( ( $query->param('ticket') && $query->param('cas_provider') ) || $q_userid || ( $shib && $shib_login ) || $pki_field ne 'None' @@ -1081,7 +1076,7 @@ sub checkauth { # If shib login and match were successful, skip further login methods unless ($shibSuccess) { - if ( $cas && $query->param('ticket') ) { + if ( $query->param('ticket') && $query->param('cas_provider') ) { my $retuserid; my $patron; ( $return, $cardnumber, $retuserid, $patron, $cas_ticket ) = @@ -1320,7 +1315,8 @@ sub checkauth { $session->param( 'register_name', $register_name ); $session->param( 'sco_user', $is_sco_user ); } - $session->param( 'cas_ticket', $cas_ticket ) if $cas_ticket; + $session->param( 'cas_ticket', $cas_ticket ) if $cas_ticket; + $session->param( 'cas_provider', $query->param('cas_provider') ) if $cas_ticket; C4::Context->set_userenv_from_session($session); } @@ -1453,10 +1449,10 @@ sub checkauth { my $template = C4::Templates::gettemplate( $auth_template_name, $type, $query ); $template->param( - login => 1, - INPUTS => \@inputs, - script_name => get_script_name(), - casAuthentication => C4::Context->preference("casAuthentication"), + login => 1, + INPUTS => \@inputs, + script_name => get_script_name(), + casAuthentication => Koha::Auth::Identity::Providers->search( { protocol => 'CAS', enabled => 1 } )->count, shibbolethAuthentication => $shib, suggestion => C4::Context->preference("suggestion"), virtualshelves => C4::Context->preference("virtualshelves"), @@ -1525,7 +1521,9 @@ sub checkauth { if ($forced_provider) { my $redirect_url; - if ( $forced_provider->protocol eq 'SAML2' ) { + if ( $forced_provider->protocol eq 'CAS' ) { + $redirect_url = login_cas_url( $query, $forced_provider->code, $type ); + } elsif ( $forced_provider->protocol eq 'SAML2' ) { $redirect_url = login_shib_url($query); } elsif ( $forced_provider->protocol eq 'OIDC' || $forced_provider->protocol eq 'OAuth' ) { my $base = ( $type eq 'opac' ) ? "/api/v1/public/oauth/login" : "/api/v1/oauth/login"; @@ -1537,23 +1535,19 @@ sub checkauth { } } - if ($cas) { - - # Is authentication against multiple CAS servers enabled? - require C4::Auth_with_cas; - if ( multipleAuth() && !$casparam ) { + if ( Koha::Auth::Identity::Providers->search( { protocol => 'CAS', enabled => 1 } )->count ) { + if ( multipleAuth() && !$query->param('cas_provider') ) { my $casservers = getMultipleAuth(); my @tmplservers; - foreach my $key ( keys %$casservers ) { - push @tmplservers, { name => $key, value => login_cas_url( $query, $key, $type ) . "?cas=$key" }; + foreach my $key ( sort keys %$casservers ) { + push @tmplservers, { name => $key, value => login_cas_url( $query, $key, $type ) }; } $template->param( casServersLoop => \@tmplservers ); } else { $template->param( - casServerUrl => login_cas_url( $query, undef, $type ), + casServerUrl => login_cas_url( $query, $query->param('cas_provider'), $type ), ); } - $template->param( invalidCasLogin => $info{'invalidCasLogin'} ); } @@ -1651,7 +1645,7 @@ sub check_api_auth { unless ( $query->param('login_userid') ) { $sessionID = $query->cookie("CGISESSID"); } - if ( $sessionID && not( $cas && $query->param('PT') ) ) { + if ( $sessionID && not( $query->param('PT') ) ) { my $return; ( $return, $session, undef ) = @@ -1677,7 +1671,7 @@ sub check_api_auth { my ( $return, $cardnumber, $cas_ticket ); # Proxy CAS auth - if ( $cas && $query->param('PT') ) { + if ( $query->param('PT') ) { my $retuserid; # In case of a CAS authentication, we use the ticket instead of the password @@ -2036,7 +2030,7 @@ sub checkpw { } $check_internal_as_fallback = 1 if $retval == 0; - } elsif ( $cas && $query && $query->param('ticket') ) { + } elsif ( $query && $query->param('ticket') && $query->param('cas_provider') ) { # In case of a CAS authentication, we use the ticket instead of the password my $ticket = $query->param('ticket'); -- 2.53.0