From f4d190f257dfab823b973e1fc310fdc0caaa16d2 Mon Sep 17 00:00:00 2001 From: Tomas Cohen Arazi Date: Tue, 5 Aug 2025 15:35:06 -0300 Subject: [PATCH] Bug 40596: Add authentication provider migration script Provides a migration tool to convert legacy CAS and Shibboleth configurations to modern identity providers. Features: * Migrates casServerUrl system preference to CAS identity provider * Migrates shibboleth configuration to Shibboleth identity provider * Supports dry-run mode to preview changes * Verbose output for detailed migration information * Checks for existing providers to avoid duplicates Usage: perl misc/migration_tools/migrate_authentication_providers.pl [--dry-run] [--verbose] Test plan: 1. Apply patch 2. Set up legacy CAS/Shibboleth configuration 3. Run script with --dry-run to preview migration 4. Run script to perform actual migration 5. Verify identity providers are created correctly 6. Test authentication with migrated providers --- .../migrate_authentication_providers.pl | 234 ++++++++++++++++++ 1 file changed, 234 insertions(+) create mode 100755 misc/migration_tools/migrate_authentication_providers.pl diff --git a/misc/migration_tools/migrate_authentication_providers.pl b/misc/migration_tools/migrate_authentication_providers.pl new file mode 100755 index 00000000000..7740aceb662 --- /dev/null +++ b/misc/migration_tools/migrate_authentication_providers.pl @@ -0,0 +1,234 @@ +#!/usr/bin/perl + +# Copyright 2024 Koha Development team +# +# 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 . + +use Modern::Perl; + +use Getopt::Long qw( GetOptions ); +use Pod::Usage qw( pod2usage ); + +use C4::Context; +use Koha::Auth::Identity::Providers; + +=head1 NAME + +migrate_authentication_providers.pl - Migrate legacy authentication configurations to identity providers + +=head1 SYNOPSIS + +migrate_authentication_providers.pl [--help] [--dry-run] [--verbose] + + Options: + --help Brief help message + --dry-run Show what would be migrated without making changes + --verbose Enable verbose output + +=head1 DESCRIPTION + +This script migrates legacy CAS and Shibboleth authentication configurations +to the new identity provider system. + +=head1 OPTIONS + +=over 8 + +=item B<--help> + +Print a brief help message and exits. + +=item B<--dry-run> + +Show what would be migrated without making any changes to the database. + +=item B<--verbose> + +Enable verbose output showing details of the migration process. + +=back + +=cut + +my $help = 0; +my $dry_run = 0; +my $verbose = 0; + +GetOptions( + 'help|?' => \$help, + 'dry-run' => \$dry_run, + 'verbose' => \$verbose, +) or pod2usage(2); + +pod2usage(1) if $help; + +print "Authentication Provider Migration Tool\n"; +print "=====================================\n\n"; + +if ($dry_run) { + print "DRY RUN MODE - No changes will be made\n\n"; +} + +# Migrate CAS configuration +migrate_cas_config(); + +# Migrate Shibboleth configuration +migrate_shibboleth_config(); + +print "\nMigration completed.\n"; + +=head1 FUNCTIONS + +=head2 migrate_cas_config + +Migrates legacy CAS configuration to identity providers. + +=cut + +sub migrate_cas_config { + print "Checking CAS configuration...\n"; + + my $cas_url = C4::Context->preference('casServerUrl'); + + if ( !$cas_url ) { + print " No CAS configuration found.\n"; + return; + } + + # Check if CAS provider already exists + my $existing = Koha::Auth::Identity::Providers->search( + { + protocol => 'CAS', + code => 'cas' + } + )->next; + + if ($existing) { + print " CAS identity provider already exists (code: cas)\n"; + return; + } + + print " Found CAS server URL: $cas_url\n"; + + if ($dry_run) { + print " [DRY RUN] Would create CAS identity provider\n"; + return; + } + + # Create CAS identity provider + my $provider = Koha::Auth::Identity::Provider->new( + { + code => 'cas', + description => 'Legacy CAS Authentication', + protocol => 'CAS', + config => qq|{"cas_url":"$cas_url"}|, + mapping => qq|{"userid":"uid","email":"mail"}|, + matchpoint => 'userid' + } + )->store; + + print " Created CAS identity provider (ID: " . $provider->identity_provider_id . ")\n"; + + if ($verbose) { + print " Code: " . $provider->code . "\n"; + print " Description: " . $provider->description . "\n"; + print " Protocol: " . $provider->protocol . "\n"; + } +} + +=head2 migrate_shibboleth_config + +Migrates legacy Shibboleth configuration to identity providers. + +=cut + +sub migrate_shibboleth_config { + print "Checking Shibboleth configuration...\n"; + + my $shibboleth_config = C4::Context->config('shibboleth'); + + if ( !$shibboleth_config ) { + print " No Shibboleth configuration found.\n"; + return; + } + + # Check if Shibboleth provider already exists + my $existing = Koha::Auth::Identity::Providers->search( + { + protocol => 'Shibboleth', + code => 'shibboleth' + } + )->next; + + if ($existing) { + print " Shibboleth identity provider already exists (code: shibboleth)\n"; + return; + } + + print " Found Shibboleth configuration\n"; + + if ($dry_run) { + print " [DRY RUN] Would create Shibboleth identity provider\n"; + return; + } + + # Extract configuration + my $sso_url = $shibboleth_config->{ssoUrl} || '/Shibboleth.sso/Login'; + my $slo_url = $shibboleth_config->{sloUrl} || '/Shibboleth.sso/Logout'; + my $entity_id = $shibboleth_config->{entityID} || 'koha'; + my $matchpoint = $shibboleth_config->{matchpoint} || 'userid'; + + # Build configuration JSON + my $config = qq|{"sso_url":"$sso_url","slo_url":"$slo_url","entity_id":"$entity_id"}|; + + # Build mapping JSON from legacy mapping + my $mapping = '{"userid":"HTTP_REMOTE_USER","email":"HTTP_MAIL","firstname":"HTTP_GIVENNAME","surname":"HTTP_SN"}'; + if ( $shibboleth_config->{mapping} ) { + + # Convert legacy mapping format + my @mapping_parts; + for my $field ( keys %{ $shibboleth_config->{mapping} } ) { + my $attr = $shibboleth_config->{mapping}->{$field}; + if ( ref($attr) eq 'HASH' && $attr->{is} ) { + push @mapping_parts, qq|"$field":"$attr->{is}"|; + } elsif ( !ref($attr) ) { + push @mapping_parts, qq|"$field":"$attr"|; + } + } + $mapping = '{' . join( ',', @mapping_parts ) . '}' if @mapping_parts; + } + + # Create Shibboleth identity provider + my $provider = Koha::Auth::Identity::Provider->new( + { + code => 'shibboleth', + description => 'Legacy Shibboleth Authentication', + protocol => 'Shibboleth', + config => $config, + mapping => $mapping, + matchpoint => $matchpoint + } + )->store; + + print " Created Shibboleth identity provider (ID: " . $provider->identity_provider_id . ")\n"; + + if ($verbose) { + print " Code: " . $provider->code . "\n"; + print " Description: " . $provider->description . "\n"; + print " Protocol: " . $provider->protocol . "\n"; + print " Matchpoint: " . $provider->matchpoint . "\n"; + } +} -- 2.50.1