2014-03-13 16:07:36 +01:00
|
|
|
|
## @file
|
|
|
|
|
# Main handler.
|
|
|
|
|
|
|
|
|
|
## @class
|
|
|
|
|
# Main handler.
|
|
|
|
|
# All methods in handler are class methods: in ModPerl environment, handlers
|
|
|
|
|
# are always launched without object created.
|
|
|
|
|
#
|
|
|
|
|
# The main method is run() who is called by Apache for each requests (using
|
|
|
|
|
# handler() wrapper).
|
|
|
|
|
#
|
|
|
|
|
# The initialization process is splitted in two parts :
|
|
|
|
|
# - init() is launched as Apache startup
|
|
|
|
|
# - globalInit() is launched at each first request received by an Apache child
|
|
|
|
|
# and each time a new configuration is detected
|
2014-05-21 19:44:24 +02:00
|
|
|
|
package Lemonldap::NG::Handler::SharedConf;
|
2014-03-13 16:07:36 +01:00
|
|
|
|
|
|
|
|
|
#use strict;
|
|
|
|
|
|
|
|
|
|
use Lemonldap::NG::Handler::Main qw(:all);
|
2014-06-27 16:17:13 +02:00
|
|
|
|
use Lemonldap::NG::Handler::Main::Logger;
|
2014-06-16 11:44:39 +02:00
|
|
|
|
use Lemonldap::NG::Handler::API qw(:httpCodes);
|
2014-06-30 11:35:32 +02:00
|
|
|
|
use Lemonldap::NG::Handler::Reload;
|
2014-03-13 16:07:36 +01:00
|
|
|
|
use Lemonldap::NG::Common::Conf; #link protected lmConf
|
|
|
|
|
use Lemonldap::NG::Common::Conf::Constants; #inherits
|
|
|
|
|
|
2014-06-27 16:17:13 +02:00
|
|
|
|
use base qw(Lemonldap::NG::Handler::Main);
|
2014-03-13 16:07:36 +01:00
|
|
|
|
|
2014-06-27 16:17:13 +02:00
|
|
|
|
our $VERSION = '1.4.0';
|
|
|
|
|
our $lmConf; # Lemonldap::NG::Common::Conf object to get config
|
|
|
|
|
our $localConfig; # Local configuration parameters, i.e. defined
|
|
|
|
|
# in lemonldap-ng.ini or in startup parameters
|
|
|
|
|
our $cfgNum = 0; # Number of the loaded remote configuration
|
|
|
|
|
our $lastCheck = 0; # Date of last configuration check (unix time)
|
|
|
|
|
our $checkTime = 600; # Time between 2 configuration check (in seconds);
|
|
|
|
|
# default value is 600, can be reset in local config
|
2014-03-13 16:07:36 +01:00
|
|
|
|
|
|
|
|
|
BEGIN {
|
2014-06-17 21:22:36 +02:00
|
|
|
|
Lemonldap::NG::Handler::API->thread_share($cfgNum);
|
2014-06-27 16:17:13 +02:00
|
|
|
|
Lemonldap::NG::Handler::API->thread_share($lastCheck);
|
|
|
|
|
Lemonldap::NG::Handler::API->thread_share($checkTime);
|
2014-06-17 21:22:36 +02:00
|
|
|
|
Lemonldap::NG::Handler::API->thread_share($lmConf);
|
|
|
|
|
Lemonldap::NG::Handler::API->thread_share($localConfig);
|
2014-03-13 16:07:36 +01:00
|
|
|
|
*EXPORT_TAGS = *Lemonldap::NG::Handler::Main::EXPORT_TAGS;
|
|
|
|
|
*EXPORT_OK = *Lemonldap::NG::Handler::Main::EXPORT_OK;
|
|
|
|
|
push(
|
|
|
|
|
@{ $EXPORT_TAGS{$_} },
|
2014-06-27 16:17:13 +02:00
|
|
|
|
qw($cfgNum $lastCheck $checkTime $lmConf $localConfig)
|
2014-03-13 16:07:36 +01:00
|
|
|
|
) foreach (qw(variables localStorage));
|
2014-06-27 16:17:13 +02:00
|
|
|
|
push @EXPORT_OK, qw($cfgNum $lastCheck $checkTime $lmConf $localConfig);
|
2014-03-13 16:07:36 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# INIT PROCESS
|
|
|
|
|
|
|
|
|
|
## @imethod void init(hashRef args)
|
2014-06-27 18:20:51 +02:00
|
|
|
|
# Read parameters and build the Lemonldap::NG::Common::Conf object.
|
2014-03-13 16:07:36 +01:00
|
|
|
|
# @param $args hash containing parameters
|
|
|
|
|
sub init($$) {
|
2014-06-27 18:20:51 +02:00
|
|
|
|
my ( $class, $args ) = @_;
|
2014-06-27 16:17:13 +02:00
|
|
|
|
|
2014-06-27 18:20:51 +02:00
|
|
|
|
# According to doc, localStorage can be declared in $args root,
|
|
|
|
|
# but it must be in $args->{configStorage}
|
2014-06-27 16:17:13 +02:00
|
|
|
|
foreach (qw(localStorage localStorageOptions)) {
|
|
|
|
|
$args->{configStorage}->{$_} ||= $args->{$_};
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-27 18:20:51 +02:00
|
|
|
|
$lmConf = Lemonldap::NG::Common::Conf->new($args->{configStorage});
|
|
|
|
|
die( "$class : unable to build configuration: "
|
|
|
|
|
. "$Lemonldap::NG::Common::Conf::msg" ) unless ($lmConf);
|
2014-06-27 16:17:13 +02:00
|
|
|
|
|
2014-06-27 18:20:51 +02:00
|
|
|
|
# Merge local configuration parameters so that params defined in
|
|
|
|
|
# startup parameters have precedence over lemonldap-ng.ini params
|
|
|
|
|
$localConfig = {
|
|
|
|
|
%{ $lmConf->getLocalConf(HANDLERSECTION) },
|
|
|
|
|
%{ $args }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$checkTime = $localConfig->{checkTime} || $checkTime;
|
|
|
|
|
|
|
|
|
|
# Few actions that must be done at server startup:
|
|
|
|
|
# * set server signature
|
|
|
|
|
$class->serverSignatureInit unless ($localConfig->{hideSignature});
|
|
|
|
|
# * launch status process
|
2014-06-30 11:18:00 +02:00
|
|
|
|
$class->statusInit($tsv) if ($localConfig->{status});
|
2014-03-13 16:07:36 +01:00
|
|
|
|
}
|
|
|
|
|
|
2014-06-27 18:20:51 +02:00
|
|
|
|
# @method void serverSignatureInit
|
|
|
|
|
# adapt server signature
|
|
|
|
|
sub serverSignatureInit {
|
|
|
|
|
my $class = shift;
|
|
|
|
|
Lemonldap::NG::Handler::API->setServerSignature(
|
2014-06-30 11:18:00 +02:00
|
|
|
|
"Lemonldap::NG/" . $Lemonldap::NG::Handler::VERSION
|
2014-06-27 18:20:51 +02:00
|
|
|
|
) if ( $Lemonldap::NG::Handler::VERSION );
|
|
|
|
|
}
|
2014-03-13 16:07:36 +01:00
|
|
|
|
|
2014-06-27 18:20:51 +02:00
|
|
|
|
## @ifn protected void statusInit()
|
|
|
|
|
# Launch the status process
|
|
|
|
|
sub statusInit {
|
2014-06-30 11:18:00 +02:00
|
|
|
|
my ( $class, $tsv ) = @_;
|
2014-06-27 18:20:51 +02:00
|
|
|
|
require IO::Pipe;
|
|
|
|
|
$statusPipe = IO::Pipe->new;
|
|
|
|
|
$statusOut = IO::Pipe->new;
|
|
|
|
|
if ( my $pid = fork() ) {
|
2014-06-30 11:18:00 +02:00
|
|
|
|
# TODO: log new process pid
|
2014-06-27 18:20:51 +02:00
|
|
|
|
$statusPipe->writer();
|
|
|
|
|
$statusOut->reader();
|
|
|
|
|
$statusPipe->autoflush(1);
|
2014-06-30 11:18:00 +02:00
|
|
|
|
( $tsv->{statusPipe}, $tsv->{statusOut} ) = ( $statusPipe, $statusOut );
|
2014-06-27 18:20:51 +02:00
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
$statusPipe->reader();
|
|
|
|
|
$statusOut->writer();
|
|
|
|
|
my $fdin = $statusPipe->fileno;
|
|
|
|
|
my $fdout = $statusOut->fileno;
|
|
|
|
|
open STDIN, "<&$fdin";
|
|
|
|
|
open STDOUT, ">&$fdout";
|
|
|
|
|
exec 'perl', '-MLemonldap::NG::Handler::Status',
|
2014-06-30 11:18:00 +02:00
|
|
|
|
map( { "-I$_" } @INC),
|
|
|
|
|
'-e &Lemonldap::NG::Handler::Status::run()';
|
2014-06-27 18:20:51 +02:00
|
|
|
|
}
|
2014-03-13 16:07:36 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# MAIN
|
|
|
|
|
|
2014-07-01 12:09:35 +02:00
|
|
|
|
# NB: function attribute ': method' requested to get $class
|
|
|
|
|
sub handler : method {
|
|
|
|
|
my ( $class, $request ) = @_;
|
|
|
|
|
Lemonldap::NG::Handler::API->newRequest($request);
|
2014-07-01 14:58:04 +02:00
|
|
|
|
$class->run();
|
2014-07-01 12:09:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sub logout : method {
|
|
|
|
|
my ( $class, $request ) = @_;
|
|
|
|
|
Lemonldap::NG::Handler::API->newRequest($request);
|
2014-07-01 14:58:04 +02:00
|
|
|
|
$class->unlog();
|
2014-07-01 12:09:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
2014-03-13 16:07:36 +01:00
|
|
|
|
## @rmethod int run(Apache2::RequestRec r)
|
|
|
|
|
# Check configuration and launch Lemonldap::NG::Handler::Main::run().
|
2014-06-27 16:17:13 +02:00
|
|
|
|
# Each $checkTime, the Apache child verify if its configuration is the same
|
2014-03-13 16:07:36 +01:00
|
|
|
|
# as the configuration stored in the local storage.
|
|
|
|
|
# @return Apache constant
|
2014-07-01 14:58:04 +02:00
|
|
|
|
sub run {
|
|
|
|
|
my $class = shift;
|
2014-06-27 16:17:13 +02:00
|
|
|
|
if ( time() - $lastCheck > $checkTime ) {
|
2014-03-13 16:07:36 +01:00
|
|
|
|
die("$class: No configuration found")
|
2014-06-27 16:17:13 +02:00
|
|
|
|
unless ( $class->checkConf );
|
2014-03-13 16:07:36 +01:00
|
|
|
|
}
|
2014-07-01 14:58:04 +02:00
|
|
|
|
return $class->SUPER::run;
|
2014-03-13 16:07:36 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# CONFIGURATION UPDATE
|
|
|
|
|
|
2014-06-27 16:17:13 +02:00
|
|
|
|
## @rmethod protected int checkConf(boolean force)
|
|
|
|
|
# Check if configuration is up to date, and reload it if needed.
|
|
|
|
|
# If the optional boolean $force is set to true,
|
|
|
|
|
# * cached configuration is ignored
|
|
|
|
|
# * and checkConf returns false if it fails to load remote config
|
|
|
|
|
# @param $force boolean
|
|
|
|
|
# @return true if config is up to date or if reload config succeeded
|
|
|
|
|
sub checkConf {
|
|
|
|
|
my ( $class, $force ) = @_;
|
|
|
|
|
my $conf = $lmConf->getConf( { local => !$force } );
|
|
|
|
|
|
2014-03-13 16:07:36 +01:00
|
|
|
|
unless ( ref($conf) ) {
|
|
|
|
|
Lemonldap::NG::Handler::Main::Logger->lmLog(
|
|
|
|
|
"$class: Unable to load configuration: $Lemonldap::NG::Common::Conf::msg",
|
|
|
|
|
'error'
|
|
|
|
|
);
|
2014-06-27 16:17:13 +02:00
|
|
|
|
return $force ? 0 : $cfgNum ? 1 : 0;
|
2014-03-13 16:07:36 +01:00
|
|
|
|
}
|
2014-06-27 16:17:13 +02:00
|
|
|
|
|
2014-03-13 16:07:36 +01:00
|
|
|
|
if ( !$cfgNum or $cfgNum != $conf->{cfgNum} ) {
|
|
|
|
|
Lemonldap::NG::Handler::Main::Logger->lmLog(
|
2014-06-19 15:29:01 +02:00
|
|
|
|
"Get configuration $conf->{cfgNum} ($Lemonldap::NG::Common::Conf::msg)",
|
2014-03-13 16:07:36 +01:00
|
|
|
|
'debug'
|
|
|
|
|
);
|
2014-06-27 16:17:13 +02:00
|
|
|
|
$lastCheck = time();
|
|
|
|
|
$cfgNum = $conf->{cfgNum};
|
|
|
|
|
$conf->{$_} = $localConfig->{$_} foreach ( keys %$localConfig );
|
2014-06-30 20:34:23 +02:00
|
|
|
|
Lemonldap::NG::Handler::Reload->configReload( $conf, $tsv );
|
2014-03-13 16:07:36 +01:00
|
|
|
|
}
|
|
|
|
|
Lemonldap::NG::Handler::Main::Logger->lmLog(
|
|
|
|
|
"$class: configuration is up to date", 'debug' );
|
2014-06-27 16:17:13 +02:00
|
|
|
|
return 1;
|
2014-03-13 16:07:36 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# RELOAD SYSTEM
|
|
|
|
|
|
2014-06-19 19:04:02 +02:00
|
|
|
|
*refresh = *reload;
|
2014-03-13 16:07:36 +01:00
|
|
|
|
|
2014-07-01 14:58:04 +02:00
|
|
|
|
## @rmethod int reload
|
2014-06-27 16:17:13 +02:00
|
|
|
|
# Launch checkConf() with $local=0, so remote configuration is tested.
|
2014-03-13 16:07:36 +01:00
|
|
|
|
# Then build a simple HTTP response that just returns "200 OK" or
|
|
|
|
|
# "500 Server Error".
|
|
|
|
|
# @return Apache constant (OK or SERVER_ERROR)
|
2014-07-01 14:58:04 +02:00
|
|
|
|
sub reload {
|
|
|
|
|
my $class = shift;
|
2014-03-13 16:07:36 +01:00
|
|
|
|
Lemonldap::NG::Handler::Main::Logger->lmLog(
|
2014-06-19 19:04:02 +02:00
|
|
|
|
"Request for configuration reload", 'notice' );
|
2014-06-27 16:17:13 +02:00
|
|
|
|
return $class->checkConf(1) ? DONE : SERVER_ERROR;
|
2014-03-13 16:07:36 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
1;
|
|
|
|
|
__END__
|
|
|
|
|
|
|
|
|
|
=head1 NAME
|
|
|
|
|
|
|
|
|
|
=encoding utf8
|
|
|
|
|
|
2014-05-21 19:44:24 +02:00
|
|
|
|
Lemonldap::NG::Handler::SharedConf - Perl extension to use dynamic
|
2014-03-13 16:07:36 +01:00
|
|
|
|
configuration provide by Lemonldap::NG::Manager.
|
|
|
|
|
|
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
|
|
|
|
|
|
package My::Package;
|
2014-05-21 19:44:24 +02:00
|
|
|
|
use Lemonldap::NG::Handler::SharedConf;
|
|
|
|
|
@ISA = qw(Lemonldap::NG::Handler::SharedConf);
|
2014-03-13 16:07:36 +01:00
|
|
|
|
__PACKAGE__->init ( {
|
|
|
|
|
localStorage => "Cache::FileCache",
|
|
|
|
|
localStorageOptions => {
|
|
|
|
|
'namespace' => 'lemonldap-ng',
|
|
|
|
|
'default_expires_in' => 600,
|
|
|
|
|
},
|
|
|
|
|
configStorage => {
|
|
|
|
|
type => "DBI"
|
|
|
|
|
dbiChain => "DBI:mysql:database=$database;host=$hostname;port=$port",
|
|
|
|
|
dbiUser => "lemonldap",
|
|
|
|
|
dbiPassword => "password",
|
|
|
|
|
},
|
|
|
|
|
} );
|
|
|
|
|
|
|
|
|
|
Call your package in /apache-dir/conf/httpd.conf :
|
|
|
|
|
|
|
|
|
|
PerlRequire MyFile
|
|
|
|
|
# TOTAL PROTECTION
|
|
|
|
|
PerlHeaderParserHandler My::Package
|
|
|
|
|
# OR SELECTED AREA
|
|
|
|
|
<Location /protected-area>
|
|
|
|
|
PerlHeaderParserHandler My::Package
|
|
|
|
|
</Location>
|
|
|
|
|
|
|
|
|
|
The configuration is loaded only at Apache start. Create an URI to force
|
|
|
|
|
configuration reload, so you don't need to restart Apache at each change :
|
|
|
|
|
|
|
|
|
|
# /apache-dir/conf/httpd.conf
|
|
|
|
|
<Location /location/that/I/ve/choosed>
|
|
|
|
|
Order deny,allow
|
|
|
|
|
Deny from all
|
|
|
|
|
Allow from my.manager.com
|
|
|
|
|
PerlHeaderParserHandler My::Package->refresh
|
|
|
|
|
</Location>
|
|
|
|
|
|
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
|
|
|
|
|
|
This library inherit from L<Lemonldap::NG::Handler::Main> to build a
|
|
|
|
|
complete SSO Handler System: a central database contains the policy of your
|
|
|
|
|
domain. People that want to access to a protected applications are redirected
|
|
|
|
|
to the portal that run L<Lemonldap::NG::Portal::SharedConf>. After reading
|
|
|
|
|
configuration from the database and authenticating the user, it stores a key
|
|
|
|
|
word for each application the user is granted to access to.
|
|
|
|
|
Then the user is redirected to the application he wanted to access and the
|
2014-05-21 19:44:24 +02:00
|
|
|
|
Apache handler build with L<Lemonldap::NG::Handler::SharedConf::DBI> has just
|
2014-03-13 16:07:36 +01:00
|
|
|
|
to verify that the keyword corresponding to the protected area is stored in
|
|
|
|
|
the database.
|
|
|
|
|
|
|
|
|
|
=head2 OVERLOADED SUBROUTINES
|
|
|
|
|
|
|
|
|
|
=head3 init
|
|
|
|
|
|
|
|
|
|
Like L<Lemonldap::NG::Handler::Main>::init() but read only localStorage
|
|
|
|
|
related options. You may change default time between two configuration checks
|
2014-06-27 16:17:13 +02:00
|
|
|
|
with the C<checkTime> parameter (default 600s).
|
2014-03-13 16:07:36 +01:00
|
|
|
|
|
|
|
|
|
=head1 OPERATION
|
|
|
|
|
|
|
|
|
|
Each new Apache child checks if there's a configuration stored in the local
|
|
|
|
|
store. If not, it calls getConf to get one and store it in the local store by
|
|
|
|
|
calling setconf.
|
|
|
|
|
|
|
|
|
|
Every 600 seconds, each Apache child checks if the local stored configuration
|
|
|
|
|
has changed and reload it if it has.
|
|
|
|
|
|
|
|
|
|
When refresh subroutine is called (by http for example: see synopsis), getConf
|
|
|
|
|
is called to get the new configuration and setconf is called to store it in the
|
|
|
|
|
local store.
|
|
|
|
|
|
|
|
|
|
=head1 SEE ALSO
|
|
|
|
|
|
|
|
|
|
L<Lemonldap::NG::Handler>, L<Lemonldap::NG::Manager>, L<Lemonldap::NG::Portal>,
|
|
|
|
|
L<http://lemonldap-ng.org/>
|
|
|
|
|
|
|
|
|
|
=head1 AUTHOR
|
|
|
|
|
|
|
|
|
|
=over
|
|
|
|
|
|
|
|
|
|
=item Clement Oudot, E<lt>clem.oudot@gmail.comE<gt>
|
|
|
|
|
|
|
|
|
|
=item Fran<EFBFBD>ois-Xavier Deltombe, E<lt>fxdeltombe@gmail.com.E<gt>
|
|
|
|
|
|
|
|
|
|
=item Xavier Guimard, E<lt>x.guimard@free.frE<gt>
|
|
|
|
|
|
|
|
|
|
=back
|
|
|
|
|
|
|
|
|
|
=head1 BUG REPORT
|
|
|
|
|
|
|
|
|
|
Use OW2 system to report bug or ask for features:
|
|
|
|
|
L<http://jira.ow2.org>
|
|
|
|
|
|
|
|
|
|
=head1 DOWNLOAD
|
|
|
|
|
|
|
|
|
|
Lemonldap::NG is available at
|
|
|
|
|
L<http://forge.objectweb.org/project/showfiles.php?group_id=274>
|
|
|
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE
|
|
|
|
|
|
|
|
|
|
=over
|
|
|
|
|
|
|
|
|
|
=item Copyright (C) 2006, 2007, 2008, 2009, 2010, 2013 by Xavier Guimard, E<lt>x.guimard@free.frE<gt>
|
|
|
|
|
|
|
|
|
|
=item Copyright (C) 2012 by Fran<EFBFBD>ois-Xavier Deltombe, E<lt>fxdeltombe@gmail.com.E<gt>
|
|
|
|
|
|
|
|
|
|
=item Copyright (C) 2006, 2008, 2009, 2010, 2011, 2012 by Clement Oudot, E<lt>clem.oudot@gmail.comE<gt>
|
|
|
|
|
|
|
|
|
|
=back
|
|
|
|
|
|
|
|
|
|
This library 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 2, or (at your option)
|
|
|
|
|
any later version.
|
|
|
|
|
|
|
|
|
|
This program 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 this program. If not, see L<http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
|
|
=cut
|