lemonldap-ng/lemonldap-ng-portal/site/cron/purgeCentralCache

323 lines
9.9 KiB
Perl
Executable File

#!/usr/bin/perl
#=============================================================================
# Cleaner for LemonLDAP::NG: removes old sessions from Apache::Session
#
# This module is written to be used by cron to clean old sessions from
# Apache::Session. It does not works with Apache::Session::Memcached
#
# This is part of LemonLDAP::NG product, released under GPL
#=============================================================================
use Lemonldap::NG::Common::Conf;
use Lemonldap::NG::Common::Conf::Constants;
use Lemonldap::NG::Common::Apache::Session;
use Lemonldap::NG::Common::Session;
use strict;
use Getopt::Std;
# Options
# -d: debug mode
# -f: force delete of corrupted sessions
our $opt_d;
our $opt_f;
getopts('df');
my $debug = $opt_d;
my $force = $opt_f;
my $nb_purged = 0;
my $nb_error = 0;
#=============================================================================
# Load configuration
#=============================================================================
my $lmconf = Lemonldap::NG::Common::Conf->new()
or die $Lemonldap::NG::Common::Conf::msg;
my $conf = $lmconf->getConf or die "Unable to get configuration ($!)";
my $localconf = $lmconf->getLocalConf(PORTALSECTION)
or die "Unable to get local configuration ($!)";
if ($localconf) {
$conf->{$_} = $localconf->{$_} foreach ( keys %$localconf );
}
print "Configuration loaded\n" if $debug;
#=============================================================================
# Timeout
#=============================================================================
print "Timeout value: " . $conf->{timeout} . "\n" if $debug;
#=============================================================================
# Apache::Session backends
#=============================================================================
my @backends;
my $module;
# Sessions
if ( defined $conf->{globalStorage} ) {
# Load module
$module = $conf->{globalStorage};
eval "use $module";
die $@ if ($@);
$conf->{globalStorageOptions}->{backend} = $module;
# Add module in managed backends
push @backends, $conf->{globalStorageOptions};
print "Session backend $module will be used\n" if $debug;
}
# SAML
if ( defined $conf->{samlStorage}
or keys %{ $conf->{samlStorageOptions} } )
{
# Load module
$module = $conf->{samlStorage} || $conf->{globalStorage};
eval "use $module";
die $@ if ($@);
$conf->{samlStorageOptions}->{backend} = $module;
# Add module in managed backends
push @backends, $conf->{samlStorageOptions};
print "SAML backend $module will be used\n" if $debug;
}
# CAS
if ( defined $conf->{casStorage}
or keys %{ $conf->{casStorageOptions} } )
{
# Load module
$module = $conf->{casStorage} || $conf->{globalStorage};
eval "use $module";
die $@ if ($@);
$conf->{casStorageOptions}->{backend} = $module;
# Add module in managed backends
push @backends, $conf->{casStorageOptions};
print "CAS backend $module will be used\n" if $debug;
}
# Captcha
if ( defined $conf->{captchaStorage}
or keys %{ $conf->{captchaStorageOptions} } )
{
# Load module
$module = $conf->{captchaStorage} || $conf->{globalStorage};
eval "use $module";
die $@ if ($@);
$conf->{captchaStorageOptions}->{backend} = $module;
# Add module in managed backends
push @backends, $conf->{captchaStorageOptions};
print "Captcha backend $module will be used\n" if $debug;
}
# OpenIDConnect
if ( defined $conf->{oidcStorage}
or keys %{ $conf->{oidcStorageOptions} } )
{
# Load module
$module = $conf->{oidcStorage} || $conf->{globalStorage};
eval "use $module";
die $@ if ($@);
$conf->{oidcStorageOptions}->{backend} = $module;
# Add module in managed backends
push @backends, $conf->{oidcStorageOptions};
print "OIDC backend $module will be used\n" if $debug;
}
#=============================================================================
# Load and purge sessions
#=============================================================================
for my $options (@backends) {
next if ( $options->{backend} eq "Apache::Session::Memcached" );
my @t;
if ( $options->{backend}->can('deleteIfLowerThan') ) {
my ( $success, $rows ) = $options->{backend}->deleteIfLowerThan(
$options,
{
not => { '_session_kind' => 'Persistent' },
or => {
_utime => time - $conf->{timeout},
(
$conf->{timeoutActivity}
? ( _lastSeen => time - $conf->{timeoutActivity} )
: ()
)
}
}
);
if ($success) {
if ($rows) {
$nb_purged += $rows;
}
next;
}
}
# Get all expired sessions
Lemonldap::NG::Common::Apache::Session->get_key_from_all_sessions(
$options,
sub {
my $entry = shift;
my $id = shift;
my $time = time;
print "Check session $id\n" if $debug;
# Empty session need to be removed
unless ($entry) {
push @t, $id;
print "Session $id is empty (corrupted?), delete forced\n"
if $debug;
}
# Do net check sessions without _utime
return undef unless $entry->{_utime};
# Do not expire persistent sessions
return undef if ( $entry->{_session_kind} eq "Persistent" );
# Session expired
if ( $time - $entry->{_utime} > $conf->{timeout} ) {
push @t, $id;
print "Session $id expired\n" if $debug;
}
# User has no activity, so considere the session has expired
elsif ( $conf->{timeoutActivity}
and $entry->{_lastSeen}
and $time - $entry->{_lastSeen} > $conf->{timeoutActivity} )
{
push @t, $id;
print "Session $id inactive\n" if $debug;
}
undef;
}
);
# Delete sessions
my @errors;
for my $id (@t) {
my $session = Lemonldap::NG::Common::Session->new(
storageModule => $options->{backend},
storageModuleOptions => $options,
cacheModule => $conf->{localSessionStorage},
cacheModuleOptions => $conf->{localSessionStorageOptions},
id => $id,
);
unless ( $session->data ) {
print "Error while opening session $id\n" if $debug;
print STDERR "Error on session $id\n";
$nb_error++;
push @errors, $id;
next;
}
unless ( $session->remove ) {
print "Error while deleting session $id\n" if $debug;
print STDERR "Error on session $id\n";
$nb_error++;
push @errors, $id;
next;
}
print "Session $id has been purged\n" if $debug;
$nb_purged++;
}
# Remove lock files for File backend
if ( $options->{backend} =~ /^Apache::Session::(?:Browseable::)?File$/i ) {
require Apache::Session::Lock::File;
my $l = new Apache::Session::Lock::File;
my $lock_directory = $options->{LockDirectory} || $options->{Directory};
$l->clean( $lock_directory, $conf->{timeout} );
}
# Force deletion of corrupted sessions for File backend
if ( $options->{backend} =~ /^Apache::Session::(?:Browseable::)?File$/i
and $force )
{
foreach (@errors) {
my $id = $_;
eval { unlink $options->{Directory} . "/$id"; };
if ($@) {
print STDERR "Unable to remove session $id\n";
}
else {
print STDERR "Session $id removed with force\n";
$nb_error--;
}
}
}
# Force deletion of corrupted sessions for DBI backend
if ( $options->{backend} =~
/^Apache::Session::(?:Browseable::)?(MySQL|Postgres|DBI|Oracle|Informix|MySQLJSON|PgHstore|PgJSON|SQLLite|Sybase)$/i
and $force )
{
my $dbi = DBI->connect_cached( $options->{DataSource},
$options->{UserName}, $options->{Password} );
my $table = $options->{TableName} || "sessions";
my $req = $dbi->prepare("DELETE from $table WHERE id=?");
foreach (@errors) {
my $id = $_;
my $res = $req->execute($id);
unless ( $res == 1 ) {
print STDERR "Fail to delete session $id with force\n";
}
else {
print STDERR "Session $id removed with force\n";
$nb_error--;
}
}
}
# Force deletion of corrupted sessions for LDAP backend
if ( $options->{backend} =~ /^Apache::Session::(?:Browseable::)?LDAP$/i
and $force )
{
my $ldap = Net::LDAP->new( $options->{ldapServer} );
my $bind = $ldap->bind( $options->{ldapBindDN},
password => $options->{ldapBindPassword} );
my $attrId = $options->{ldapAttributeId} | "cn";
foreach (@errors) {
my $id = $_;
my $sessionDn =
$attrId . "=" . $id . "," . $options->{ldapConfBase};
my $delete = $ldap->delete($sessionDn);
if ( $delete->is_error ) {
print STDERR "Fail to delete session $id with force\n";
}
else {
print STDERR "Session $id removed with force\n";
$nb_error--;
}
}
}
}
#=============================================================================
# Exit
#=============================================================================
print "$nb_purged sessions have been purged\n" if $debug;
print STDERR
"$nb_error sessions remaining, try to purge them with force (option -f)\n"
if $nb_error;
my $exit = $nb_error ? 1 : 0;
exit $exit;