Bugzilla – Attachment 193621 Details for
Bug 39224
Migrate SAML/Shibboleth configuration into Identity Providers
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 39224: Add and Update Koha Object classes
9cad677.patch (text/plain), 29.36 KB, created by
Martin Renvoize (ashimema)
on 2026-02-23 11:17:00 UTC
(
hide
)
Description:
Bug 39224: Add and Update Koha Object classes
Filename:
MIME Type:
Creator:
Martin Renvoize (ashimema)
Created:
2026-02-23 11:17:00 UTC
Size:
29.36 KB
patch
obsolete
>From 9cad6778d43d04ae602ba363cad677d4f5151a5d Mon Sep 17 00:00:00 2001 >From: Martin Renvoize <martin.renvoize@openfifth.co.uk> >Date: Sun, 22 Feb 2026 20:18:30 +0000 >Subject: [PATCH] Bug 39224: Add and Update Koha Object classes > >Updates the Koha object layer for unified identity providers: > >- Koha::Auth::Hostname(s): Manage library hostnames >- Koha::Auth::Identity::Provider::Hostname(s): Link providers to > hostnames with force_sso support >- Koha::Auth::Identity::Provider::Mapping(s): Map provider fields > to patron attributes >- Updates Koha::Auth::Identity::Provider with mappings/hostnames > accessors and simplified to_api >- Updates Koha::Auth::Identity::Providers with find_forced_provider > and polymorphic dispatch via object_class >- Updates C4::Auth for hostname-based provider selection >- Updates C4::Auth_with_shibboleth for new provider structure >- Adds ForceIdentityProvider support in Koha::Config::SysPref >- Updates AuthClient template plugin for hostname-aware login > >Test plan: >1. Run: prove t/db_dependent/Koha/Auth/Identity/Provider.t >2. Verify provider objects can be created and linked to hostnames > >Sponsored-by: ByWater Solutions >--- > C4/Auth.pm | 46 +++++----- > C4/Auth_with_shibboleth.pm | 68 ++++++++++---- > Koha/Auth/Hostname.pm | 87 ++++++++++++++++++ > Koha/Auth/Hostnames.pm | 50 +++++++++++ > Koha/Auth/Identity/Provider.pm | 109 ++++++++++------------- > Koha/Auth/Identity/Provider/Hostname.pm | 56 ++++++++++++ > Koha/Auth/Identity/Provider/Hostnames.pm | 51 +++++++++++ > Koha/Auth/Identity/Provider/Mapping.pm | 72 +++++++++++++++ > Koha/Auth/Identity/Provider/Mappings.pm | 89 ++++++++++++++++++ > Koha/Auth/Identity/Providers.pm | 70 ++++++++++++++- > Koha/Config/SysPref.pm | 9 +- > Koha/Template/Plugin/AuthClient.pm | 17 +++- > Makefile.PL | 1 + > 13 files changed, 619 insertions(+), 106 deletions(-) > create mode 100644 Koha/Auth/Hostname.pm > create mode 100644 Koha/Auth/Hostnames.pm > create mode 100644 Koha/Auth/Identity/Provider/Hostname.pm > create mode 100644 Koha/Auth/Identity/Provider/Hostnames.pm > create mode 100644 Koha/Auth/Identity/Provider/Mapping.pm > create mode 100644 Koha/Auth/Identity/Provider/Mappings.pm > >diff --git a/C4/Auth.pm b/C4/Auth.pm >index b5308ae4a42..bae5ebe4150 100644 >--- a/C4/Auth.pm >+++ b/C4/Auth.pm >@@ -173,7 +173,7 @@ sub get_template_and_user { > my $cookie_mgr = Koha::CookieManager->new; > > # Get shibboleth login attribute >- my $shib = C4::Context->config('useshibboleth') && shib_ok(); >+ my $shib = C4::Context->preference('ShibbolethAuthentication') && shib_ok(); > my $shib_login = $shib ? get_login_shib() : undef; > > C4::Context->interface( $in->{type} ); >@@ -811,7 +811,7 @@ sub checkauth { > my $query = shift; > > # Get shibboleth login attribute >- my $shib = C4::Context->config('useshibboleth') && shib_ok(); >+ my $shib = C4::Context->preference('ShibbolethAuthentication') && shib_ok(); > my $shib_login = $shib ? get_login_shib() : undef; > > # $authnotrequired will be set for scripts which will run without authentication >@@ -1021,6 +1021,13 @@ sub checkauth { > $auth_state = 'logout'; > } > >+ my $is_sco_sci = >+ ( $query->param('sco_user_login') >+ || $query->param('sci_user_login') >+ || ( $template_name && $template_name =~ m|sc[io]/| ) ); >+ my $forced_provider = >+ $is_sco_sci ? undef : Koha::Auth::Identity::Providers->find_forced_provider( $ENV{SERVER_NAME} ); >+ > unless ($userid) { > > #we initiate a session prior to checking for a username to allow for anonymous sessions... >@@ -1151,15 +1158,9 @@ sub checkauth { > } > > # If shib configured and shibOnly enabled, we should ignore anything other than a shibboleth type login. >- if ( >- $shib >+ if ( $shib > && !$shibSuccess >- && ( >- ( ( $type eq 'opac' ) && C4::Context->preference('OPACShibOnly') ) >- || ( ( $type ne 'opac' ) >- && C4::Context->preference('staffShibOnly') ) >- ) >- ) >+ && $forced_provider ) > { > $return = 0; > } >@@ -1522,6 +1523,20 @@ sub checkauth { > ); > } > >+ if ($forced_provider) { >+ my $redirect_url; >+ if ( $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"; >+ $redirect_url = "$base/" . $forced_provider->code . "/$type"; >+ } >+ if ($redirect_url) { >+ print $query->redirect( -uri => $redirect_url, -status => 303 ); >+ safe_exit; >+ } >+ } >+ > if ($cas) { > > # Is authentication against multiple CAS servers enabled? >@@ -1544,15 +1559,6 @@ sub checkauth { > > if ($shib) { > >- #If shibOnly is enabled just go ahead and redirect directly >- if ( ( ( $type eq 'opac' ) && C4::Context->preference('OPACShibOnly') ) >- || ( ( $type ne 'opac' ) && C4::Context->preference('staffShibOnly') ) ) >- { >- my $redirect_url = login_shib_url($query); >- print $query->redirect( -uri => "$redirect_url", -status => 303 ); >- safe_exit; >- } >- > $template->param( > shibbolethAuthentication => $shib, > shibbolethLoginUrl => login_shib_url($query), >@@ -2003,7 +2009,7 @@ sub checkpw { > $type = 'opac' unless $type; > > # Get shibboleth login attribute >- my $shib = C4::Context->config('useshibboleth') && shib_ok(); >+ my $shib = C4::Context->preference('ShibbolethAuthentication') && shib_ok(); > my $shib_login = $shib ? get_login_shib() : undef; > > my $anonymous_patron = C4::Context->preference('AnonymousPatron'); >diff --git a/C4/Auth_with_shibboleth.pm b/C4/Auth_with_shibboleth.pm >index dd58ec747eb..d5449b925f6 100644 >--- a/C4/Auth_with_shibboleth.pm >+++ b/C4/Auth_with_shibboleth.pm >@@ -18,10 +18,13 @@ package C4::Auth_with_shibboleth; > # along with Koha; if not, see <https://www.gnu.org/licenses>. > > use Modern::Perl; >-use base 'Exporter'; >+ >+our ( @ISA, @EXPORT_OK ); > > BEGIN { >- our @EXPORT_OK = qw(shib_ok logout_shib login_shib_url checkpw_shib get_login_shib); >+ require Exporter; >+ @ISA = qw(Exporter); >+ @EXPORT_OK = qw(shib_ok logout_shib login_shib_url checkpw_shib get_login_shib); > } > > use C4::Context; >@@ -34,6 +37,7 @@ use Carp qw( carp ); > use List::MoreUtils qw( any ); > > use Koha::Logger; >+use Koha::Auth::Identity::Providers; > > # Check that shib config is not malformed > >@@ -241,28 +245,45 @@ sub _get_return { > } > > sub _get_shib_config { >- my $config = C4::Context->config('shibboleth'); >- >- if ( !$config ) { >- Koha::Logger->get->warn('shibboleth config not defined'); >+ my $hostname = $ENV{SERVER_NAME}; >+ my $provider = Koha::Auth::Identity::Providers->search( >+ { >+ 'me.protocol' => 'SAML2', >+ 'me.enabled' => 1, >+ 'hostname.hostname' => $hostname, >+ 'hostnames.is_enabled' => 1, >+ }, >+ { prefetch => [ 'mappings', { 'hostnames' => 'hostname' } ], rows => 1 } >+ )->next; >+ return 0 unless $provider; >+ >+ my ( $mapping, $matchpoint ) = $provider->mappings->as_auth_mapping; >+ >+ unless ($matchpoint) { >+ carp 'shibboleth matchpoint not defined'; > return 0; > } > >- if ( $config->{matchpoint} >- && defined( $config->{mapping}->{ $config->{matchpoint} }->{is} ) ) >- { >- my $logger = Koha::Logger->get; >- $logger->debug( "koha borrower field to match: " . $config->{matchpoint} ); >- $logger->debug( "shibboleth attribute to match: " . $config->{mapping}->{ $config->{matchpoint} }->{is} ); >- return $config; >- } else { >- if ( !$config->{matchpoint} ) { >- carp 'shibboleth matchpoint not defined'; >- } else { >- carp 'shibboleth matchpoint not mapped'; >- } >+ unless ( defined $mapping->{$matchpoint}->{is} ) { >+ carp 'shibboleth matchpoint not mapped'; > return 0; > } >+ >+ my $saml2_config = $provider->get_config // {}; >+ >+ my $config = { >+ matchpoint => $matchpoint, >+ mapping => $mapping, >+ autocreate => $saml2_config->{autocreate} || 0, >+ sync => $saml2_config->{sync} || 0, >+ welcome => $saml2_config->{welcome} || 0, >+ }; >+ >+ my $logger = Koha::Logger->get; >+ $logger->debug( "koha borrower field to match: " . $config->{matchpoint} ); >+ $logger->debug( "shibboleth attribute to match: " . $mapping->{ $config->{matchpoint} }->{is} ); >+ >+ return $config; > } > > 1; >@@ -405,6 +426,15 @@ A sugar function to that simply returns the current page URI with appropriate pr > > This routine is NOT exported > >+=head2 get_force_sso_setting >+ >+ my $force_sso = get_force_sso_setting($interface); >+ >+Returns the force SSO setting for the given interface (opac or intranet). >+Returns 0 if shibboleth is not configured or the setting is disabled. >+ >+ $interface - 'opac' or 'intranet' >+ > =head2 _get_shib_config > > my $config = _get_shib_config(); >diff --git a/Koha/Auth/Hostname.pm b/Koha/Auth/Hostname.pm >new file mode 100644 >index 00000000000..dfa7c11f659 >--- /dev/null >+++ b/Koha/Auth/Hostname.pm >@@ -0,0 +1,87 @@ >+package Koha::Auth::Hostname; >+ >+# Copyright Koha Community 2026 >+# >+# 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 3 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, see <https://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+ >+use base qw(Koha::Object); >+ >+use C4::Context; >+use Koha::Database; >+ >+=head1 NAME >+ >+Koha::Auth::Hostname - Koha Auth Hostname Object class >+ >+=head1 API >+ >+=head2 Class methods >+ >+=head3 sync_from_syspref >+ >+ Koha::Auth::Hostname->sync_from_syspref( 'OPACBaseURL', $url ); >+ >+Updates (or inserts) the reserved hostname row for the given URL syspref. >+C<OPACBaseURL> is always stored as C<hostname_id = 1>; C<staffClientBaseURL> >+as C<hostname_id = 2>. Does nothing if C<$value> is empty or does not contain >+a recognisable hostname. >+ >+=cut >+ >+my %SYSPREF_HOSTNAME_ID = ( >+ opacbaseurl => 1, >+ staffclientbaseurl => 2, >+); >+ >+sub sync_from_syspref { >+ my ( $class, $pref_name, $value ) = @_; >+ $value //= ''; >+ my $hostname_id = $SYSPREF_HOSTNAME_ID{ lc $pref_name } or return; >+ my ($hostname) = $value =~ m{^https?://([^/:?#]+)} or return; >+ Koha::Database->new->schema->resultset('Hostname')->update_or_create( >+ { hostname_id => $hostname_id, hostname => $hostname }, >+ { key => 'primary' } >+ ); >+} >+ >+=head3 sync_from_sysprefs >+ >+Ensures that the hostnames derived from OPACBaseURL and staffClientBaseURL >+are present in the hostnames table. OPACBaseURL maps to C<hostname_id = 1> >+and staffClientBaseURL to C<hostname_id = 2>. >+ >+=cut >+ >+sub sync_from_sysprefs { >+ my ($class) = @_; >+ for my $pref ( keys %SYSPREF_HOSTNAME_ID ) { >+ $class->sync_from_syspref( $pref, C4::Context->preference($pref) ); >+ } >+} >+ >+=head2 Internal methods >+ >+=head3 _type >+ >+=cut >+ >+sub _type { >+ return 'Hostname'; >+} >+ >+1; >diff --git a/Koha/Auth/Hostnames.pm b/Koha/Auth/Hostnames.pm >new file mode 100644 >index 00000000000..95b200bb9a3 >--- /dev/null >+++ b/Koha/Auth/Hostnames.pm >@@ -0,0 +1,50 @@ >+package Koha::Auth::Hostnames; >+ >+# Copyright Koha Community 2026 >+# >+# 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 3 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, see <https://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+ >+use base qw(Koha::Objects); >+ >+use Koha::Auth::Hostname; >+ >+=head1 NAME >+ >+Koha::Auth::Hostnames - Koha Auth Hostnames Object set class >+ >+=head1 API >+ >+=head2 Internal methods >+ >+=head3 _type >+ >+=cut >+ >+sub _type { >+ return 'Hostname'; >+} >+ >+=head3 object_class >+ >+=cut >+ >+sub object_class { >+ return 'Koha::Auth::Hostname'; >+} >+ >+1; >diff --git a/Koha/Auth/Identity/Provider.pm b/Koha/Auth/Identity/Provider.pm >index f9f3d51339d..c9788c7df4a 100644 >--- a/Koha/Auth/Identity/Provider.pm >+++ b/Koha/Auth/Identity/Provider.pm >@@ -22,6 +22,8 @@ use Modern::Perl; > use base qw(Koha::Object Koha::Object::JSONFields); > > use Koha::Auth::Identity::Provider::Domains; >+use Koha::Auth::Identity::Provider::Hostnames; >+use Koha::Auth::Identity::Provider::Mappings; > use Koha::Exceptions; > > =head1 NAME >@@ -46,6 +48,35 @@ sub domains { > return Koha::Auth::Identity::Provider::Domains->_new_from_dbic( scalar $self->_result->domains ); > } > >+=head3 mappings >+ >+ my $mappings = $provider->mappings; >+ >+Returns the related I<Koha::Auth::Identity::Provider::Mappings> iterator. >+ >+=cut >+ >+sub mappings { >+ my ($self) = @_; >+ >+ return Koha::Auth::Identity::Provider::Mappings->_new_from_dbic( >+ scalar $self->_result->identity_provider_mappings ); >+} >+ >+=head3 hostnames >+ >+ my $hostnames = $provider->hostnames; >+ >+Returns the related I<Koha::Auth::Identity::Provider::Hostnames> iterator. >+ >+=cut >+ >+sub hostnames { >+ my ($self) = @_; >+ >+ return Koha::Auth::Identity::Provider::Hostnames->_new_from_dbic( scalar $self->_result->hostnames ); >+} >+ > =head3 get_config > > my $config = $provider->get_config; >@@ -81,6 +112,15 @@ sub get_config { > } > ); > >+ # SAML2 (config holds protocol-specific settings like autocreate/sync/welcome) >+ $provider->set_config( >+ { >+ autocreate => 1, >+ sync => 1, >+ welcome => 0, >+ } >+ ); >+ > This method stores the passed config in JSON format. > > =cut >@@ -99,53 +139,16 @@ sub set_config { > return $self->set_encoded_json_field( { data => $config, field => 'config' } ); > } > >-=head3 get_mapping >+=head3 store > >- my $mapping = $provider->get_mapping; >- >-Returns a I<hashref> containing the attribute mapping for the provider. >+Identity provider specific store method to ensure config population before saving to db > > =cut > >-sub get_mapping { >+sub store { > my ($self) = @_; >- >- return $self->decode_json_field( { field => 'mapping' } ); >-} >- >-=head3 set_mapping >- >- $provider->mapping( $mapping ); >- >-This method stores the passed mappings in JSON format. >- >-=cut >- >-sub set_mapping { >- my ( $self, $mapping ) = @_; >- >- return $self->set_encoded_json_field( { data => $mapping, field => 'mapping' } ); >-} >- >-=head3 upgrade_class >- >- my $upgraded_object = $provider->upgrade_class >- >-Returns a new instance of the object, with the right class. >- >-=cut >- >-sub upgrade_class { >- my ($self) = @_; >- my $protocol = $self->protocol; >- >- my $class = $self->protocol_to_class_mapping->{$protocol}; >- >- Koha::Exception->throw( $protocol . ' is not a valid protocol' ) >- unless $class; >- >- eval "require $class"; >- return $class->_new_from_dbic( $self->_result ); >+ $self->config( $self->config // '{}' ); >+ return $self->SUPER::store(); > } > > =head2 Internal methods >@@ -162,12 +165,10 @@ suitable for API output. > sub to_api { > my ( $self, $params ) = @_; > >- my $config = $self->get_config; >- my $mapping = $self->get_mapping; >+ my $config = $self->get_config; > > my $json = $self->SUPER::to_api($params); >- $json->{config} = $config; >- $json->{mapping} = $mapping; >+ $json->{config} = $config; > > return $json; > } >@@ -180,22 +181,6 @@ sub _type { > return 'IdentityProvider'; > } > >-=head3 protocol_to_class_mapping >- >- my $mapping = Koha::Auth::Identity::Provider::protocol_to_class_mapping >- >-Internal method that returns a mapping between I<protocol> codes and >-implementing I<classes>. To be used by B<upgrade_class>. >- >-=cut >- >-sub protocol_to_class_mapping { >- return { >- OAuth => 'Koha::Auth::Identity::Provider::OAuth', >- OIDC => 'Koha::Auth::Identity::Provider::OIDC', >- }; >-} >- > =head3 mandatory_config_attributes > > Stub method for raising exceptions on invalid protocols. >diff --git a/Koha/Auth/Identity/Provider/Hostname.pm b/Koha/Auth/Identity/Provider/Hostname.pm >new file mode 100644 >index 00000000000..824aa98bc1f >--- /dev/null >+++ b/Koha/Auth/Identity/Provider/Hostname.pm >@@ -0,0 +1,56 @@ >+package Koha::Auth::Identity::Provider::Hostname; >+ >+# Copyright Koha Community 2026 >+# >+# 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 3 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, see <https://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+ >+use base qw(Koha::Object); >+ >+=head1 NAME >+ >+Koha::Auth::Identity::Provider::Hostname - Koha Auth Provider Hostname Object class >+ >+=head1 API >+ >+=head2 Class methods >+ >+=head3 to_api >+ >+Overrides the default serialization to embed the hostname string from the >+related Hostname record alongside the hostname_id FK. >+ >+=cut >+ >+sub to_api { >+ my ( $self, $params ) = @_; >+ my $data = $self->SUPER::to_api($params); >+ $data->{hostname} = $self->_result->hostname->hostname; >+ return $data; >+} >+ >+=head2 Internal methods >+ >+=head3 _type >+ >+=cut >+ >+sub _type { >+ return 'IdentityProviderHostname'; >+} >+ >+1; >diff --git a/Koha/Auth/Identity/Provider/Hostnames.pm b/Koha/Auth/Identity/Provider/Hostnames.pm >new file mode 100644 >index 00000000000..e22fd1f2fb8 >--- /dev/null >+++ b/Koha/Auth/Identity/Provider/Hostnames.pm >@@ -0,0 +1,51 @@ >+package Koha::Auth::Identity::Provider::Hostnames; >+ >+# Copyright Koha Community 2026 >+# >+# 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 3 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, see <https://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+ >+use Koha::Database; >+use Koha::Auth::Identity::Provider::Hostname; >+ >+use base qw(Koha::Objects); >+ >+=head1 NAME >+ >+Koha::Auth::Identity::Provider::Hostnames - Koha Auth Provider Hostnames Object set class >+ >+=head1 API >+ >+=head2 Internal methods >+ >+=head3 _type >+ >+=cut >+ >+sub _type { >+ return 'IdentityProviderHostname'; >+} >+ >+=head3 object_class >+ >+=cut >+ >+sub object_class { >+ return 'Koha::Auth::Identity::Provider::Hostname'; >+} >+ >+1; >diff --git a/Koha/Auth/Identity/Provider/Mapping.pm b/Koha/Auth/Identity/Provider/Mapping.pm >new file mode 100644 >index 00000000000..721f700993f >--- /dev/null >+++ b/Koha/Auth/Identity/Provider/Mapping.pm >@@ -0,0 +1,72 @@ >+package Koha::Auth::Identity::Provider::Mapping; >+ >+# 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 3 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, see <https://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+ >+use base qw(Koha::Object); >+ >+use Koha::Exceptions; >+use Koha::Auth::Identity::Provider::Mappings; >+ >+=head1 NAME >+ >+Koha::Auth::Identity::Provider::Mapping - Koha Identity Provider field mapping object >+ >+=head1 API >+ >+=head2 Class methods >+ >+=head3 store >+ >+Override store to enforce the single-matchpoint rule and validate that at least >+one of C<provider_field> or C<default_content> is supplied. >+ >+=cut >+ >+sub store { >+ my ($self) = @_; >+ >+ unless ( defined $self->provider_field || defined $self->default_content ) { >+ Koha::Exceptions::MissingParameter->throw( parameter => 'provider_field or default_content' ); >+ } >+ >+ if ( $self->is_matchpoint ) { >+ >+ # Clear any existing matchpoint for this provider >+ Koha::Auth::Identity::Provider::Mappings->search( >+ { >+ identity_provider_id => $self->identity_provider_id, >+ is_matchpoint => 1, >+ ( $self->mapping_id ? ( mapping_id => { '!=' => $self->mapping_id } ) : () ), >+ } >+ )->update( { is_matchpoint => 0 } ); >+ } >+ >+ return $self->SUPER::store(); >+} >+ >+=head2 Internal methods >+ >+=head3 _type >+ >+=cut >+ >+sub _type { >+ return 'IdentityProviderMapping'; >+} >+ >+1; >diff --git a/Koha/Auth/Identity/Provider/Mappings.pm b/Koha/Auth/Identity/Provider/Mappings.pm >new file mode 100644 >index 00000000000..4607ba38368 >--- /dev/null >+++ b/Koha/Auth/Identity/Provider/Mappings.pm >@@ -0,0 +1,89 @@ >+package Koha::Auth::Identity::Provider::Mappings; >+ >+# 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 3 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, see <https://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+ >+use base qw(Koha::Objects); >+ >+use Koha::Auth::Identity::Provider::Mapping; >+ >+=head1 NAME >+ >+Koha::Auth::Identity::Provider::Mappings - Koha Identity Provider field mappings collection >+ >+=head1 API >+ >+=head2 Class methods >+ >+=head3 get_matchpoint >+ >+ my $matchpoint_mapping = $mappings->get_matchpoint; >+ >+Returns the mapping row flagged as the matchpoint, or undef if none. >+ >+=cut >+ >+sub get_matchpoint { >+ my ($self) = @_; >+ return $self->search( { is_matchpoint => 1 } )->next; >+} >+ >+=head3 as_auth_mapping >+ >+ my $mapping_hashref = $mappings->as_auth_mapping; >+ >+Returns a hashref suitable for use by the authentication layer: >+C<{ koha_field => provider_field, ... }> plus C<matchpoint> key. >+ >+=cut >+ >+sub as_auth_mapping { >+ my ($self) = @_; >+ >+ my %mapping; >+ my $matchpoint; >+ >+ while ( my $m = $self->next ) { >+ $mapping{ $m->koha_field } = { >+ is => $m->provider_field, >+ content => $m->default_content, >+ }; >+ $matchpoint = $m->koha_field if $m->is_matchpoint; >+ } >+ >+ return ( \%mapping, $matchpoint ); >+} >+ >+=head2 Internal methods >+ >+=head3 _type >+ >+=cut >+ >+sub _type { >+ return 'IdentityProviderMapping'; >+} >+ >+=head3 object_class >+ >+=cut >+ >+sub object_class { >+ return 'Koha::Auth::Identity::Provider::Mapping'; >+} >+ >+1; >diff --git a/Koha/Auth/Identity/Providers.pm b/Koha/Auth/Identity/Providers.pm >index 0bedd81880c..1cd47471d5b 100644 >--- a/Koha/Auth/Identity/Providers.pm >+++ b/Koha/Auth/Identity/Providers.pm >@@ -19,8 +19,10 @@ package Koha::Auth::Identity::Providers; > > use Modern::Perl; > >-use Koha::Database; > use Koha::Auth::Identity::Provider; >+use Koha::Auth::Identity::Provider::Hostnames; >+use Koha::Auth::Identity::Provider::OAuth; >+use Koha::Auth::Identity::Provider::OIDC; > > use base qw(Koha::Objects); > >@@ -30,6 +32,37 @@ Koha::Auth::Identity::Providers - Koha Auth Provider Object class > > =head1 API > >+=head2 Class methods >+ >+=head3 find_forced_provider >+ >+ my $provider = Koha::Auth::Identity::Providers->find_forced_provider($hostname); >+ >+Returns the I<Koha::Auth::Identity::Provider> object if the given I<hostname> is configured to >+force SSO, or C<undef> if no forced provider is found. >+ >+=cut >+ >+sub find_forced_provider { >+ my ( $self, $hostname ) = @_; >+ return unless $hostname; >+ >+ my $hostname_link = Koha::Auth::Identity::Provider::Hostnames->search( >+ { >+ 'hostname.hostname' => $hostname, >+ 'identity_provider.enabled' => 1, >+ 'me.is_enabled' => 1, >+ 'me.force_sso' => 1, >+ }, >+ { join => [ 'hostname', 'identity_provider' ] } >+ )->next; >+ >+ return unless $hostname_link; >+ >+ my $ip_result = $hostname_link->_result->identity_provider; >+ return $self->object_class($ip_result)->_new_from_dbic($ip_result); >+} >+ > =head2 Internal methods > > =cut >@@ -42,12 +75,45 @@ sub _type { > return 'IdentityProvider'; > } > >+=head3 _polymorphic_field >+ >+Return the field in the table that defines the polymorphic class to be built >+ >+=cut >+ >+sub _polymorphic_field { >+ return 'protocol'; >+} >+ >+=head3 _polymorphic_map >+ >+Return the mapping from protocol value to implementing class name >+ >+=cut >+ >+sub _polymorphic_map { >+ return { >+ OAuth => 'Koha::Auth::Identity::Provider::OAuth', >+ OIDC => 'Koha::Auth::Identity::Provider::OIDC', >+ }; >+} >+ > =head3 object_class > >+Return object class dynamically based on protocol >+ > =cut > > sub object_class { >- return 'Koha::Auth::Identity::Provider'; >+ my ( $self, $object ) = @_; >+ >+ return 'Koha::Auth::Identity::Provider' unless $object; >+ >+ my $field = $self->_polymorphic_field; >+ my $map = $self->_polymorphic_map; >+ >+ return $map->{ $object->$field } || 'Koha::Auth::Identity::Provider'; > } > > 1; >+ >diff --git a/Koha/Config/SysPref.pm b/Koha/Config/SysPref.pm >index 229a291553c..976707597dc 100644 >--- a/Koha/Config/SysPref.pm >+++ b/Koha/Config/SysPref.pm >@@ -46,7 +46,14 @@ sub store { > > C4::Log::logaction( 'SYSTEMPREFERENCE', $action, undef, $self->variable . ' | ' . $self->value ); > >- return $self->SUPER::store($self); >+ my $result = $self->SUPER::store($self); >+ >+ if ( $self->variable =~ /\A(?:opacbaseurl|staffclientbaseurl)\z/i ) { >+ require Koha::Auth::Hostname; >+ Koha::Auth::Hostname->sync_from_syspref( $self->variable, $self->value ); >+ } >+ >+ return $result; > } > > =head3 delete >diff --git a/Koha/Template/Plugin/AuthClient.pm b/Koha/Template/Plugin/AuthClient.pm >index 7b28a3facd4..ccd858e0d3b 100644 >--- a/Koha/Template/Plugin/AuthClient.pm >+++ b/Koha/Template/Plugin/AuthClient.pm >@@ -55,8 +55,21 @@ sub get_providers { > > # Handle database upgrade state where schema might be out of sync > try { >- my $providers = >- Koha::Auth::Identity::Providers->search( { "domains.allow_$interface" => 1 }, { prefetch => 'domains' } ); >+ my $hostname = $ENV{SERVER_NAME}; >+ my $providers = Koha::Auth::Identity::Providers->search( >+ { >+ "domains.allow_$interface" => 1, >+ "me.enabled" => 1, >+ -or => [ >+ { 'hostname.hostname' => $hostname, 'hostnames.is_enabled' => 1 }, >+ { 'hostnames.identity_provider_hostname_id' => undef } >+ ] >+ }, >+ { >+ join => [ 'domains', { 'hostnames' => 'hostname' } ], >+ distinct => 1 >+ } >+ ); > my $base_url = ( $interface eq 'staff' ) ? "/api/v1/oauth/login" : "/api/v1/public/oauth/login"; > > while ( my $provider = $providers->next ) { >diff --git a/Makefile.PL b/Makefile.PL >index 6e849fe9911..5b85970c796 100644 >--- a/Makefile.PL >+++ b/Makefile.PL >@@ -363,6 +363,7 @@ my $target_map = { > './serials' => 'INTRANET_CGI_DIR', > './services' => 'INTRANET_CGI_DIR', > './sip2' => 'INTRANET_CGI_DIR', >+ './shibboleth' => 'INTRANET_CGI_DIR', > './skel' => 'NONE', > './skel/var/lock/koha' => { target => 'LOCK_DIR', trimdir => -1 }, > './skel/var/log/koha' => { target => 'LOG_DIR', trimdir => -1 }, >-- >2.53.0 >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 39224
:
189234
|
189235
|
189236
|
189237
|
189238
|
189239
|
189240
|
189241
|
189242
|
189243
|
189244
|
193618
|
193619
|
193620
|
193621
|
193622
|
193623
|
193624
|
193625
|
193626
|
193627
|
193681
|
193682
|
193683
|
193684
|
193685
|
193686
|
193687
|
193688
|
193689
|
193690
|
193691
|
193692