#!/usr/bin/perl #============================================================================= # LemonLDAP::NG session conversion tool # # This script lets an administrator migrate existing sessions from one backend # to another. It is mostly useful when run on persistant sessions, but it can # be useful in some other cases too, such as OIDC Offline sessions # # This is part of LemonLDAP::NG product, released under GPL #============================================================================= use Lemonldap::NG::Common::Apache::Session; use Lemonldap::NG::Common::Session; use Config::IniFiles; use strict; use Getopt::Long; use Pod::Usage; our $VERSION = "2.0.12"; # Options # -d: debug mode # -c: configuration file # -r: rename attributes # -i: ignore errors # -x: exclude attributes my $debug; my $config_file; my $ignore_errors; my %rename; my @exclude; my $help; my $nb_converted = 0; my $nb_error = 0; GetOptions( 'help|?' => \$help, 'debug|d' => \$debug, 'config|c=s' => \$config_file, 'ignore-errors|i' => \$ignore_errors, 'rename|r=s' => \%rename, 'exclude|x=s' => \@exclude, ) or pod2usage(2); pod2usage( -exitval => 1, -verbose => 99, -sections => "SYNOPSIS|OPTIONS|CONFIGURATION FILE FORMAT" ) if $help; unless ($config_file) { pod2usage( -exitval => 2, -verbose => 99, -message => "You must provide the -c option\n", -sections => "SYNOPSIS|OPTIONS|CONFIGURATION FILE FORMAT" ); } my $inicfg = Config::IniFiles->new( -file => $config_file, -allowcontinue => 1 ); my $cfg = {}; die "Could not read configuration file" unless $inicfg; for my $section (qw/sessions_from sessions_to/) { die "Could not find section $section in configuration file $config_file" unless $inicfg->SectionExists($section); # Load section parameters my $r; foreach ( $inicfg->Parameters($section) ) { $r->{$_} = $inicfg->val( $section, $_ ); # Remove spaces before and after value (#1488) $r->{$_} =~ s/^\s*(.+?)\s*/$1/; if ( $r->{$_} =~ /^[{\[].*[}\]]$/ || $r->{$_} =~ /^sub\s*{.*}$/ ) { eval "\$r->{$_} = $r->{$_}"; if ($@) { die "Error evaluating $section/$_: $@"; } } } $cfg->{$section} = $r; } my $backendFrom; my $backendTo; my @sessionKindOnly; if ( $cfg->{sessions_from}->{sessionKind} ) { @sessionKindOnly = split /\W+/, $cfg->{sessions_from}->{sessionKind}; } if ( $cfg->{sessions_from}->{storageModule} ) { $backendFrom = $cfg->{sessions_from}->{storageModuleOptions}; $backendFrom->{backend} = $cfg->{sessions_from}->{storageModule}; } else { die "[sessions_from] configuration section does not declare a storageModule"; } if ( $cfg->{sessions_to}->{storageModule} ) { $backendTo = $cfg->{sessions_to}->{storageModuleOptions}; $backendTo->{backend} = $cfg->{sessions_to}->{storageModule}; } else { die "[sessions_to] configuration section does not declare a storageModule"; } Lemonldap::NG::Common::Apache::Session->get_key_from_all_sessions( $backendFrom, sub { my $entry = shift; my $id = shift; # If filtering sessionKind if (@sessionKindOnly) { unless ( grep { $_ eq $entry->{_session_kind} } @sessionKindOnly ) { print "Ignoring session $id with type " . $entry->{_session_kind} . "\n" if $debug; return undef; } } if (%rename) { for my $oldkey ( keys %rename ) { my $newkey = $rename{$oldkey}; if ( $newkey and $entry->{$oldkey} ) { print "Renaming $oldkey to $newkey in session $id\n" if $debug; $entry->{$newkey} = delete $entry->{$oldkey}; } } } if (@exclude) { for my $excludekey (@exclude) { if ( $entry->{$excludekey} ) { print "Exclude $excludekey in session $id\n" if $debug; delete $entry->{$excludekey}; } } } print "Processing session $id\n" if $debug; my $s = Lemonldap::NG::Common::Session->new( { storageModule => $backendTo->{backend}, storageModuleOptions => $backendTo, id => $id, info => $entry, force => 1, } ); if ( $s->error ) { die "Error encountered on session $id" unless $ignore_errors; $nb_error += 1; print "Error converting session $id : " . $s->error . "\n"; } else { print "Session $id successfully converted\n" if $debug; $nb_converted += 1; } } ); print "$nb_converted sessions have been converted\n"; print "$nb_error errors encountered during conversion\n" if $nb_error; my $exit = $nb_error ? 1 : 0; exit $exit; __END__ =head1 NAME =encoding utf8 convertSessions - A tool to convert Lemonldap::NG sessions between storage backends. =head1 SYNOPSIS convertSession [-di] [-r oldkey=newkey ] -c parameters.ini =head1 DESCRIPTION convertConfig is a command line tool to migrate all sessions stored in a source backend (sessions_from), into a new backend (sessions_to). It requires a special configuration file in which you must list the source and destination backend modules and parameters. Sessions will not be deleted from the source backend. Existing sessions in the destination backend will be kept, unless they have the same session ID as a session in the source backend. In that case, the source will overwrite the destination. =head1 OPTIONS =over =item B<--config>,B<-c> Specify configuration file =item B<--debug>,B<-d> Turns on debugging information =item B<--ignore-errors>,B<-i> Skip to the next session if converting a session fails =item B<--rename oldkey=newkey>,B<-r oldkey=newkey> Rename key names when migrating from one backend to the next. This option can be specified multiple times =back =head1 CONFIGURATION FILE FORMAT The configuration file needs two sections to describe the source and destination backends Here is an example [sessions_from] storageModule = Apache::Session::File storageModuleOptions = { \ 'Directory' => '/var/lib/lemonldap-ng/sessions', \ 'LockDirectory' => '/var/lib/lemonldap-ng/sessions/lock', \ } # Only migrate some session types # sessionKind = Persistent, SSO [sessions_to] storageModule = Apache::Session::Browseable::Postgres storageModuleOptions = { \ 'DataSource' => 'DBI:Pg:database=lemonldapdb;host=pg.example.com', \ 'UserName' => 'lemonldaplogin', \ 'Password' => 'lemonldappw', \ 'Commit' => 1, \ 'Index' => 'ipAddr _whatToTrace user', \ 'TableName' => 'sessions', \ } The C parameter may be used to filter only some session types. Thanks to this, you can use this script to migrate from one database holding all your sessions to separate tables from each session type. =head1 SEE ALSO L =head1 AUTHORS =over =item Maxime Besson, Emaxime.besson@worteks.comE =back =head1 BUG REPORT Use OW2 system to report bug or ask for features: L =head1 DOWNLOAD Lemonldap::NG is available at L