diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index c149e29..cc814bf 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -4,7 +4,7 @@ env: VERSION: 2.19.1 PGVERSION: 15 DEBIANRELEASE: bookworm - DOCKERREVISION: 3 + DOCKERREVISION: 4 on: push: diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index f972703..0d29621 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -8,7 +8,7 @@ env: on: push: - branches-ignore: [master,stable] + branches-ignore: [master,stable,experimental] jobs: test: diff --git a/Changes.md b/Changes.md index 1ed19a9..6b9f517 100644 --- a/Changes.md +++ b/Changes.md @@ -1,5 +1,6 @@ # Changes +* 2024-08-14: add message-broker * 2024-08-16(v2.19.1-3): * add `TLS_CERT_FILE` and `TLS_KEY_FILE` variables (v2.19.1-3) * preserve requests in RELAY diff --git a/base-no-s6/Dockerfile b/base-no-s6/Dockerfile index 4176410..996d522 100644 --- a/base-no-s6/Dockerfile +++ b/base-no-s6/Dockerfile @@ -79,7 +79,9 @@ RUN echo "# Install packages from ${DEBIAN_VERSION} (${LLNGDIST})" && \ chown www-data:www-data /var/lib/lemonldap-ng/cache && \ chmod 750 /var/lib/lemonldap-ng/cache && \ perl -000 -MJSON -i -ne '$_=JSON::from_json($_);$_->{reloadUrls}={};print JSON->new->pretty->canonical->encode($_)' /var/lib/lemonldap-ng/conf/lmConf-1.json && \ + perl -i -pe 's/\r//g' /usr/share/perl5/Lemonldap/NG/Common/Conf/DefaultValues.pm && \ echo "patch session-cli.patch" && patch -p1 {refLocalStorage} ) { +- $self->setDefault($conf); +- $self->setValuesFromEnv($conf); +- $self->compactConf($conf); ++ $self->setDefault($conf); ++ $self->setValuesFromEnv($conf); ++ $self->compactConf($conf); ++ ++ if ( Lemonldap::NG::Handler::Main->can('tsv') ++ and Lemonldap::NG::Handler::Main->tsv->{msgBrokerWriter} ) ++ { ++ Lemonldap::NG::Handler::Main->tsv->{msgBrokerWriter} ++ ->publish( $conf->{eventQueueName}, { action => 'newConf' } ); ++ } ++ else { + eval { Lemonldap::NG::Handler::Main->reload() }; + } + +@@ -234,8 +241,9 @@ sub getConf { + + # Store configuration in cache + $self->setLocalConf($r) +- if ( $self->{refLocalStorage} +- and not( $noCache == 1 or $raw ) ); ++ if ( $self->{refLocalStorage} ++ and ( !$noCache or $noCache != 1 ) ++ and !$raw ); + } + } + +--- a/usr/share/perl5/Lemonldap/NG/Common/Conf/DefaultValues.pm ++++ b/usr/share/perl5/Lemonldap/NG/Common/Conf/DefaultValues.pm +@@ -38,6 +38,7 @@ sub defaultValues { + 'checkDevOpsDownload' => 1, + 'checkHIBPRequired' => 1, + 'checkHIBPURL' => 'https://api.pwnedpasswords.com/range/', ++ 'checkMsg' => 5, + 'checkTime' => 600, + 'checkUserDisplayComputedSession' => 1, + 'checkUserDisplayEmptyHeaders' => 0, +@@ -82,6 +83,7 @@ sub defaultValues { + }, + 'displaySessionId' => 1, + 'domain' => 'example.com', ++ 'eventQueueName' => 'llng_events', + 'exportedVars' => { + 'UA' => 'HTTP_USER_AGENT' + }, +@@ -193,6 +195,7 @@ sub defaultValues { + 'managerPassword' => '', + 'max2FDevices' => 10, + 'max2FDevicesNameLength' => 20, ++ 'messageBrokerOptions' => {}, + 'multiValuesSeparator' => '; ', + 'mySessionAuthorizedRWKeys' => [ + '_appsListOrder', +diff --git a/usr/share/perl5/Lemonldap/NG/Common/Logger/MessageBroker.pm b/usr/share/perl5/Lemonldap/NG/Common/Logger/MessageBroker.pm +new file mode 100644 +index 000000000..3aeec8433 +--- /dev/null ++++ b/usr/share/perl5/Lemonldap/NG/Common/Logger/MessageBroker.pm +@@ -0,0 +1,40 @@ ++package Lemonldap::NG::Common::Logger::MessageBroker; ++ ++use strict; ++ ++our $VERSION = '2.0.18'; ++ ++sub new { ++ my $self = bless {}, shift; ++ my ( $conf, %args ) = @_; ++ my $show = 1; ++ die 'Missing conf->loggerBroker' unless $conf->{loggerBroker}; ++ $conf->{loggerBroker} =~ s/^::/Lemonldap::NG::Common::MessageBroker::/; ++ my $brokerChannel = $conf->{loggerBrokerChannel} ++ || ( $args{user} ? 'llng-userlogs' : 'llng-logs' ); ++ my $type = $args{user} ? 'logs' : 'userLogs'; ++ eval "use $conf->{loggerBroker}"; ++ die "Unable to load $conf->{loggerBroker}: $@" if $@; ++ $self->{broker} = $conf->{loggerBroker}->new( $conf->{loggerBrokerOpts} ) ++ or die 'Unable to create message broker connector'; ++ ++ foreach (qw(error warn notice info debug)) { ++ if ($show) { ++ eval qq'sub $_ {\$_[0]->{broker}->publish("$brokerChannel", { ++ type => "$type", ++ data => \$_[1], ++ time => time, ++ level => "$_", ++ })}'; ++ die $@ if ($@); ++ } ++ else { ++ eval qq'sub $_ {1}'; ++ } ++ $show = 0 if ( $conf->{logLevel} eq $_ ); ++ } ++ die "Unknown logLevel $conf->{logLevel}" if $show; ++ return $self; ++} ++ ++1; +diff --git a/usr/share/perl5/Lemonldap/NG/Common/MessageBroker/MQTT.pm b/usr/share/perl5/Lemonldap/NG/Common/MessageBroker/MQTT.pm +new file mode 100644 +index 000000000..0416d4df5 +--- /dev/null ++++ b/usr/share/perl5/Lemonldap/NG/Common/MessageBroker/MQTT.pm +@@ -0,0 +1,96 @@ ++package Lemonldap::NG::Common::MessageBroker::MQTT; ++ ++use strict; ++use Net::MQTT::Simple; ++ ++our $VERSION = '2.20.0'; ++ ++sub new { ++ my ( $class, $conf, $logger ) = @_; ++ my $args = $conf->{messageBrokerOptions}; ++ $args //= {}; ++ $args->{server} ||= 'localhost:1883'; ++ my $mqtt; ++ if ( $args->{ssl} ) { ++ require Net::MQTT::Simple::SSL; ++ my $h = {}; ++ $h->{$_} = $args->{$_} ++ foreach (qw(SSL_ca_file SSL_cert_file SSL_key_file)); ++ $mqtt = Net::MQTT::Simple::SSL->new( $args->{server}, $h ); ++ if ( $args->{user} ) { ++ $mqtt->login( $args->{user}, $args->{password} ); ++ } ++ } ++ else { ++ $mqtt = Net::MQTT::Simple->new( $args->{server} ); ++ } ++ unless ($mqtt) { ++ $logger->error("Unable to connect to MQTT server $@$!"); ++ return; ++ } ++ my $self = bless { mqtt => $mqtt, _ch => [], logger => $logger }, $class; ++ return $self; ++} ++ ++sub publish { ++ my ( $self, $channel, $msg ) = @_; ++ die 'Not a hash msg' unless ref $msg eq 'HASH'; ++ my $j = eval { JSON::to_json($msg) }; ++ die "MessageBroker publish only hashes! $@" if $@; ++ $self->{mqtt}->publish( "llng/$channel", $j ); ++} ++ ++sub subscribe { ++ my ( $self, $channel ) = @_; ++ $self->{messages}{$channel} = []; ++ $self->{mqtt}->subscribe( ++ "llng/$channel", ++ sub { ++ return unless $_[1]; ++ $_[0] =~ s#llng/##; ++ my $tmp = eval { JSON::from_json( $_[1] ) }; ++ if ($@) { ++ $self->{logger}->error("Bad message from MQTT server: $@") ++ } ++ else { ++ push @{ $self->{messages}{ $_[0] } }, $tmp; ++ } ++ } ++ ); ++ push @{ $self->{_ch} }, "llng/$channel"; ++} ++ ++sub DESTROY { ++ my ($self) = @_; ++ eval { ++ ( $self->{mqtt} && $self->{mqtt}->unsubscribe($_) ) ++ foreach ( @{ $self->{_ch} } ); ++ }; ++ $self->{logger}->error($@) if $@; ++} ++ ++sub getNextMessage { ++ my ( $self, $channel, $delay ) = @_; ++ return undef unless $self->{messages}{$channel}; ++ return shift( @{ $self->{messages}{$channel} } ) ++ if @{ $self->{messages}{$channel} }; ++ $self->{mqtt}->tick( $delay // 0 ); ++ return shift( @{ $self->{messages}{$channel} } ) ++ if @{ $self->{messages}{$channel} }; ++ return; ++} ++ ++sub waitForNextMessage { ++ my ( $self, $channel ) = @_; ++ return undef ++ unless $self->{messages}{$channel}; ++ ++ # Infinite loop until one message is seen ++ my $res; ++ while ( !$res ) { ++ $res = $self->{redis}->getNextMessage( $channel, 1 ); ++ } ++ return $res; ++} ++ ++1; +diff --git a/usr/share/perl5/Lemonldap/NG/Common/MessageBroker/NoBroker.pm b/usr/share/perl5/Lemonldap/NG/Common/MessageBroker/NoBroker.pm +new file mode 100644 +index 000000000..f4257fa98 +--- /dev/null ++++ b/usr/share/perl5/Lemonldap/NG/Common/MessageBroker/NoBroker.pm +@@ -0,0 +1,59 @@ ++package Lemonldap::NG::Common::MessageBroker::NoBroker; ++ ++# This pseudo message broker only dispatch messages into current node. ++# It also inserts periodically "newConf" into llng_events to permit- to ++# detect configuration changes usng the checkTime parameter. ++ ++use strict; ++use Lemonldap::NG::Common::Conf::Constants; ++ ++our $VERSION = '2.20.0'; ++ ++our $lastCheck = time; ++ ++our $channels = {}; ++ ++sub new { ++ my ( $class, $conf, $logger ) = @_; ++ $channels->{ $conf->{eventQueueName} } //= []; ++ return bless { ++ checkTime => $conf->{checkTime}, ++ eventQueueName => $conf->{eventQueueName}, ++ logger => $logger, ++ }, $class; ++} ++ ++sub publish { ++ my ( $self, $channel, $msg ) = @_; ++ die unless $channel and $msg; ++ $channels->{$channel} = [] ++ unless ref( $channels->{$channel} ); ++ push @{ $channels->{$channel} }, $msg; ++} ++ ++sub subscribe { } ++ ++sub getNextMessage { ++ my ( $self, $channel, $delay ) = @_; ++ if ( time >= $lastCheck + $self->{checkTime} ) { ++ $self->publish( $self->{eventQueueName}, { action => 'newConf' } ); ++ $lastCheck = time; ++ } ++ if ( ref( $channels->{$channel} ) ++ and @{ $channels->{$channel} } ) ++ { ++ return shift @{ $channels->{$channel} }; ++ } ++} ++ ++sub waitForNextMessage { ++ my ( $self, $channel ) = @_; ++ while (1) { ++ if ( my $msg = $self->getNextMessage($channel) ) { ++ return $msg; ++ } ++ sleep 1; ++ } ++} ++ ++1; +diff --git a/usr/share/perl5/Lemonldap/NG/Common/MessageBroker/Pg.pm b/usr/share/perl5/Lemonldap/NG/Common/MessageBroker/Pg.pm +new file mode 100644 +index 000000000..e15e9d0f4 +--- /dev/null ++++ b/usr/share/perl5/Lemonldap/NG/Common/MessageBroker/Pg.pm +@@ -0,0 +1,74 @@ ++package Lemonldap::NG::Common::MessageBroker::Pg; ++ ++use strict; ++use DBI; ++use JSON; ++ ++our $VERSION = '2.20.0'; ++ ++sub new { ++ my ( $class, $conf, $logger ) = @_; ++ my $args = $conf->{messageBrokerOptions}; ++ unless ($args ++ and $args->{dbiChain} ++ and $args->{dbiUser} ++ and $args->{dbiPassword} ) ++ { ++ $logger->error('MISSING OPTIONS FOR PG PUB/SUB'); ++ return undef; ++ } ++ my $self = bless { %{$args}, logger => $logger }, $class; ++ return $self; ++} ++ ++sub publish { ++ my ( $self, $channel, $msg ) = @_; ++ die 'Not a hash msg' unless ref $msg eq 'HASH'; ++ my $j = eval { JSON::to_json($msg) }; ++ die "MessageBroker publish only hashes! $@" if $@; ++ $self->_dbh->do( "NOTIFY $channel, ?", undef, $j ); ++} ++ ++sub subscribe { ++ my ( $self, $channel ) = @_; ++ $self->{messages}{$channel} = []; ++ $self->_dbh->do("LISTEN $channel"); ++} ++ ++sub getNextMessage { ++ my ( $self, $channel, $delay ) = @_; ++ return undef ++ unless $self->{messages}{$channel}; ++ if ( my $notify = $self->_dbh->pg_notifies ) { ++ my ( $name, $pid, $payload ) = @$notify; ++ $payload = eval { JSON::from_json($payload) }; ++ if ($@) { ++ $self->{logger}->error("Bad message from Pg: $@"); ++ } ++ else { ++ push @{ $self->{messages}{$name} }, $payload; ++ } ++ } ++ return shift( @{ $self->{messages}{$channel} } ) ++ if @{ $self->{messages}{$channel} }; ++} ++ ++sub waitForNextMessage { ++ my ( $self, $channel ) = @_; ++ return undef unless $self->{messages}{$channel}; ++ ++ # Infinite loop until one message is seen ++ my $res; ++ while ( not( $res = $self->getNextMessage($channel) ) ) { ++ sleep 1; ++ } ++} ++ ++sub _dbh { ++ my ($self) = @_; ++ return $self->{_dbh} if ( $self->{_dbh} and $self->{_dbh}->ping ); ++ $self->{_dbh} = DBI->connect_cached( $self->{dbiChain}, $self->{dbiUser}, ++ $self->{dbiPassword}, { RaiseError => 1, AutoCommit => 1, } ); ++} ++ ++1; +diff --git a/usr/share/perl5/Lemonldap/NG/Common/MessageBroker/Redis.pm b/usr/share/perl5/Lemonldap/NG/Common/MessageBroker/Redis.pm +new file mode 100644 +index 000000000..e444395b7 +--- /dev/null ++++ b/usr/share/perl5/Lemonldap/NG/Common/MessageBroker/Redis.pm +@@ -0,0 +1,71 @@ ++package Lemonldap::NG::Common::MessageBroker::Redis; ++ ++use strict; ++use Redis; ++use JSON; ++ ++our $VERSION = '2.20.0'; ++ ++sub new { ++ my ( $class, $conf, $logger ) = @_; ++ my $self = bless { logger => $logger }, $class; ++ my $args = $conf->{messageBrokerOptions} // {}; ++ ++ # Reconnection parameters ++ # - try to reconnect every 1s up to 60s ++ $args->{reconnect} //= 60; ++ $args->{every} //= 1000000; ++ $self->{redis} = Redis->new(%$args); ++ $self->{messages} = {}; ++ return $self; ++} ++ ++sub publish { ++ my ( $self, $channel, $msg ) = @_; ++ die 'Not a hash msg' unless ref $msg eq 'HASH'; ++ my $j = eval { JSON::to_json($msg) }; ++ die "MessageBroker publish only hashes! $@" if $@; ++ $self->{redis}->publish( $channel, $j ); ++} ++ ++sub subscribe { ++ my ( $self, $channel ) = @_; ++ $self->{messages}{$channel} = []; ++ $self->{redis}->subscribe( ++ $channel, ++ sub { ++ my $tmp = eval { JSON::from_json( $_[0] ) }; ++ if ($@) { ++ $self->{logger}->error("Bad message from Redis: $@"); ++ } ++ else { ++ push @{ $self->{messages}{$channel} }, $tmp; ++ } ++ } ++ ); ++} ++ ++sub getNextMessage { ++ my ( $self, $channel, $delay ) = @_; ++ return undef ++ unless $self->{messages}{$channel}; ++ return shift( @{ $self->{messages}{$channel} } ) ++ if @{ $self->{messages}{$channel} }; ++ $self->{redis}->wait_for_messages( $delay || 0.001 ); ++ return shift( @{ $self->{messages}{$channel} } ) ++ if @{ $self->{messages}{$channel} }; ++ return; ++} ++ ++sub waitForNextMessage { ++ my ( $self, $channel ) = @_; ++ return undef ++ unless $self->{messages}{$channel}; ++ ++ # Infinite loop until one message is seen ++ $self->{redis}->wait_for_messages(1) ++ while ( !@{ $self->{messages}{$channel} } ); ++ return shift( @{ $self->{messages}{$channel} } ); ++} ++ ++1; +diff --git a/usr/share/perl5/Lemonldap/NG/Handler/Main/Init.pm b/usr/share/perl5/Lemonldap/NG/Handler/Main/Init.pm +index 783799e61..fb777e5f0 100644 +--- a/usr/share/perl5/Lemonldap/NG/Handler/Main/Init.pm ++++ b/usr/share/perl5/Lemonldap/NG/Handler/Main/Init.pm +@@ -33,6 +33,7 @@ sub init($$) { + { %{ $class->confAcc->getLocalConf('handler') }, %{$args} } ); + + $class->checkTime( $class->localConfig->{checkTime} || $class->checkTime ); ++ $class->checkMsg( $class->localConfig->{checkMsg} || $class->checkMsg ); + + # Few actions that must be done at server startup: + # * set log level for Lemonldap::NG logs +diff --git a/usr/share/perl5/Lemonldap/NG/Handler/Main/MsgActions.pm b/usr/share/perl5/Lemonldap/NG/Handler/Main/MsgActions.pm +new file mode 100644 +index 000000000..9ae96fd81 +--- /dev/null ++++ b/usr/share/perl5/Lemonldap/NG/Handler/Main/MsgActions.pm +@@ -0,0 +1,34 @@ ++package Lemonldap::NG::Handler::Main::MsgActions; ++ ++use strict; ++use Exporter 'import'; ++ ++our @EXPORT = qw(msgActions addMsgAction delMsgAction); ++ ++our $msgActions = { ++ newConf => sub { ++ my ( $class, $msg, $req ) = @_; ++ unless ( $class->checkConf() ) { ++ $class->logger->error("$class: No configuration found"); ++ $req->data->{noTry} = 1; ++ } ++ }, ++ unlog => sub { ++ my ( $class, $msg, $req ) = @_; ++ $class->localUnlog( $req, $msg->{id} ); ++ }, ++}; ++ ++sub msgActions { return $msgActions } ++ ++sub addMsgAction { ++ my ( $name, $sub ) = @_; ++ $msgActions->{$name} = $sub; ++} ++ ++sub delMsgAction { ++ my ($name) = @_; ++ delete $msgActions->{$name}; ++} ++ ++1; +diff --git a/usr/share/perl5/Lemonldap/NG/Handler/Main/Reload.pm b/usr/share/perl5/Lemonldap/NG/Handler/Main/Reload.pm +index 9fcb0c14f..fda61fd5b 100644 +--- a/usr/share/perl5/Lemonldap/NG/Handler/Main/Reload.pm ++++ b/usr/share/perl5/Lemonldap/NG/Handler/Main/Reload.pm +@@ -37,6 +37,8 @@ sub checkConf { + my ( $class, $force ) = @_; + $class->logger->debug("Check configuration for $class"); + my $prm = { local => !$force, localPrm => $class->localConfig }; ++ my $cfgNum = $class->confAcc->lastCfg(); ++ $prm->{local} = 0 if $class->cfgNum and $cfgNum != $class->cfgNum; + my $conf = $class->confAcc->getConf($prm); + chomp $Lemonldap::NG::Common::Conf::msg; + +@@ -93,6 +95,7 @@ sub checkConf { + } + } + $class->checkTime( $conf->{checkTime} ) if $conf->{checkTime}; ++ $class->checkMsg( $conf->{checkMsg} ) if $conf->{checkMsg}; + $class->lastCheck( time() ); + $class->logger->debug("$class: configuration is up to date"); + return 1; +@@ -164,6 +167,11 @@ sub reload { + # - outputPostData + # - aliasInit(): + # - vhostAlias ++# - oauth2Init(): ++# - oauth2Options ++# - msgBrokerInit(): ++# - msgBrokerWriter ++# - msgBrokerReader + # + # The *Init() methods can be run in any order, + # but jailInit must be run first because $tsv->{jail} +@@ -178,7 +186,8 @@ sub configReload { + + foreach my $sub ( + qw( defaultValuesInit jailInit portalInit locationRulesInit +- sessionStorageInit headersInit postUrlInit aliasInit oauth2Init ) ++ sessionStorageInit headersInit postUrlInit aliasInit ++ oauth2Init msgBrokerInit ) + ) + { + $class->logger->debug("Process $$ calls $sub"); +@@ -217,7 +226,7 @@ sub defaultValuesInit { + useSafeJail httpOnly whatToTrace handlerInternalCache + handlerServiceTokenTTL customToTrace lwpOpts lwpSslOpts + authChoiceAuthBasic authChoiceParam upgradeSession +- hashedSessionStore ++ hashedSessionStore eventQueueName + ) + ); + +@@ -616,6 +625,18 @@ sub oauth2Init { + return 1; + } + ++sub msgBrokerInit { ++ my ( $class, $conf ) = @_; ++ ++ my $brokerClass = $conf->{messageBroker} || '::NoBroker'; ++ $brokerClass =~ s/^::/Lemonldap::NG::Common::MessageBroker::/; ++ eval "require $brokerClass"; ++ die $@ if $@; ++ $class->tsv->{msgBrokerReader} = $brokerClass->new($conf, $class->logger); ++ $class->tsv->{msgBrokerReader}->subscribe( $conf->{eventQueueName} ); ++ $class->tsv->{msgBrokerWriter} = $brokerClass->new($conf, $class->logger); ++} ++ + sub substitute { + my ( $class, $expr ) = @_; + $expr //= ''; +diff --git a/usr/share/perl5/Lemonldap/NG/Handler/Main/Run.pm b/usr/share/perl5/Lemonldap/NG/Handler/Main/Run.pm +index bbeb94675..6df0229fd 100644 +--- a/usr/share/perl5/Lemonldap/NG/Handler/Main/Run.pm ++++ b/usr/share/perl5/Lemonldap/NG/Handler/Main/Run.pm +@@ -12,6 +12,7 @@ use MIME::Base64; + use URI; + use URI::Escape; + use Lemonldap::NG::Common::Session; ++use Lemonldap::NG::Handler::Main::MsgActions; + + # Methods that must be overloaded + +@@ -80,15 +81,17 @@ sub getStatus { + + # Method that must be called by base packages (Handler::ApacheMP2,...) to get + # type of handler to call (Main, AuthBasic,...) ++ + sub checkType { + my ( $class, $req ) = @_; + +- if ( time() - $class->lastCheck > $class->checkTime ) { +- unless ( $class->checkConf ) { +- $class->logger->error("$class: No configuration found"); +- $req->data->{noTry} = 1; +- return 'Fail'; +- } ++ # Always launch "newConf" task if never started ++ msgActions->{newConf}->( $class, {}, $req ) ++ unless $class->tsv and %{ $class->tsv }; ++ ++ # Check for event in events queue every 5 seconds ++ if ( time - $class->lastCheckMsg > $class->checkMsg ) { ++ defined( $class->checkEvent($req) ) or return 'Fail'; + } + my $vhost = $class->resolveAlias($req); + return ( defined $class->tsv->{type}->{$vhost} ) +@@ -96,6 +99,58 @@ sub checkType { + : 'Main'; + } + ++# Method to check for event in events queue ++sub checkEvent { ++ my ( $class, $req, $delay ) = @_; ++ $class->logger->debug('Checking for events'); ++ unless ( $class->tsv->{msgBrokerReader} ) { ++ $class->logger->error('Not initialized'); ++ return; ++ } ++ my $ret = ''; ++ while ( my $msg = ++ $class->tsv->{msgBrokerReader} ++ ->getNextMessage( $class->tsv->{eventQueueName}, $delay ) ) ++ { ++ if ( $msg->{action} ) { ++ $class->logger->debug("Processing event $msg->{action}"); ++ if ( my $sub = msgActions->{ $msg->{action} } ) { ++ $sub->( $class, $msg, $req ); ++ $ret = $msg->{action}; ++ } ++ else { ++ $class->logger->error("Unkown action $msg->{action}"); ++ } ++ } ++ else { ++ $class->logger->error( ++ 'Malformed message: ' . JSON::to_json($msg) ); ++ } ++ } ++ $class->lastCheckMsg(time); ++ return $ret; ++} ++ ++# Method to push an event into message queue. ++sub publishEvent { ++ my ( $class, $req, $msg ) = @_; ++ die unless $msg; ++ $class->logger->debug("Publishing event $msg->{action}"); ++ unless ( $class->tsv->{msgBrokerWriter} ) { ++ $class->logger->error('Not initialized'); ++ return; ++ } ++ $class->tsv->{msgBrokerWriter} ++ ->publish( $class->tsv->{eventQueueName}, $msg ); ++ my $ret; ++ my $start = time; ++ ++ # After pushing message, let's pop message queue for this node and ++ # then launch the concerning method ++ do { $ret = $class->checkEvent( $req, 0.1 ) } ++ while ( ( !$ret or $ret ne $msg->{action} ) and time < $start + 1 ); ++} ++ + ## @rmethod int run + # Check configuration and launch Lemonldap::NG::Handler::Main::run(). + # Each $checkTime, server child verifies if its configuration is the same +diff --git a/usr/share/perl5/Lemonldap/NG/Handler/Main/SharedVariables.pm b/usr/share/perl5/Lemonldap/NG/Handler/Main/SharedVariables.pm +index 2e7b38cb5..627aab5b2 100644 +--- a/usr/share/perl5/Lemonldap/NG/Handler/Main/SharedVariables.pm ++++ b/usr/share/perl5/Lemonldap/NG/Handler/Main/SharedVariables.pm +@@ -12,17 +12,19 @@ use strict; + BEGIN { + # Thread shared properties (if threads are available: needs to be loaded elsewhere) + our $_tshv = { +- tsv => {}, +- cfgNum => 0, +- cfgDate => 0, +- lastCheck => 0, +- checkTime => 600, +- confAcc => {}, +- logger => {}, +- userLogger => {}, +- _auditLogger => {}, +- lmConf => {}, +- localConfig => {}, ++ tsv => {}, ++ cfgNum => 0, ++ cfgDate => 0, ++ checkMsg => 5, ++ lastCheck => 0, ++ lastCheckMsg => 0, ++ checkTime => 600, ++ confAcc => {}, ++ logger => {}, ++ userLogger => {}, ++ _auditLogger => {}, ++ lmConf => {}, ++ localConfig => {}, + }; + + # Current sessions properties diff --git a/base/Dockerfile b/base/Dockerfile index 84d2f9a..b4b7d7f 100644 --- a/base/Dockerfile +++ b/base/Dockerfile @@ -91,7 +91,9 @@ RUN echo "# Install packages from ${DEBIAN_VERSION} (${LLNGDIST})" && \ chown www-data:www-data /var/lib/lemonldap-ng/cache && \ chmod 750 /var/lib/lemonldap-ng/cache && \ perl -000 -MJSON -i -ne '$_=JSON::from_json($_);$_->{reloadUrls}={};print JSON->new->pretty->canonical->encode($_)' /var/lib/lemonldap-ng/conf/lmConf-1.json && \ + perl -i -pe 's/\r//g' /usr/share/perl5/Lemonldap/NG/Common/Conf/DefaultValues.pm && \ echo "patch session-cli.patch" && patch -p1 {refLocalStorage} ) { +- $self->setDefault($conf); +- $self->setValuesFromEnv($conf); +- $self->compactConf($conf); ++ $self->setDefault($conf); ++ $self->setValuesFromEnv($conf); ++ $self->compactConf($conf); ++ ++ if ( Lemonldap::NG::Handler::Main->can('tsv') ++ and Lemonldap::NG::Handler::Main->tsv->{msgBrokerWriter} ) ++ { ++ Lemonldap::NG::Handler::Main->tsv->{msgBrokerWriter} ++ ->publish( $conf->{eventQueueName}, { action => 'newConf' } ); ++ } ++ else { + eval { Lemonldap::NG::Handler::Main->reload() }; + } + +@@ -234,8 +241,9 @@ sub getConf { + + # Store configuration in cache + $self->setLocalConf($r) +- if ( $self->{refLocalStorage} +- and not( $noCache == 1 or $raw ) ); ++ if ( $self->{refLocalStorage} ++ and ( !$noCache or $noCache != 1 ) ++ and !$raw ); + } + } + +--- a/usr/share/perl5/Lemonldap/NG/Common/Conf/DefaultValues.pm ++++ b/usr/share/perl5/Lemonldap/NG/Common/Conf/DefaultValues.pm +@@ -38,6 +38,7 @@ sub defaultValues { + 'checkDevOpsDownload' => 1, + 'checkHIBPRequired' => 1, + 'checkHIBPURL' => 'https://api.pwnedpasswords.com/range/', ++ 'checkMsg' => 5, + 'checkTime' => 600, + 'checkUserDisplayComputedSession' => 1, + 'checkUserDisplayEmptyHeaders' => 0, +@@ -82,6 +83,7 @@ sub defaultValues { + }, + 'displaySessionId' => 1, + 'domain' => 'example.com', ++ 'eventQueueName' => 'llng_events', + 'exportedVars' => { + 'UA' => 'HTTP_USER_AGENT' + }, +@@ -193,6 +195,7 @@ sub defaultValues { + 'managerPassword' => '', + 'max2FDevices' => 10, + 'max2FDevicesNameLength' => 20, ++ 'messageBrokerOptions' => {}, + 'multiValuesSeparator' => '; ', + 'mySessionAuthorizedRWKeys' => [ + '_appsListOrder', +diff --git a/usr/share/perl5/Lemonldap/NG/Common/Logger/MessageBroker.pm b/usr/share/perl5/Lemonldap/NG/Common/Logger/MessageBroker.pm +new file mode 100644 +index 000000000..3aeec8433 +--- /dev/null ++++ b/usr/share/perl5/Lemonldap/NG/Common/Logger/MessageBroker.pm +@@ -0,0 +1,40 @@ ++package Lemonldap::NG::Common::Logger::MessageBroker; ++ ++use strict; ++ ++our $VERSION = '2.0.18'; ++ ++sub new { ++ my $self = bless {}, shift; ++ my ( $conf, %args ) = @_; ++ my $show = 1; ++ die 'Missing conf->loggerBroker' unless $conf->{loggerBroker}; ++ $conf->{loggerBroker} =~ s/^::/Lemonldap::NG::Common::MessageBroker::/; ++ my $brokerChannel = $conf->{loggerBrokerChannel} ++ || ( $args{user} ? 'llng-userlogs' : 'llng-logs' ); ++ my $type = $args{user} ? 'logs' : 'userLogs'; ++ eval "use $conf->{loggerBroker}"; ++ die "Unable to load $conf->{loggerBroker}: $@" if $@; ++ $self->{broker} = $conf->{loggerBroker}->new( $conf->{loggerBrokerOpts} ) ++ or die 'Unable to create message broker connector'; ++ ++ foreach (qw(error warn notice info debug)) { ++ if ($show) { ++ eval qq'sub $_ {\$_[0]->{broker}->publish("$brokerChannel", { ++ type => "$type", ++ data => \$_[1], ++ time => time, ++ level => "$_", ++ })}'; ++ die $@ if ($@); ++ } ++ else { ++ eval qq'sub $_ {1}'; ++ } ++ $show = 0 if ( $conf->{logLevel} eq $_ ); ++ } ++ die "Unknown logLevel $conf->{logLevel}" if $show; ++ return $self; ++} ++ ++1; +diff --git a/usr/share/perl5/Lemonldap/NG/Common/MessageBroker/MQTT.pm b/usr/share/perl5/Lemonldap/NG/Common/MessageBroker/MQTT.pm +new file mode 100644 +index 000000000..0416d4df5 +--- /dev/null ++++ b/usr/share/perl5/Lemonldap/NG/Common/MessageBroker/MQTT.pm +@@ -0,0 +1,96 @@ ++package Lemonldap::NG::Common::MessageBroker::MQTT; ++ ++use strict; ++use Net::MQTT::Simple; ++ ++our $VERSION = '2.20.0'; ++ ++sub new { ++ my ( $class, $conf, $logger ) = @_; ++ my $args = $conf->{messageBrokerOptions}; ++ $args //= {}; ++ $args->{server} ||= 'localhost:1883'; ++ my $mqtt; ++ if ( $args->{ssl} ) { ++ require Net::MQTT::Simple::SSL; ++ my $h = {}; ++ $h->{$_} = $args->{$_} ++ foreach (qw(SSL_ca_file SSL_cert_file SSL_key_file)); ++ $mqtt = Net::MQTT::Simple::SSL->new( $args->{server}, $h ); ++ if ( $args->{user} ) { ++ $mqtt->login( $args->{user}, $args->{password} ); ++ } ++ } ++ else { ++ $mqtt = Net::MQTT::Simple->new( $args->{server} ); ++ } ++ unless ($mqtt) { ++ $logger->error("Unable to connect to MQTT server $@$!"); ++ return; ++ } ++ my $self = bless { mqtt => $mqtt, _ch => [], logger => $logger }, $class; ++ return $self; ++} ++ ++sub publish { ++ my ( $self, $channel, $msg ) = @_; ++ die 'Not a hash msg' unless ref $msg eq 'HASH'; ++ my $j = eval { JSON::to_json($msg) }; ++ die "MessageBroker publish only hashes! $@" if $@; ++ $self->{mqtt}->publish( "llng/$channel", $j ); ++} ++ ++sub subscribe { ++ my ( $self, $channel ) = @_; ++ $self->{messages}{$channel} = []; ++ $self->{mqtt}->subscribe( ++ "llng/$channel", ++ sub { ++ return unless $_[1]; ++ $_[0] =~ s#llng/##; ++ my $tmp = eval { JSON::from_json( $_[1] ) }; ++ if ($@) { ++ $self->{logger}->error("Bad message from MQTT server: $@") ++ } ++ else { ++ push @{ $self->{messages}{ $_[0] } }, $tmp; ++ } ++ } ++ ); ++ push @{ $self->{_ch} }, "llng/$channel"; ++} ++ ++sub DESTROY { ++ my ($self) = @_; ++ eval { ++ ( $self->{mqtt} && $self->{mqtt}->unsubscribe($_) ) ++ foreach ( @{ $self->{_ch} } ); ++ }; ++ $self->{logger}->error($@) if $@; ++} ++ ++sub getNextMessage { ++ my ( $self, $channel, $delay ) = @_; ++ return undef unless $self->{messages}{$channel}; ++ return shift( @{ $self->{messages}{$channel} } ) ++ if @{ $self->{messages}{$channel} }; ++ $self->{mqtt}->tick( $delay // 0 ); ++ return shift( @{ $self->{messages}{$channel} } ) ++ if @{ $self->{messages}{$channel} }; ++ return; ++} ++ ++sub waitForNextMessage { ++ my ( $self, $channel ) = @_; ++ return undef ++ unless $self->{messages}{$channel}; ++ ++ # Infinite loop until one message is seen ++ my $res; ++ while ( !$res ) { ++ $res = $self->{redis}->getNextMessage( $channel, 1 ); ++ } ++ return $res; ++} ++ ++1; +diff --git a/usr/share/perl5/Lemonldap/NG/Common/MessageBroker/NoBroker.pm b/usr/share/perl5/Lemonldap/NG/Common/MessageBroker/NoBroker.pm +new file mode 100644 +index 000000000..f4257fa98 +--- /dev/null ++++ b/usr/share/perl5/Lemonldap/NG/Common/MessageBroker/NoBroker.pm +@@ -0,0 +1,59 @@ ++package Lemonldap::NG::Common::MessageBroker::NoBroker; ++ ++# This pseudo message broker only dispatch messages into current node. ++# It also inserts periodically "newConf" into llng_events to permit- to ++# detect configuration changes usng the checkTime parameter. ++ ++use strict; ++use Lemonldap::NG::Common::Conf::Constants; ++ ++our $VERSION = '2.20.0'; ++ ++our $lastCheck = time; ++ ++our $channels = {}; ++ ++sub new { ++ my ( $class, $conf, $logger ) = @_; ++ $channels->{ $conf->{eventQueueName} } //= []; ++ return bless { ++ checkTime => $conf->{checkTime}, ++ eventQueueName => $conf->{eventQueueName}, ++ logger => $logger, ++ }, $class; ++} ++ ++sub publish { ++ my ( $self, $channel, $msg ) = @_; ++ die unless $channel and $msg; ++ $channels->{$channel} = [] ++ unless ref( $channels->{$channel} ); ++ push @{ $channels->{$channel} }, $msg; ++} ++ ++sub subscribe { } ++ ++sub getNextMessage { ++ my ( $self, $channel, $delay ) = @_; ++ if ( time >= $lastCheck + $self->{checkTime} ) { ++ $self->publish( $self->{eventQueueName}, { action => 'newConf' } ); ++ $lastCheck = time; ++ } ++ if ( ref( $channels->{$channel} ) ++ and @{ $channels->{$channel} } ) ++ { ++ return shift @{ $channels->{$channel} }; ++ } ++} ++ ++sub waitForNextMessage { ++ my ( $self, $channel ) = @_; ++ while (1) { ++ if ( my $msg = $self->getNextMessage($channel) ) { ++ return $msg; ++ } ++ sleep 1; ++ } ++} ++ ++1; +diff --git a/usr/share/perl5/Lemonldap/NG/Common/MessageBroker/Pg.pm b/usr/share/perl5/Lemonldap/NG/Common/MessageBroker/Pg.pm +new file mode 100644 +index 000000000..e15e9d0f4 +--- /dev/null ++++ b/usr/share/perl5/Lemonldap/NG/Common/MessageBroker/Pg.pm +@@ -0,0 +1,74 @@ ++package Lemonldap::NG::Common::MessageBroker::Pg; ++ ++use strict; ++use DBI; ++use JSON; ++ ++our $VERSION = '2.20.0'; ++ ++sub new { ++ my ( $class, $conf, $logger ) = @_; ++ my $args = $conf->{messageBrokerOptions}; ++ unless ($args ++ and $args->{dbiChain} ++ and $args->{dbiUser} ++ and $args->{dbiPassword} ) ++ { ++ $logger->error('MISSING OPTIONS FOR PG PUB/SUB'); ++ return undef; ++ } ++ my $self = bless { %{$args}, logger => $logger }, $class; ++ return $self; ++} ++ ++sub publish { ++ my ( $self, $channel, $msg ) = @_; ++ die 'Not a hash msg' unless ref $msg eq 'HASH'; ++ my $j = eval { JSON::to_json($msg) }; ++ die "MessageBroker publish only hashes! $@" if $@; ++ $self->_dbh->do( "NOTIFY $channel, ?", undef, $j ); ++} ++ ++sub subscribe { ++ my ( $self, $channel ) = @_; ++ $self->{messages}{$channel} = []; ++ $self->_dbh->do("LISTEN $channel"); ++} ++ ++sub getNextMessage { ++ my ( $self, $channel, $delay ) = @_; ++ return undef ++ unless $self->{messages}{$channel}; ++ if ( my $notify = $self->_dbh->pg_notifies ) { ++ my ( $name, $pid, $payload ) = @$notify; ++ $payload = eval { JSON::from_json($payload) }; ++ if ($@) { ++ $self->{logger}->error("Bad message from Pg: $@"); ++ } ++ else { ++ push @{ $self->{messages}{$name} }, $payload; ++ } ++ } ++ return shift( @{ $self->{messages}{$channel} } ) ++ if @{ $self->{messages}{$channel} }; ++} ++ ++sub waitForNextMessage { ++ my ( $self, $channel ) = @_; ++ return undef unless $self->{messages}{$channel}; ++ ++ # Infinite loop until one message is seen ++ my $res; ++ while ( not( $res = $self->getNextMessage($channel) ) ) { ++ sleep 1; ++ } ++} ++ ++sub _dbh { ++ my ($self) = @_; ++ return $self->{_dbh} if ( $self->{_dbh} and $self->{_dbh}->ping ); ++ $self->{_dbh} = DBI->connect_cached( $self->{dbiChain}, $self->{dbiUser}, ++ $self->{dbiPassword}, { RaiseError => 1, AutoCommit => 1, } ); ++} ++ ++1; +diff --git a/usr/share/perl5/Lemonldap/NG/Common/MessageBroker/Redis.pm b/usr/share/perl5/Lemonldap/NG/Common/MessageBroker/Redis.pm +new file mode 100644 +index 000000000..e444395b7 +--- /dev/null ++++ b/usr/share/perl5/Lemonldap/NG/Common/MessageBroker/Redis.pm +@@ -0,0 +1,71 @@ ++package Lemonldap::NG::Common::MessageBroker::Redis; ++ ++use strict; ++use Redis; ++use JSON; ++ ++our $VERSION = '2.20.0'; ++ ++sub new { ++ my ( $class, $conf, $logger ) = @_; ++ my $self = bless { logger => $logger }, $class; ++ my $args = $conf->{messageBrokerOptions} // {}; ++ ++ # Reconnection parameters ++ # - try to reconnect every 1s up to 60s ++ $args->{reconnect} //= 60; ++ $args->{every} //= 1000000; ++ $self->{redis} = Redis->new(%$args); ++ $self->{messages} = {}; ++ return $self; ++} ++ ++sub publish { ++ my ( $self, $channel, $msg ) = @_; ++ die 'Not a hash msg' unless ref $msg eq 'HASH'; ++ my $j = eval { JSON::to_json($msg) }; ++ die "MessageBroker publish only hashes! $@" if $@; ++ $self->{redis}->publish( $channel, $j ); ++} ++ ++sub subscribe { ++ my ( $self, $channel ) = @_; ++ $self->{messages}{$channel} = []; ++ $self->{redis}->subscribe( ++ $channel, ++ sub { ++ my $tmp = eval { JSON::from_json( $_[0] ) }; ++ if ($@) { ++ $self->{logger}->error("Bad message from Redis: $@"); ++ } ++ else { ++ push @{ $self->{messages}{$channel} }, $tmp; ++ } ++ } ++ ); ++} ++ ++sub getNextMessage { ++ my ( $self, $channel, $delay ) = @_; ++ return undef ++ unless $self->{messages}{$channel}; ++ return shift( @{ $self->{messages}{$channel} } ) ++ if @{ $self->{messages}{$channel} }; ++ $self->{redis}->wait_for_messages( $delay || 0.001 ); ++ return shift( @{ $self->{messages}{$channel} } ) ++ if @{ $self->{messages}{$channel} }; ++ return; ++} ++ ++sub waitForNextMessage { ++ my ( $self, $channel ) = @_; ++ return undef ++ unless $self->{messages}{$channel}; ++ ++ # Infinite loop until one message is seen ++ $self->{redis}->wait_for_messages(1) ++ while ( !@{ $self->{messages}{$channel} } ); ++ return shift( @{ $self->{messages}{$channel} } ); ++} ++ ++1; +diff --git a/usr/share/perl5/Lemonldap/NG/Handler/Main/Init.pm b/usr/share/perl5/Lemonldap/NG/Handler/Main/Init.pm +index 783799e61..fb777e5f0 100644 +--- a/usr/share/perl5/Lemonldap/NG/Handler/Main/Init.pm ++++ b/usr/share/perl5/Lemonldap/NG/Handler/Main/Init.pm +@@ -33,6 +33,7 @@ sub init($$) { + { %{ $class->confAcc->getLocalConf('handler') }, %{$args} } ); + + $class->checkTime( $class->localConfig->{checkTime} || $class->checkTime ); ++ $class->checkMsg( $class->localConfig->{checkMsg} || $class->checkMsg ); + + # Few actions that must be done at server startup: + # * set log level for Lemonldap::NG logs +diff --git a/usr/share/perl5/Lemonldap/NG/Handler/Main/MsgActions.pm b/usr/share/perl5/Lemonldap/NG/Handler/Main/MsgActions.pm +new file mode 100644 +index 000000000..9ae96fd81 +--- /dev/null ++++ b/usr/share/perl5/Lemonldap/NG/Handler/Main/MsgActions.pm +@@ -0,0 +1,34 @@ ++package Lemonldap::NG::Handler::Main::MsgActions; ++ ++use strict; ++use Exporter 'import'; ++ ++our @EXPORT = qw(msgActions addMsgAction delMsgAction); ++ ++our $msgActions = { ++ newConf => sub { ++ my ( $class, $msg, $req ) = @_; ++ unless ( $class->checkConf() ) { ++ $class->logger->error("$class: No configuration found"); ++ $req->data->{noTry} = 1; ++ } ++ }, ++ unlog => sub { ++ my ( $class, $msg, $req ) = @_; ++ $class->localUnlog( $req, $msg->{id} ); ++ }, ++}; ++ ++sub msgActions { return $msgActions } ++ ++sub addMsgAction { ++ my ( $name, $sub ) = @_; ++ $msgActions->{$name} = $sub; ++} ++ ++sub delMsgAction { ++ my ($name) = @_; ++ delete $msgActions->{$name}; ++} ++ ++1; +diff --git a/usr/share/perl5/Lemonldap/NG/Handler/Main/Reload.pm b/usr/share/perl5/Lemonldap/NG/Handler/Main/Reload.pm +index 9fcb0c14f..fda61fd5b 100644 +--- a/usr/share/perl5/Lemonldap/NG/Handler/Main/Reload.pm ++++ b/usr/share/perl5/Lemonldap/NG/Handler/Main/Reload.pm +@@ -37,6 +37,8 @@ sub checkConf { + my ( $class, $force ) = @_; + $class->logger->debug("Check configuration for $class"); + my $prm = { local => !$force, localPrm => $class->localConfig }; ++ my $cfgNum = $class->confAcc->lastCfg(); ++ $prm->{local} = 0 if $class->cfgNum and $cfgNum != $class->cfgNum; + my $conf = $class->confAcc->getConf($prm); + chomp $Lemonldap::NG::Common::Conf::msg; + +@@ -93,6 +95,7 @@ sub checkConf { + } + } + $class->checkTime( $conf->{checkTime} ) if $conf->{checkTime}; ++ $class->checkMsg( $conf->{checkMsg} ) if $conf->{checkMsg}; + $class->lastCheck( time() ); + $class->logger->debug("$class: configuration is up to date"); + return 1; +@@ -164,6 +167,11 @@ sub reload { + # - outputPostData + # - aliasInit(): + # - vhostAlias ++# - oauth2Init(): ++# - oauth2Options ++# - msgBrokerInit(): ++# - msgBrokerWriter ++# - msgBrokerReader + # + # The *Init() methods can be run in any order, + # but jailInit must be run first because $tsv->{jail} +@@ -178,7 +186,8 @@ sub configReload { + + foreach my $sub ( + qw( defaultValuesInit jailInit portalInit locationRulesInit +- sessionStorageInit headersInit postUrlInit aliasInit oauth2Init ) ++ sessionStorageInit headersInit postUrlInit aliasInit ++ oauth2Init msgBrokerInit ) + ) + { + $class->logger->debug("Process $$ calls $sub"); +@@ -217,7 +226,7 @@ sub defaultValuesInit { + useSafeJail httpOnly whatToTrace handlerInternalCache + handlerServiceTokenTTL customToTrace lwpOpts lwpSslOpts + authChoiceAuthBasic authChoiceParam upgradeSession +- hashedSessionStore ++ hashedSessionStore eventQueueName + ) + ); + +@@ -616,6 +625,18 @@ sub oauth2Init { + return 1; + } + ++sub msgBrokerInit { ++ my ( $class, $conf ) = @_; ++ ++ my $brokerClass = $conf->{messageBroker} || '::NoBroker'; ++ $brokerClass =~ s/^::/Lemonldap::NG::Common::MessageBroker::/; ++ eval "require $brokerClass"; ++ die $@ if $@; ++ $class->tsv->{msgBrokerReader} = $brokerClass->new($conf, $class->logger); ++ $class->tsv->{msgBrokerReader}->subscribe( $conf->{eventQueueName} ); ++ $class->tsv->{msgBrokerWriter} = $brokerClass->new($conf, $class->logger); ++} ++ + sub substitute { + my ( $class, $expr ) = @_; + $expr //= ''; +diff --git a/usr/share/perl5/Lemonldap/NG/Handler/Main/Run.pm b/usr/share/perl5/Lemonldap/NG/Handler/Main/Run.pm +index bbeb94675..6df0229fd 100644 +--- a/usr/share/perl5/Lemonldap/NG/Handler/Main/Run.pm ++++ b/usr/share/perl5/Lemonldap/NG/Handler/Main/Run.pm +@@ -12,6 +12,7 @@ use MIME::Base64; + use URI; + use URI::Escape; + use Lemonldap::NG::Common::Session; ++use Lemonldap::NG::Handler::Main::MsgActions; + + # Methods that must be overloaded + +@@ -80,15 +81,17 @@ sub getStatus { + + # Method that must be called by base packages (Handler::ApacheMP2,...) to get + # type of handler to call (Main, AuthBasic,...) ++ + sub checkType { + my ( $class, $req ) = @_; + +- if ( time() - $class->lastCheck > $class->checkTime ) { +- unless ( $class->checkConf ) { +- $class->logger->error("$class: No configuration found"); +- $req->data->{noTry} = 1; +- return 'Fail'; +- } ++ # Always launch "newConf" task if never started ++ msgActions->{newConf}->( $class, {}, $req ) ++ unless $class->tsv and %{ $class->tsv }; ++ ++ # Check for event in events queue every 5 seconds ++ if ( time - $class->lastCheckMsg > $class->checkMsg ) { ++ defined( $class->checkEvent($req) ) or return 'Fail'; + } + my $vhost = $class->resolveAlias($req); + return ( defined $class->tsv->{type}->{$vhost} ) +@@ -96,6 +99,58 @@ sub checkType { + : 'Main'; + } + ++# Method to check for event in events queue ++sub checkEvent { ++ my ( $class, $req, $delay ) = @_; ++ $class->logger->debug('Checking for events'); ++ unless ( $class->tsv->{msgBrokerReader} ) { ++ $class->logger->error('Not initialized'); ++ return; ++ } ++ my $ret = ''; ++ while ( my $msg = ++ $class->tsv->{msgBrokerReader} ++ ->getNextMessage( $class->tsv->{eventQueueName}, $delay ) ) ++ { ++ if ( $msg->{action} ) { ++ $class->logger->debug("Processing event $msg->{action}"); ++ if ( my $sub = msgActions->{ $msg->{action} } ) { ++ $sub->( $class, $msg, $req ); ++ $ret = $msg->{action}; ++ } ++ else { ++ $class->logger->error("Unkown action $msg->{action}"); ++ } ++ } ++ else { ++ $class->logger->error( ++ 'Malformed message: ' . JSON::to_json($msg) ); ++ } ++ } ++ $class->lastCheckMsg(time); ++ return $ret; ++} ++ ++# Method to push an event into message queue. ++sub publishEvent { ++ my ( $class, $req, $msg ) = @_; ++ die unless $msg; ++ $class->logger->debug("Publishing event $msg->{action}"); ++ unless ( $class->tsv->{msgBrokerWriter} ) { ++ $class->logger->error('Not initialized'); ++ return; ++ } ++ $class->tsv->{msgBrokerWriter} ++ ->publish( $class->tsv->{eventQueueName}, $msg ); ++ my $ret; ++ my $start = time; ++ ++ # After pushing message, let's pop message queue for this node and ++ # then launch the concerning method ++ do { $ret = $class->checkEvent( $req, 0.1 ) } ++ while ( ( !$ret or $ret ne $msg->{action} ) and time < $start + 1 ); ++} ++ + ## @rmethod int run + # Check configuration and launch Lemonldap::NG::Handler::Main::run(). + # Each $checkTime, server child verifies if its configuration is the same +diff --git a/usr/share/perl5/Lemonldap/NG/Handler/Main/SharedVariables.pm b/usr/share/perl5/Lemonldap/NG/Handler/Main/SharedVariables.pm +index 2e7b38cb5..627aab5b2 100644 +--- a/usr/share/perl5/Lemonldap/NG/Handler/Main/SharedVariables.pm ++++ b/usr/share/perl5/Lemonldap/NG/Handler/Main/SharedVariables.pm +@@ -12,17 +12,19 @@ use strict; + BEGIN { + # Thread shared properties (if threads are available: needs to be loaded elsewhere) + our $_tshv = { +- tsv => {}, +- cfgNum => 0, +- cfgDate => 0, +- lastCheck => 0, +- checkTime => 600, +- confAcc => {}, +- logger => {}, +- userLogger => {}, +- _auditLogger => {}, +- lmConf => {}, +- localConfig => {}, ++ tsv => {}, ++ cfgNum => 0, ++ cfgDate => 0, ++ checkMsg => 5, ++ lastCheck => 0, ++ lastCheckMsg => 0, ++ checkTime => 600, ++ confAcc => {}, ++ logger => {}, ++ userLogger => {}, ++ _auditLogger => {}, ++ lmConf => {}, ++ localConfig => {}, + }; + + # Current sessions properties diff --git a/full/Dockerfile b/full/Dockerfile index 522adb3..797e97d 100644 --- a/full/Dockerfile +++ b/full/Dockerfile @@ -25,6 +25,7 @@ RUN \ echo patch token-exchange.patch && patch -p1 < token-exchange.patch && \ echo patch crowdsec.patch && patch -p1 < crowdsec.patch && \ echo patch recaptcha.patch && patch -p1 < recaptcha.patch && \ + echo patch msg-broker.patch && patch -p1 < msg-broker.patch && \ rm -f *.patch && \ LLNG_DEFAULTCONFFILE=/etc/lemonldap-ng/lemonldap-ng.ini \ perl -MLemonldap::NG::Manager::Build -e 'Lemonldap::NG::Manager::Build->run( \ diff --git a/full/msg-broker.patch b/full/msg-broker.patch new file mode 100644 index 0000000..25d650b --- /dev/null +++ b/full/msg-broker.patch @@ -0,0 +1,523 @@ +diff --git a/usr/share/perl5/Lemonldap/NG/Manager/Build/Attributes.pm b/usr/share/perl5/Lemonldap/NG/Manager/Build/Attributes.pm +index 5a3a3e94d..3e49f83ae 100644 +--- a/usr/share/perl5/Lemonldap/NG/Manager/Build/Attributes.pm ++++ b/usr/share/perl5/Lemonldap/NG/Manager/Build/Attributes.pm +@@ -316,6 +316,12 @@ sub attributes { + default => 600, + flags => 'hp', + }, ++ checkMsg => { ++ type => 'int', ++ documentation => 'Timeout to check new evant', ++ default => 5, ++ flags => 'hp', ++ }, + defaultNewKeySize => { + type => 'int', + documentation => 'Default size for new RSA key helper', +@@ -487,7 +493,6 @@ sub attributes { + }, + reloadUrls => { + type => 'keyTextContainer', +- help => 'configlocation.html#configuration-reload', + keyTest => qr/^$Regexp::Common::URI::RFC2396::host(?::\d+)?$/, + test => $url, + msgFail => '__badUrl__', +@@ -1846,6 +1851,30 @@ sub attributes { + documentation => 'List of virtualHosts with their get parameters', + }, + ++ # Message broker ++ messageBroker => { ++ type => 'select', ++ documentation => 'Messages broker module', ++ select => [ ++ { k => '', v => '' }, ++ { k => '::Redis', v => 'Redis' }, ++ { k => '::Pg', v => 'PostgreSQL' }, ++ { k => '::MQTT', v => 'MQTT' }, ++ ], ++ flags => 'hp', ++ }, ++ messageBrokerOptions => { ++ type => 'keyTextContainer', ++ default => {}, ++ documentation => 'Options of messages broker module', ++ flags => 'hp', ++ }, ++ eventQueueName => { ++ type => 'text', ++ default => 'llng_events', ++ documentation => 'Event channel name', ++ }, ++ + # Jitsi Meet tokens issuer + issuerDBJitsiMeetTokensActivation => { + type => 'bool', +diff --git a/usr/share/perl5/Lemonldap/NG/Manager/Build/Tree.pm b/usr/share/perl5/Lemonldap/NG/Manager/Build/Tree.pm +index 6f574a37d..d4386e439 100644 +--- a/usr/share/perl5/Lemonldap/NG/Manager/Build/Tree.pm ++++ b/usr/share/perl5/Lemonldap/NG/Manager/Build/Tree.pm +@@ -645,11 +645,6 @@ sub tree { + }, + ] + }, +- { +- title => 'reloadParams', +- help => 'configlocation.html#configuration-reload', +- nodes => [ 'reloadTimeout', 'compactConf', 'reloadUrls' ] +- }, + { + title => 'plugins', + help => 'start.html#plugins', +@@ -1295,6 +1290,15 @@ sub tree { + 'redirectFormMethod', 'activeTimer', + ] + }, ++ { ++ title => 'eventsManagement', ++ help => 'eventsmanagement.html', ++ nodes => [ ++ 'messageBroker', 'messageBrokerOptions', ++ 'reloadTimeout', 'compactConf', ++ 'reloadUrls', 'eventQueueName', ++ ] ++ }, + ] + } + ] +diff --git a/usr/share/perl5/Lemonldap/NG/Manager/Plugin.pm b/usr/share/perl5/Lemonldap/NG/Manager/Plugin.pm +index e6c302ce3..cd63fba72 100644 +--- a/usr/share/perl5/Lemonldap/NG/Manager/Plugin.pm ++++ b/usr/share/perl5/Lemonldap/NG/Manager/Plugin.pm +@@ -70,6 +70,10 @@ sub loadTemplate { + #@return reload status as boolean + sub applyConf { + my ( $self, $newConf ) = @_; ++ return ++ unless $self->p->api->tsv->{msgBrokerWriter} ++ and ref( $self->p->api->tsv->{msgBrokerWriter} ) =~ /NoBroker$/; ++ + my $status; + + # 1 Apply conf locally +diff --git a/usr/share/lemonldap-ng/manager/htdocs/static/languages/ar.json b/usr/share/lemonldap-ng/manager/htdocs/static/languages/ar.json +index 8ad2cda55..37266e201 100644 +--- a/usr/share/lemonldap-ng/manager/htdocs/static/languages/ar.json ++++ b/usr/share/lemonldap-ng/manager/htdocs/static/languages/ar.json +@@ -359,6 +359,8 @@ + "enterPassword":"أدخل كلمة المرور (اختياري)", + "error":"خطأ", + "errors":"ERRORS", ++"eventQueueName":"Event channel name", ++"eventsManagement":"Events management", + "exportedAttr":"SOAP/REST السمات المصدرة", + "exportedHeaders":"الهيدر المصدرة", + "exportedVars":"المتغيرات المصدرة", +@@ -605,6 +607,8 @@ + "menu":"القائمة", + "menuCategory":"قائمة الاقسام", + "message":"رسالة", ++"messageBroker":"Pub/Sub system", ++"messageBrokerOptions":"Pub/Sub system options", + "messages":"رسائل", + "modulesTitle":"الوحدات المستخدمة", + "multiIp":"عنوان آي بي متعدد", +@@ -1004,7 +1008,6 @@ + "registerUrl":"Register page URL", + "registerextra":"Registrable", + "registerrule":"Registration rule", +-"reloadParams":"إعادة تحميل الإعدادات", + "reloadTimeout":"Reload timeout", + "reloadUrls":"Reload URLs", + "rememberAuthChoice":"Remember authentication choice", +diff --git a/usr/share/lemonldap-ng/manager/htdocs/static/languages/en.json b/usr/share/lemonldap-ng/manager/htdocs/static/languages/en.json +index d02c520ae..dce9b19e5 100644 +--- a/usr/share/lemonldap-ng/manager/htdocs/static/languages/en.json ++++ b/usr/share/lemonldap-ng/manager/htdocs/static/languages/en.json +@@ -359,6 +359,8 @@ + "enterPassword":"Enter password (optional)", + "error":"Error", + "errors":"ERRORS", ++"eventQueueName":"Event channel name", ++"eventsManagement":"Events management", + "exportedAttr":"SOAP/REST exported attributes", + "exportedHeaders":"Exported headers", + "exportedVars":"Exported Variables", +@@ -605,6 +607,8 @@ + "menu":"Menu", + "menuCategory":"Menu category", + "message":"Message", ++"messageBroker":"Pub/Sub system", ++"messageBrokerOptions":"Pub/Sub system options", + "messages":"Messages", + "modulesTitle":"Used modules", + "multiIp":"Multi IP", +@@ -1004,7 +1008,6 @@ + "registerUrl":"Register page URL", + "registerextra":"Registrable", + "registerrule":"Registration rule", +-"reloadParams":"Configuration reload", + "reloadTimeout":"Reload timeout", + "reloadUrls":"Reload URLs", + "rememberAuthChoice":"Remember authentication choice", +diff --git a/usr/share/lemonldap-ng/manager/htdocs/static/languages/es.json b/usr/share/lemonldap-ng/manager/htdocs/static/languages/es.json +index 0badeb409..025481c6f 100644 +--- a/usr/share/lemonldap-ng/manager/htdocs/static/languages/es.json ++++ b/usr/share/lemonldap-ng/manager/htdocs/static/languages/es.json +@@ -359,6 +359,8 @@ + "enterPassword":"Introduzca password (opcional)", + "error":"Error", + "errors":"ERRORES", ++"eventQueueName":"Event channel name", ++"eventsManagement":"Events management", + "exportedAttr":"Atributos SOAP/REST exportados", + "exportedHeaders":"Cabeceras exportadas", + "exportedVars":"Variables exportadas", +@@ -605,6 +607,8 @@ + "menu":"Menú", + "menuCategory":"Categoría de menú", + "message":"Mensaje", ++"messageBroker":"Pub/Sub system", ++"messageBrokerOptions":"Pub/Sub system options", + "messages":"Mensajes", + "modulesTitle":"Módulos utilizados", + "multiIp":"Multi IP", +@@ -1004,7 +1008,6 @@ + "registerUrl":"URL de la página de registro", + "registerextra":"Registrable", + "registerrule":"Registration rule", +-"reloadParams":"Recargar configuración", + "reloadTimeout":"Reload timeout", + "reloadUrls":"Recargar las URL", + "rememberAuthChoice":"Remember authentication choice", +diff --git a/usr/share/lemonldap-ng/manager/htdocs/static/languages/fr.json b/usr/share/lemonldap-ng/manager/htdocs/static/languages/fr.json +index 72d0ba6da..f5b7a46df 100644 +--- a/usr/share/lemonldap-ng/manager/htdocs/static/languages/fr.json ++++ b/usr/share/lemonldap-ng/manager/htdocs/static/languages/fr.json +@@ -359,6 +359,8 @@ + "enterPassword":"Entrer le mot de passe (optionnel)", + "error":"Erreur", + "errors":"ERREURS", ++"eventQueueName":"Nom du canal des événements", ++"eventsManagement":"Gestion des événements", + "exportedAttr":"Attributs exportés par le portail (SOAP/REST)", + "exportedHeaders":"En-têtes exportés", + "exportedVars":"Attributs à exporter", +@@ -605,6 +607,8 @@ + "menu":"Menu", + "menuCategory":"Catégorie du menu", + "message":"Message", ++"messageBroker":"Système pub/sub", ++"messageBrokerOptions":"Options du système pub/sub", + "messages":"Messages", + "modulesTitle":"Modules utilisés", + "multiIp":"Multiples IP", +@@ -1004,7 +1008,6 @@ + "registerUrl":"URL de la page de création", + "registerextra":"Enregistrable", + "registerrule":"Règle d'enregistrement", +-"reloadParams":"Mise à jour de la configuration", + "reloadTimeout":"Délai de mise à jour", + "reloadUrls":"URLs de mise à jour", + "rememberAuthChoice":"Se souvenir du choix d'authentification", +diff --git a/usr/share/lemonldap-ng/manager/htdocs/static/languages/he.json b/usr/share/lemonldap-ng/manager/htdocs/static/languages/he.json +index 14891f173..681a27b55 100644 +--- a/usr/share/lemonldap-ng/manager/htdocs/static/languages/he.json ++++ b/usr/share/lemonldap-ng/manager/htdocs/static/languages/he.json +@@ -359,6 +359,8 @@ + "enterPassword":"להקליד סיסמה (רשות)", + "error":"שגיאה", + "errors":"שגיאות", ++"eventQueueName":"Event channel name", ++"eventsManagement":"Events management", + "exportedAttr":"מאפייני SOAP/REST מיוצאים", + "exportedHeaders":"כותרות מיוצאות", + "exportedVars":"משתנים מיוצאים", +@@ -605,6 +607,8 @@ + "menu":"תפריט", + "menuCategory":"קטגוריית תפריט", + "message":"הודעה", ++"messageBroker":"Pub/Sub system", ++"messageBrokerOptions":"Pub/Sub system options", + "messages":"הודעות", + "modulesTitle":"מודולים בשימוש", + "multiIp":"ריבוי כתובות IP", +@@ -1004,7 +1008,6 @@ + "registerUrl":"כתובת עמוד רישום", + "registerextra":"Registrable", + "registerrule":"Registration rule", +-"reloadParams":"Configuration reload", + "reloadTimeout":"Reload timeout", + "reloadUrls":"רענון כתובות", + "rememberAuthChoice":"Remember authentication choice", +diff --git a/usr/share/lemonldap-ng/manager/htdocs/static/languages/it.json b/usr/share/lemonldap-ng/manager/htdocs/static/languages/it.json +index 6edf467fd..d821ab095 100644 +--- a/usr/share/lemonldap-ng/manager/htdocs/static/languages/it.json ++++ b/usr/share/lemonldap-ng/manager/htdocs/static/languages/it.json +@@ -359,6 +359,8 @@ + "enterPassword":"Inserisci password (opzionale)", + "error":"Errore", + "errors":"ERRORI", ++"eventQueueName":"Event channel name", ++"eventsManagement":"Events management", + "exportedAttr":"Attributi di SOAP/REST esportati", + "exportedHeaders":"Intestazioni esportate", + "exportedVars":"Variabili esportate", +@@ -605,6 +607,8 @@ + "menu":"Menu", + "menuCategory":"Categoria del menu", + "message":"Messaggio", ++"messageBroker":"Pub/Sub system", ++"messageBrokerOptions":"Pub/Sub system options", + "messages":"Messaggi", + "modulesTitle":"Moduli usati", + "multiIp":"Multi IP", +@@ -1004,7 +1008,6 @@ + "registerUrl":"URL della pagina di registrazione", + "registerextra":"Registrable", + "registerrule":"Registration rule", +-"reloadParams":"Ricarica di configurazione", + "reloadTimeout":"Ricarica il timeout", + "reloadUrls":"Ricarica gli URL", + "rememberAuthChoice":"Remember authentication choice", +diff --git a/usr/share/lemonldap-ng/manager/htdocs/static/languages/pl.json b/usr/share/lemonldap-ng/manager/htdocs/static/languages/pl.json +index 9f73b4f2e..51d9b51bc 100644 +--- a/usr/share/lemonldap-ng/manager/htdocs/static/languages/pl.json ++++ b/usr/share/lemonldap-ng/manager/htdocs/static/languages/pl.json +@@ -359,6 +359,8 @@ + "enterPassword":"Wpisz hasło (opcjonalnie)", + "error":"Błąd", + "errors":"BŁĘDY", ++"eventQueueName":"Event channel name", ++"eventsManagement":"Events management", + "exportedAttr":"Wyeksportowane atrybuty SOAP/REST", + "exportedHeaders":"Wyeksportowane nagłówki", + "exportedVars":"Wyeksportowane zmienne", +@@ -605,6 +607,8 @@ + "menu":"Menu", + "menuCategory":"Kategoria menu", + "message":"Wiadomość", ++"messageBroker":"Pub/Sub system", ++"messageBrokerOptions":"Pub/Sub system options", + "messages":"Wiadomości", + "modulesTitle":"Użyte moduły", + "multiIp":"Multi IP", +@@ -1004,7 +1008,6 @@ + "registerUrl":"Zarejestruj adres URL strony", + "registerextra":"podlegający rejestracji", + "registerrule":"Registration rule", +-"reloadParams":"Załaduj ponownie konfigurację", + "reloadTimeout":"Limit czasu przeładowania", + "reloadUrls":"Załaduj ponownie adresy URL", + "rememberAuthChoice":"Zapamiętaj wybór uwierzytelniania", +diff --git a/usr/share/lemonldap-ng/manager/htdocs/static/languages/pt.json b/usr/share/lemonldap-ng/manager/htdocs/static/languages/pt.json +index 39b48f3a4..d5a9a9c2a 100644 +--- a/usr/share/lemonldap-ng/manager/htdocs/static/languages/pt.json ++++ b/usr/share/lemonldap-ng/manager/htdocs/static/languages/pt.json +@@ -359,6 +359,8 @@ + "enterPassword":"Informe senha (opcional)", + "error":"Erro", + "errors":"ERROS", ++"eventQueueName":"Event channel name", ++"eventsManagement":"Events management", + "exportedAttr":"Atributos exportados SOAP/REST", + "exportedHeaders":"Cabeçalhos exportados", + "exportedVars":"Variáveis exportadas", +@@ -605,6 +607,8 @@ + "menu":"Menu", + "menuCategory":"Categoria de menu", + "message":"Mensagem", ++"messageBroker":"Pub/Sub system", ++"messageBrokerOptions":"Pub/Sub system options", + "messages":"Mensagens", + "modulesTitle":"Módulos usados", + "multiIp":"Múltiplos IP", +@@ -1004,7 +1008,6 @@ + "registerUrl":"URL da página de registro", + "registerextra":"Registrável", + "registerrule":"Registration rule", +-"reloadParams":"Recarregar configuração", + "reloadTimeout":"Expiração de recarga", + "reloadUrls":"URLs de recarga", + "rememberAuthChoice":"Lembrar escolha de autenticação", +diff --git a/usr/share/lemonldap-ng/manager/htdocs/static/languages/pt_BR.json b/usr/share/lemonldap-ng/manager/htdocs/static/languages/pt_BR.json +index 26ca99b63..693cf96cc 100644 +--- a/usr/share/lemonldap-ng/manager/htdocs/static/languages/pt_BR.json ++++ b/usr/share/lemonldap-ng/manager/htdocs/static/languages/pt_BR.json +@@ -359,6 +359,8 @@ + "enterPassword":"Informe senha (opcional)", + "error":"Erro", + "errors":"ERROS", ++"eventQueueName":"Event channel name", ++"eventsManagement":"Events management", + "exportedAttr":"Atributos exportados SOAP/REST", + "exportedHeaders":"Cabeçalhos exportados", + "exportedVars":"Variáveis exportadas", +@@ -605,6 +607,8 @@ + "menu":"Menu", + "menuCategory":"Categoria de menu", + "message":"Mensagem", ++"messageBroker":"Pub/Sub system", ++"messageBrokerOptions":"Pub/Sub system options", + "messages":"Mensagens", + "modulesTitle":"Módulos usados", + "multiIp":"Múltiplos IPs", +@@ -1004,7 +1008,6 @@ + "registerUrl":"URL da página de registro", + "registerextra":"Registrável", + "registerrule":"Registration rule", +-"reloadParams":"Recarregar configuração", + "reloadTimeout":"Expiração de recarga", + "reloadUrls":"URLs de recarga", + "rememberAuthChoice":"Lembrar escolha de autenticação", +diff --git a/usr/share/lemonldap-ng/manager/htdocs/static/languages/ru.json b/usr/share/lemonldap-ng/manager/htdocs/static/languages/ru.json +index 506ca6c80..9c6677aa8 100644 +--- a/usr/share/lemonldap-ng/manager/htdocs/static/languages/ru.json ++++ b/usr/share/lemonldap-ng/manager/htdocs/static/languages/ru.json +@@ -359,6 +359,8 @@ + "enterPassword":"Введите пароль (опционально)", + "error":"Ошибка", + "errors":"ОШИБКИ", ++"eventQueueName":"Event channel name", ++"eventsManagement":"Events management", + "exportedAttr":"Экспортированные атрибуты SOAP/REST", + "exportedHeaders":"Экспортированные заголовки", + "exportedVars":"Экспортированные переменные", +@@ -605,6 +607,8 @@ + "menu":"Меню", + "menuCategory":"Категория меню", + "message":"Сообщение", ++"messageBroker":"Pub/Sub system", ++"messageBrokerOptions":"Pub/Sub system options", + "messages":"Сообщения", + "modulesTitle":"Используемые модули", + "multiIp":"Мульти-IP", +@@ -1004,7 +1008,6 @@ + "registerUrl":"URL страницы регистрации", + "registerextra":"Регистрируемый", + "registerrule":"Registration rule", +-"reloadParams":"Перезагрузка конфигурации", + "reloadTimeout":"Время ожидания перезагрузки", + "reloadUrls":"Перезагрузка URL", + "rememberAuthChoice":"Запомнить выбор аутентификации", +diff --git a/usr/share/lemonldap-ng/manager/htdocs/static/languages/tr.json b/usr/share/lemonldap-ng/manager/htdocs/static/languages/tr.json +index 8c162a17e..f53762af3 100644 +--- a/usr/share/lemonldap-ng/manager/htdocs/static/languages/tr.json ++++ b/usr/share/lemonldap-ng/manager/htdocs/static/languages/tr.json +@@ -359,6 +359,8 @@ + "enterPassword":"Parolayı gir (isteğe bağlı)", + "error":"Hata", + "errors":"HATALAR", ++"eventQueueName":"Event channel name", ++"eventsManagement":"Events management", + "exportedAttr":"Dışa aktarılan SOAP/REST nitelikleri", + "exportedHeaders":"Dışa aktarılan başlıklar", + "exportedVars":"Dışa Aktarılan Değişkenler", +@@ -605,6 +607,8 @@ + "menu":"Menü", + "menuCategory":"Menü kategorisi", + "message":"Mesaj", ++"messageBroker":"Pub/Sub system", ++"messageBrokerOptions":"Pub/Sub system options", + "messages":"Mesajlar", + "modulesTitle":"Kullanılan modüller", + "multiIp":"Çoklu IP", +@@ -1004,7 +1008,6 @@ + "registerUrl":"Kayıt sayfası URL'si", + "registerextra":"Kaydedilebilir", + "registerrule":"Registration rule", +-"reloadParams":"Yapılandırma yeniden yüklendi", + "reloadTimeout":"Yeniden yükleme zaman aşımı", + "reloadUrls":"URL'leri yeniden yükle", + "rememberAuthChoice":"Kimlik doğrulama seçimini hatırla", +diff --git a/usr/share/lemonldap-ng/manager/htdocs/static/languages/vi.json b/usr/share/lemonldap-ng/manager/htdocs/static/languages/vi.json +index fcf7e4b56..a07e03705 100644 +--- a/usr/share/lemonldap-ng/manager/htdocs/static/languages/vi.json ++++ b/usr/share/lemonldap-ng/manager/htdocs/static/languages/vi.json +@@ -359,6 +359,8 @@ + "enterPassword":"Nhập mật khẩu (tùy chọn)", + "error":"Lỗi", + "errors":"LỖI", ++"eventQueueName":"Event channel name", ++"eventsManagement":"Events management", + "exportedAttr":"Biến SOAP/REST đã được xuất", + "exportedHeaders":"Tiêu đề đã được xuất", + "exportedVars":"Biến đã được xuất", +@@ -605,6 +607,8 @@ + "menu":"Menu", + "menuCategory":"Loại menu", + "message":"Tin nhắn", ++"messageBroker":"Pub/Sub system", ++"messageBrokerOptions":"Pub/Sub system options", + "messages":"Tin nhắn", + "modulesTitle":"Các mô-đun đã sử dụng", + "multiIp":"Nhiều địa chỉ IP", +@@ -1004,7 +1008,6 @@ + "registerUrl":"URL trang đăng ký", + "registerextra":"Có thể đăng ký", + "registerrule":"Registration rule", +-"reloadParams":"Tải lại cấu hình", + "reloadTimeout":"hết thời gian tải lại", + "reloadUrls":"Tải lại URLs", + "rememberAuthChoice":"Ghi nhớ lựa chọn xác thực", +diff --git a/usr/share/lemonldap-ng/manager/htdocs/static/languages/zh.json b/usr/share/lemonldap-ng/manager/htdocs/static/languages/zh.json +index 311ec95ea..b07f3ba33 100644 +--- a/usr/share/lemonldap-ng/manager/htdocs/static/languages/zh.json ++++ b/usr/share/lemonldap-ng/manager/htdocs/static/languages/zh.json +@@ -359,6 +359,8 @@ + "enterPassword":"輸入密碼(選擇性)", + "error":"错误", + "errors":"錯誤", ++"eventQueueName":"Event channel name", ++"eventsManagement":"Events management", + "exportedAttr":"SOAP/REST 已匯出屬性", + "exportedHeaders":"已匯出的標頭", + "exportedVars":"已匯出的變數", +@@ -605,6 +607,8 @@ + "menu":"選單", + "menuCategory":"選單分類", + "message":"訊息", ++"messageBroker":"Pub/Sub system", ++"messageBrokerOptions":"Pub/Sub system options", + "messages":"訊息", + "modulesTitle":"已使用的模組", + "multiIp":"多 IP", +@@ -1004,7 +1008,6 @@ + "registerUrl":"註冊 URL", + "registerextra":"Registrable", + "registerrule":"Registration rule", +-"reloadParams":"設定重新載入", + "reloadTimeout":"重新載入逾時", + "reloadUrls":"重新載入 URL", + "rememberAuthChoice":"Remember authentication choice", +diff --git a/usr/share/lemonldap-ng/manager/htdocs/static/languages/zh_TW.json b/usr/share/lemonldap-ng/manager/htdocs/static/languages/zh_TW.json +index ff74edb6e..38b7ec516 100644 +--- a/usr/share/lemonldap-ng/manager/htdocs/static/languages/zh_TW.json ++++ b/usr/share/lemonldap-ng/manager/htdocs/static/languages/zh_TW.json +@@ -359,6 +359,8 @@ + "enterPassword":"輸入密碼(選擇性)", + "error":"錯誤", + "errors":"錯誤", ++"eventQueueName":"Event channel name", ++"eventsManagement":"Events management", + "exportedAttr":"SOAP/REST 已匯出屬性", + "exportedHeaders":"已匯出的標頭", + "exportedVars":"已匯出的變數", +@@ -605,6 +607,8 @@ + "menu":"選單", + "menuCategory":"選單分類", + "message":"訊息", ++"messageBroker":"Pub/Sub system", ++"messageBrokerOptions":"Pub/Sub system options", + "messages":"訊息", + "modulesTitle":"已使用的模組", + "multiIp":"多 IP", +@@ -1004,7 +1008,6 @@ + "registerUrl":"註冊 URL", + "registerextra":"Registrable", + "registerrule":"Registration rule", +-"reloadParams":"設定重新載入", + "reloadTimeout":"重新載入逾時", + "reloadUrls":"重新載入 URL", + "rememberAuthChoice":"Remember authentication choice", diff --git a/manager/Dockerfile b/manager/Dockerfile index 5fc17c1..cb2122a 100644 --- a/manager/Dockerfile +++ b/manager/Dockerfile @@ -32,6 +32,7 @@ RUN \ echo patch token-exchange.patch && patch -p1 < token-exchange.patch && \ echo patch crowdsec.patch && patch -p1 < crowdsec.patch && \ echo patch recaptcha.patch && patch -p1 < recaptcha.patch && \ + echo patch msg-broker.patch && patch -p1 < msg-broker.patch && \ rm -f *.patch && \ LLNG_DEFAULTCONFFILE=/etc/lemonldap-ng/lemonldap-ng.ini \ perl -MLemonldap::NG::Manager::Build -e 'Lemonldap::NG::Manager::Build->run( \ diff --git a/manager/msg-broker.patch b/manager/msg-broker.patch new file mode 100644 index 0000000..25d650b --- /dev/null +++ b/manager/msg-broker.patch @@ -0,0 +1,523 @@ +diff --git a/usr/share/perl5/Lemonldap/NG/Manager/Build/Attributes.pm b/usr/share/perl5/Lemonldap/NG/Manager/Build/Attributes.pm +index 5a3a3e94d..3e49f83ae 100644 +--- a/usr/share/perl5/Lemonldap/NG/Manager/Build/Attributes.pm ++++ b/usr/share/perl5/Lemonldap/NG/Manager/Build/Attributes.pm +@@ -316,6 +316,12 @@ sub attributes { + default => 600, + flags => 'hp', + }, ++ checkMsg => { ++ type => 'int', ++ documentation => 'Timeout to check new evant', ++ default => 5, ++ flags => 'hp', ++ }, + defaultNewKeySize => { + type => 'int', + documentation => 'Default size for new RSA key helper', +@@ -487,7 +493,6 @@ sub attributes { + }, + reloadUrls => { + type => 'keyTextContainer', +- help => 'configlocation.html#configuration-reload', + keyTest => qr/^$Regexp::Common::URI::RFC2396::host(?::\d+)?$/, + test => $url, + msgFail => '__badUrl__', +@@ -1846,6 +1851,30 @@ sub attributes { + documentation => 'List of virtualHosts with their get parameters', + }, + ++ # Message broker ++ messageBroker => { ++ type => 'select', ++ documentation => 'Messages broker module', ++ select => [ ++ { k => '', v => '' }, ++ { k => '::Redis', v => 'Redis' }, ++ { k => '::Pg', v => 'PostgreSQL' }, ++ { k => '::MQTT', v => 'MQTT' }, ++ ], ++ flags => 'hp', ++ }, ++ messageBrokerOptions => { ++ type => 'keyTextContainer', ++ default => {}, ++ documentation => 'Options of messages broker module', ++ flags => 'hp', ++ }, ++ eventQueueName => { ++ type => 'text', ++ default => 'llng_events', ++ documentation => 'Event channel name', ++ }, ++ + # Jitsi Meet tokens issuer + issuerDBJitsiMeetTokensActivation => { + type => 'bool', +diff --git a/usr/share/perl5/Lemonldap/NG/Manager/Build/Tree.pm b/usr/share/perl5/Lemonldap/NG/Manager/Build/Tree.pm +index 6f574a37d..d4386e439 100644 +--- a/usr/share/perl5/Lemonldap/NG/Manager/Build/Tree.pm ++++ b/usr/share/perl5/Lemonldap/NG/Manager/Build/Tree.pm +@@ -645,11 +645,6 @@ sub tree { + }, + ] + }, +- { +- title => 'reloadParams', +- help => 'configlocation.html#configuration-reload', +- nodes => [ 'reloadTimeout', 'compactConf', 'reloadUrls' ] +- }, + { + title => 'plugins', + help => 'start.html#plugins', +@@ -1295,6 +1290,15 @@ sub tree { + 'redirectFormMethod', 'activeTimer', + ] + }, ++ { ++ title => 'eventsManagement', ++ help => 'eventsmanagement.html', ++ nodes => [ ++ 'messageBroker', 'messageBrokerOptions', ++ 'reloadTimeout', 'compactConf', ++ 'reloadUrls', 'eventQueueName', ++ ] ++ }, + ] + } + ] +diff --git a/usr/share/perl5/Lemonldap/NG/Manager/Plugin.pm b/usr/share/perl5/Lemonldap/NG/Manager/Plugin.pm +index e6c302ce3..cd63fba72 100644 +--- a/usr/share/perl5/Lemonldap/NG/Manager/Plugin.pm ++++ b/usr/share/perl5/Lemonldap/NG/Manager/Plugin.pm +@@ -70,6 +70,10 @@ sub loadTemplate { + #@return reload status as boolean + sub applyConf { + my ( $self, $newConf ) = @_; ++ return ++ unless $self->p->api->tsv->{msgBrokerWriter} ++ and ref( $self->p->api->tsv->{msgBrokerWriter} ) =~ /NoBroker$/; ++ + my $status; + + # 1 Apply conf locally +diff --git a/usr/share/lemonldap-ng/manager/htdocs/static/languages/ar.json b/usr/share/lemonldap-ng/manager/htdocs/static/languages/ar.json +index 8ad2cda55..37266e201 100644 +--- a/usr/share/lemonldap-ng/manager/htdocs/static/languages/ar.json ++++ b/usr/share/lemonldap-ng/manager/htdocs/static/languages/ar.json +@@ -359,6 +359,8 @@ + "enterPassword":"أدخل كلمة المرور (اختياري)", + "error":"خطأ", + "errors":"ERRORS", ++"eventQueueName":"Event channel name", ++"eventsManagement":"Events management", + "exportedAttr":"SOAP/REST السمات المصدرة", + "exportedHeaders":"الهيدر المصدرة", + "exportedVars":"المتغيرات المصدرة", +@@ -605,6 +607,8 @@ + "menu":"القائمة", + "menuCategory":"قائمة الاقسام", + "message":"رسالة", ++"messageBroker":"Pub/Sub system", ++"messageBrokerOptions":"Pub/Sub system options", + "messages":"رسائل", + "modulesTitle":"الوحدات المستخدمة", + "multiIp":"عنوان آي بي متعدد", +@@ -1004,7 +1008,6 @@ + "registerUrl":"Register page URL", + "registerextra":"Registrable", + "registerrule":"Registration rule", +-"reloadParams":"إعادة تحميل الإعدادات", + "reloadTimeout":"Reload timeout", + "reloadUrls":"Reload URLs", + "rememberAuthChoice":"Remember authentication choice", +diff --git a/usr/share/lemonldap-ng/manager/htdocs/static/languages/en.json b/usr/share/lemonldap-ng/manager/htdocs/static/languages/en.json +index d02c520ae..dce9b19e5 100644 +--- a/usr/share/lemonldap-ng/manager/htdocs/static/languages/en.json ++++ b/usr/share/lemonldap-ng/manager/htdocs/static/languages/en.json +@@ -359,6 +359,8 @@ + "enterPassword":"Enter password (optional)", + "error":"Error", + "errors":"ERRORS", ++"eventQueueName":"Event channel name", ++"eventsManagement":"Events management", + "exportedAttr":"SOAP/REST exported attributes", + "exportedHeaders":"Exported headers", + "exportedVars":"Exported Variables", +@@ -605,6 +607,8 @@ + "menu":"Menu", + "menuCategory":"Menu category", + "message":"Message", ++"messageBroker":"Pub/Sub system", ++"messageBrokerOptions":"Pub/Sub system options", + "messages":"Messages", + "modulesTitle":"Used modules", + "multiIp":"Multi IP", +@@ -1004,7 +1008,6 @@ + "registerUrl":"Register page URL", + "registerextra":"Registrable", + "registerrule":"Registration rule", +-"reloadParams":"Configuration reload", + "reloadTimeout":"Reload timeout", + "reloadUrls":"Reload URLs", + "rememberAuthChoice":"Remember authentication choice", +diff --git a/usr/share/lemonldap-ng/manager/htdocs/static/languages/es.json b/usr/share/lemonldap-ng/manager/htdocs/static/languages/es.json +index 0badeb409..025481c6f 100644 +--- a/usr/share/lemonldap-ng/manager/htdocs/static/languages/es.json ++++ b/usr/share/lemonldap-ng/manager/htdocs/static/languages/es.json +@@ -359,6 +359,8 @@ + "enterPassword":"Introduzca password (opcional)", + "error":"Error", + "errors":"ERRORES", ++"eventQueueName":"Event channel name", ++"eventsManagement":"Events management", + "exportedAttr":"Atributos SOAP/REST exportados", + "exportedHeaders":"Cabeceras exportadas", + "exportedVars":"Variables exportadas", +@@ -605,6 +607,8 @@ + "menu":"Menú", + "menuCategory":"Categoría de menú", + "message":"Mensaje", ++"messageBroker":"Pub/Sub system", ++"messageBrokerOptions":"Pub/Sub system options", + "messages":"Mensajes", + "modulesTitle":"Módulos utilizados", + "multiIp":"Multi IP", +@@ -1004,7 +1008,6 @@ + "registerUrl":"URL de la página de registro", + "registerextra":"Registrable", + "registerrule":"Registration rule", +-"reloadParams":"Recargar configuración", + "reloadTimeout":"Reload timeout", + "reloadUrls":"Recargar las URL", + "rememberAuthChoice":"Remember authentication choice", +diff --git a/usr/share/lemonldap-ng/manager/htdocs/static/languages/fr.json b/usr/share/lemonldap-ng/manager/htdocs/static/languages/fr.json +index 72d0ba6da..f5b7a46df 100644 +--- a/usr/share/lemonldap-ng/manager/htdocs/static/languages/fr.json ++++ b/usr/share/lemonldap-ng/manager/htdocs/static/languages/fr.json +@@ -359,6 +359,8 @@ + "enterPassword":"Entrer le mot de passe (optionnel)", + "error":"Erreur", + "errors":"ERREURS", ++"eventQueueName":"Nom du canal des événements", ++"eventsManagement":"Gestion des événements", + "exportedAttr":"Attributs exportés par le portail (SOAP/REST)", + "exportedHeaders":"En-têtes exportés", + "exportedVars":"Attributs à exporter", +@@ -605,6 +607,8 @@ + "menu":"Menu", + "menuCategory":"Catégorie du menu", + "message":"Message", ++"messageBroker":"Système pub/sub", ++"messageBrokerOptions":"Options du système pub/sub", + "messages":"Messages", + "modulesTitle":"Modules utilisés", + "multiIp":"Multiples IP", +@@ -1004,7 +1008,6 @@ + "registerUrl":"URL de la page de création", + "registerextra":"Enregistrable", + "registerrule":"Règle d'enregistrement", +-"reloadParams":"Mise à jour de la configuration", + "reloadTimeout":"Délai de mise à jour", + "reloadUrls":"URLs de mise à jour", + "rememberAuthChoice":"Se souvenir du choix d'authentification", +diff --git a/usr/share/lemonldap-ng/manager/htdocs/static/languages/he.json b/usr/share/lemonldap-ng/manager/htdocs/static/languages/he.json +index 14891f173..681a27b55 100644 +--- a/usr/share/lemonldap-ng/manager/htdocs/static/languages/he.json ++++ b/usr/share/lemonldap-ng/manager/htdocs/static/languages/he.json +@@ -359,6 +359,8 @@ + "enterPassword":"להקליד סיסמה (רשות)", + "error":"שגיאה", + "errors":"שגיאות", ++"eventQueueName":"Event channel name", ++"eventsManagement":"Events management", + "exportedAttr":"מאפייני SOAP/REST מיוצאים", + "exportedHeaders":"כותרות מיוצאות", + "exportedVars":"משתנים מיוצאים", +@@ -605,6 +607,8 @@ + "menu":"תפריט", + "menuCategory":"קטגוריית תפריט", + "message":"הודעה", ++"messageBroker":"Pub/Sub system", ++"messageBrokerOptions":"Pub/Sub system options", + "messages":"הודעות", + "modulesTitle":"מודולים בשימוש", + "multiIp":"ריבוי כתובות IP", +@@ -1004,7 +1008,6 @@ + "registerUrl":"כתובת עמוד רישום", + "registerextra":"Registrable", + "registerrule":"Registration rule", +-"reloadParams":"Configuration reload", + "reloadTimeout":"Reload timeout", + "reloadUrls":"רענון כתובות", + "rememberAuthChoice":"Remember authentication choice", +diff --git a/usr/share/lemonldap-ng/manager/htdocs/static/languages/it.json b/usr/share/lemonldap-ng/manager/htdocs/static/languages/it.json +index 6edf467fd..d821ab095 100644 +--- a/usr/share/lemonldap-ng/manager/htdocs/static/languages/it.json ++++ b/usr/share/lemonldap-ng/manager/htdocs/static/languages/it.json +@@ -359,6 +359,8 @@ + "enterPassword":"Inserisci password (opzionale)", + "error":"Errore", + "errors":"ERRORI", ++"eventQueueName":"Event channel name", ++"eventsManagement":"Events management", + "exportedAttr":"Attributi di SOAP/REST esportati", + "exportedHeaders":"Intestazioni esportate", + "exportedVars":"Variabili esportate", +@@ -605,6 +607,8 @@ + "menu":"Menu", + "menuCategory":"Categoria del menu", + "message":"Messaggio", ++"messageBroker":"Pub/Sub system", ++"messageBrokerOptions":"Pub/Sub system options", + "messages":"Messaggi", + "modulesTitle":"Moduli usati", + "multiIp":"Multi IP", +@@ -1004,7 +1008,6 @@ + "registerUrl":"URL della pagina di registrazione", + "registerextra":"Registrable", + "registerrule":"Registration rule", +-"reloadParams":"Ricarica di configurazione", + "reloadTimeout":"Ricarica il timeout", + "reloadUrls":"Ricarica gli URL", + "rememberAuthChoice":"Remember authentication choice", +diff --git a/usr/share/lemonldap-ng/manager/htdocs/static/languages/pl.json b/usr/share/lemonldap-ng/manager/htdocs/static/languages/pl.json +index 9f73b4f2e..51d9b51bc 100644 +--- a/usr/share/lemonldap-ng/manager/htdocs/static/languages/pl.json ++++ b/usr/share/lemonldap-ng/manager/htdocs/static/languages/pl.json +@@ -359,6 +359,8 @@ + "enterPassword":"Wpisz hasło (opcjonalnie)", + "error":"Błąd", + "errors":"BŁĘDY", ++"eventQueueName":"Event channel name", ++"eventsManagement":"Events management", + "exportedAttr":"Wyeksportowane atrybuty SOAP/REST", + "exportedHeaders":"Wyeksportowane nagłówki", + "exportedVars":"Wyeksportowane zmienne", +@@ -605,6 +607,8 @@ + "menu":"Menu", + "menuCategory":"Kategoria menu", + "message":"Wiadomość", ++"messageBroker":"Pub/Sub system", ++"messageBrokerOptions":"Pub/Sub system options", + "messages":"Wiadomości", + "modulesTitle":"Użyte moduły", + "multiIp":"Multi IP", +@@ -1004,7 +1008,6 @@ + "registerUrl":"Zarejestruj adres URL strony", + "registerextra":"podlegający rejestracji", + "registerrule":"Registration rule", +-"reloadParams":"Załaduj ponownie konfigurację", + "reloadTimeout":"Limit czasu przeładowania", + "reloadUrls":"Załaduj ponownie adresy URL", + "rememberAuthChoice":"Zapamiętaj wybór uwierzytelniania", +diff --git a/usr/share/lemonldap-ng/manager/htdocs/static/languages/pt.json b/usr/share/lemonldap-ng/manager/htdocs/static/languages/pt.json +index 39b48f3a4..d5a9a9c2a 100644 +--- a/usr/share/lemonldap-ng/manager/htdocs/static/languages/pt.json ++++ b/usr/share/lemonldap-ng/manager/htdocs/static/languages/pt.json +@@ -359,6 +359,8 @@ + "enterPassword":"Informe senha (opcional)", + "error":"Erro", + "errors":"ERROS", ++"eventQueueName":"Event channel name", ++"eventsManagement":"Events management", + "exportedAttr":"Atributos exportados SOAP/REST", + "exportedHeaders":"Cabeçalhos exportados", + "exportedVars":"Variáveis exportadas", +@@ -605,6 +607,8 @@ + "menu":"Menu", + "menuCategory":"Categoria de menu", + "message":"Mensagem", ++"messageBroker":"Pub/Sub system", ++"messageBrokerOptions":"Pub/Sub system options", + "messages":"Mensagens", + "modulesTitle":"Módulos usados", + "multiIp":"Múltiplos IP", +@@ -1004,7 +1008,6 @@ + "registerUrl":"URL da página de registro", + "registerextra":"Registrável", + "registerrule":"Registration rule", +-"reloadParams":"Recarregar configuração", + "reloadTimeout":"Expiração de recarga", + "reloadUrls":"URLs de recarga", + "rememberAuthChoice":"Lembrar escolha de autenticação", +diff --git a/usr/share/lemonldap-ng/manager/htdocs/static/languages/pt_BR.json b/usr/share/lemonldap-ng/manager/htdocs/static/languages/pt_BR.json +index 26ca99b63..693cf96cc 100644 +--- a/usr/share/lemonldap-ng/manager/htdocs/static/languages/pt_BR.json ++++ b/usr/share/lemonldap-ng/manager/htdocs/static/languages/pt_BR.json +@@ -359,6 +359,8 @@ + "enterPassword":"Informe senha (opcional)", + "error":"Erro", + "errors":"ERROS", ++"eventQueueName":"Event channel name", ++"eventsManagement":"Events management", + "exportedAttr":"Atributos exportados SOAP/REST", + "exportedHeaders":"Cabeçalhos exportados", + "exportedVars":"Variáveis exportadas", +@@ -605,6 +607,8 @@ + "menu":"Menu", + "menuCategory":"Categoria de menu", + "message":"Mensagem", ++"messageBroker":"Pub/Sub system", ++"messageBrokerOptions":"Pub/Sub system options", + "messages":"Mensagens", + "modulesTitle":"Módulos usados", + "multiIp":"Múltiplos IPs", +@@ -1004,7 +1008,6 @@ + "registerUrl":"URL da página de registro", + "registerextra":"Registrável", + "registerrule":"Registration rule", +-"reloadParams":"Recarregar configuração", + "reloadTimeout":"Expiração de recarga", + "reloadUrls":"URLs de recarga", + "rememberAuthChoice":"Lembrar escolha de autenticação", +diff --git a/usr/share/lemonldap-ng/manager/htdocs/static/languages/ru.json b/usr/share/lemonldap-ng/manager/htdocs/static/languages/ru.json +index 506ca6c80..9c6677aa8 100644 +--- a/usr/share/lemonldap-ng/manager/htdocs/static/languages/ru.json ++++ b/usr/share/lemonldap-ng/manager/htdocs/static/languages/ru.json +@@ -359,6 +359,8 @@ + "enterPassword":"Введите пароль (опционально)", + "error":"Ошибка", + "errors":"ОШИБКИ", ++"eventQueueName":"Event channel name", ++"eventsManagement":"Events management", + "exportedAttr":"Экспортированные атрибуты SOAP/REST", + "exportedHeaders":"Экспортированные заголовки", + "exportedVars":"Экспортированные переменные", +@@ -605,6 +607,8 @@ + "menu":"Меню", + "menuCategory":"Категория меню", + "message":"Сообщение", ++"messageBroker":"Pub/Sub system", ++"messageBrokerOptions":"Pub/Sub system options", + "messages":"Сообщения", + "modulesTitle":"Используемые модули", + "multiIp":"Мульти-IP", +@@ -1004,7 +1008,6 @@ + "registerUrl":"URL страницы регистрации", + "registerextra":"Регистрируемый", + "registerrule":"Registration rule", +-"reloadParams":"Перезагрузка конфигурации", + "reloadTimeout":"Время ожидания перезагрузки", + "reloadUrls":"Перезагрузка URL", + "rememberAuthChoice":"Запомнить выбор аутентификации", +diff --git a/usr/share/lemonldap-ng/manager/htdocs/static/languages/tr.json b/usr/share/lemonldap-ng/manager/htdocs/static/languages/tr.json +index 8c162a17e..f53762af3 100644 +--- a/usr/share/lemonldap-ng/manager/htdocs/static/languages/tr.json ++++ b/usr/share/lemonldap-ng/manager/htdocs/static/languages/tr.json +@@ -359,6 +359,8 @@ + "enterPassword":"Parolayı gir (isteğe bağlı)", + "error":"Hata", + "errors":"HATALAR", ++"eventQueueName":"Event channel name", ++"eventsManagement":"Events management", + "exportedAttr":"Dışa aktarılan SOAP/REST nitelikleri", + "exportedHeaders":"Dışa aktarılan başlıklar", + "exportedVars":"Dışa Aktarılan Değişkenler", +@@ -605,6 +607,8 @@ + "menu":"Menü", + "menuCategory":"Menü kategorisi", + "message":"Mesaj", ++"messageBroker":"Pub/Sub system", ++"messageBrokerOptions":"Pub/Sub system options", + "messages":"Mesajlar", + "modulesTitle":"Kullanılan modüller", + "multiIp":"Çoklu IP", +@@ -1004,7 +1008,6 @@ + "registerUrl":"Kayıt sayfası URL'si", + "registerextra":"Kaydedilebilir", + "registerrule":"Registration rule", +-"reloadParams":"Yapılandırma yeniden yüklendi", + "reloadTimeout":"Yeniden yükleme zaman aşımı", + "reloadUrls":"URL'leri yeniden yükle", + "rememberAuthChoice":"Kimlik doğrulama seçimini hatırla", +diff --git a/usr/share/lemonldap-ng/manager/htdocs/static/languages/vi.json b/usr/share/lemonldap-ng/manager/htdocs/static/languages/vi.json +index fcf7e4b56..a07e03705 100644 +--- a/usr/share/lemonldap-ng/manager/htdocs/static/languages/vi.json ++++ b/usr/share/lemonldap-ng/manager/htdocs/static/languages/vi.json +@@ -359,6 +359,8 @@ + "enterPassword":"Nhập mật khẩu (tùy chọn)", + "error":"Lỗi", + "errors":"LỖI", ++"eventQueueName":"Event channel name", ++"eventsManagement":"Events management", + "exportedAttr":"Biến SOAP/REST đã được xuất", + "exportedHeaders":"Tiêu đề đã được xuất", + "exportedVars":"Biến đã được xuất", +@@ -605,6 +607,8 @@ + "menu":"Menu", + "menuCategory":"Loại menu", + "message":"Tin nhắn", ++"messageBroker":"Pub/Sub system", ++"messageBrokerOptions":"Pub/Sub system options", + "messages":"Tin nhắn", + "modulesTitle":"Các mô-đun đã sử dụng", + "multiIp":"Nhiều địa chỉ IP", +@@ -1004,7 +1008,6 @@ + "registerUrl":"URL trang đăng ký", + "registerextra":"Có thể đăng ký", + "registerrule":"Registration rule", +-"reloadParams":"Tải lại cấu hình", + "reloadTimeout":"hết thời gian tải lại", + "reloadUrls":"Tải lại URLs", + "rememberAuthChoice":"Ghi nhớ lựa chọn xác thực", +diff --git a/usr/share/lemonldap-ng/manager/htdocs/static/languages/zh.json b/usr/share/lemonldap-ng/manager/htdocs/static/languages/zh.json +index 311ec95ea..b07f3ba33 100644 +--- a/usr/share/lemonldap-ng/manager/htdocs/static/languages/zh.json ++++ b/usr/share/lemonldap-ng/manager/htdocs/static/languages/zh.json +@@ -359,6 +359,8 @@ + "enterPassword":"輸入密碼(選擇性)", + "error":"错误", + "errors":"錯誤", ++"eventQueueName":"Event channel name", ++"eventsManagement":"Events management", + "exportedAttr":"SOAP/REST 已匯出屬性", + "exportedHeaders":"已匯出的標頭", + "exportedVars":"已匯出的變數", +@@ -605,6 +607,8 @@ + "menu":"選單", + "menuCategory":"選單分類", + "message":"訊息", ++"messageBroker":"Pub/Sub system", ++"messageBrokerOptions":"Pub/Sub system options", + "messages":"訊息", + "modulesTitle":"已使用的模組", + "multiIp":"多 IP", +@@ -1004,7 +1008,6 @@ + "registerUrl":"註冊 URL", + "registerextra":"Registrable", + "registerrule":"Registration rule", +-"reloadParams":"設定重新載入", + "reloadTimeout":"重新載入逾時", + "reloadUrls":"重新載入 URL", + "rememberAuthChoice":"Remember authentication choice", +diff --git a/usr/share/lemonldap-ng/manager/htdocs/static/languages/zh_TW.json b/usr/share/lemonldap-ng/manager/htdocs/static/languages/zh_TW.json +index ff74edb6e..38b7ec516 100644 +--- a/usr/share/lemonldap-ng/manager/htdocs/static/languages/zh_TW.json ++++ b/usr/share/lemonldap-ng/manager/htdocs/static/languages/zh_TW.json +@@ -359,6 +359,8 @@ + "enterPassword":"輸入密碼(選擇性)", + "error":"錯誤", + "errors":"錯誤", ++"eventQueueName":"Event channel name", ++"eventsManagement":"Events management", + "exportedAttr":"SOAP/REST 已匯出屬性", + "exportedHeaders":"已匯出的標頭", + "exportedVars":"已匯出的變數", +@@ -605,6 +607,8 @@ + "menu":"選單", + "menuCategory":"選單分類", + "message":"訊息", ++"messageBroker":"Pub/Sub system", ++"messageBrokerOptions":"Pub/Sub system options", + "messages":"訊息", + "modulesTitle":"已使用的模組", + "multiIp":"多 IP", +@@ -1004,7 +1008,6 @@ + "registerUrl":"註冊 URL", + "registerextra":"Registrable", + "registerrule":"Registration rule", +-"reloadParams":"設定重新載入", + "reloadTimeout":"重新載入逾時", + "reloadUrls":"重新載入 URL", + "rememberAuthChoice":"Remember authentication choice", diff --git a/portal/Dockerfile b/portal/Dockerfile index 7834d6d..064d645 100644 --- a/portal/Dockerfile +++ b/portal/Dockerfile @@ -37,7 +37,7 @@ COPY *.patch / RUN for p in appgrid.patch jwt-type.patch app-scope.patch ignorepollers.patch \ fixedLogout.patch more-logs.patch introspec-for-public-clients.patch \ token-exchange.patch redirect-ajax.patch back-channel-debug.patch \ - crowdsec.patch recaptcha.patch \ + crowdsec.patch recaptcha.patch msg-broker.patch \ ; do echo patch $p && patch -p1 < $p; done && \ rm -f /*.patch && \ echo "# Install nginx configuration files" && \ diff --git a/portal/__testbroker__/docker-compose.yml b/portal/__testbroker__/docker-compose.yml new file mode 100644 index 0000000..b3315c6 --- /dev/null +++ b/portal/__testbroker__/docker-compose.yml @@ -0,0 +1,56 @@ +version: "3.4" + +services: + db: + container_name: test-broker-db + image: yadd/lemonldap-ng-pg-database + environment: + - POSTGRES_PASSWORD=zz + healthcheck: + test: ["CMD-SHELL", "pg_isready"] + interval: 10s + timeout: 5s + retries: 5 + networks: + - db + #ports: + # - 5432:5432 + + auth: + container_name: test-broker-auth + depends_on: + db: + condition: service_healthy + image: yadd/lemonldap-ng-portal + environment: + - LOGLEVEL=debug + - PG_SERVER=db + - LOGGER=stderr + - USERLOGGER=stderr + - PORTAL=http://auth.example.com:19876/ + - "RELAY=manager.example.com=http://manager.example.com/" + networks: + - db + #ports: + # - 19876:80 + + manager.example.com: + container_name: test-broker-manager + depends_on: + db: + condition: service_healthy + auth: + condition: service_started + image: yadd/lemonldap-ng-manager + environment: + - LOGLEVEL=debug + - PG_SERVER=db + - LOGGER=stderr + - USERLOGGER=stderr + - PORTAL=http://auth.example.com:19876/ + networks: + - db + +networks: + db: + name: db diff --git a/portal/msg-broker.patch b/portal/msg-broker.patch new file mode 100644 index 0000000..f984b07 --- /dev/null +++ b/portal/msg-broker.patch @@ -0,0 +1,15 @@ +diff --git a/usr/share/perl5/Lemonldap/NG/Portal/Main/Run.pm b/usr/share/perl5/Lemonldap/NG/Portal/Main/Run.pm +index 14c5ec1eb..06e188d67 100644 +--- a/usr/share/perl5/Lemonldap/NG/Portal/Main/Run.pm ++++ b/usr/share/perl5/Lemonldap/NG/Portal/Main/Run.pm +@@ -742,7 +742,9 @@ sub _deleteSession { + ) unless ($preserveCookie); + } + +- HANDLER->localUnlog( $req, $session->id ); ++ # Publishing an "unlog" event will automatically remove the corresponding ++ # session from handler cache. See publishEvent into Handler::Main::Run ++ HANDLER->publishEvent( $req, { action => 'unlog', id => $session->id } ); + $session->remove; + + # Create an obsolete cookie to remove it diff --git a/portal/test b/portal/test index 07f2539..0a60309 100755 --- a/portal/test +++ b/portal/test @@ -10,3 +10,13 @@ set -e cd __test__ $COMPOSE run --rm test $COMPOSE down + +cd ../__testbroker__ +$COMPOSE up -d +echo "Be sure conf is loaded" +docker exec test-broker-manager /usr/share/docker-llng/updateConf get macros && echo +docker exec test-broker-auth /usr/share/docker-llng/updateConf get macros && echo +echo "Modify macro on manager, define AA => CC" +docker exec test-broker-manager /usr/share/docker-llng/updateConf set macros AA CC +echo "Verify propagation on portal, should get CC when querying for macros AA" +docker exec test-broker-auth /usr/share/docker-llng/updateConf get macros AA | grep CC diff --git a/uwsgi-portal/Dockerfile b/uwsgi-portal/Dockerfile index ae9a733..87707cc 100644 --- a/uwsgi-portal/Dockerfile +++ b/uwsgi-portal/Dockerfile @@ -34,7 +34,7 @@ COPY *.patch / RUN for p in appgrid.patch jwt-type.patch app-scope.patch ignorepollers.patch \ fixedLogout.patch more-logs.patch introspec-for-public-clients.patch \ token-exchange.patch redirect-ajax.patch back-channel-debug.patch \ - crowdsec.patch recaptcha.patch \ + crowdsec.patch recaptcha.patch msg-broker.patch \ ; do echo patch $p && patch -p1 < $p; done && \ rm -f /*.patch && \ echo "# Install nginx configuration files" && \ diff --git a/uwsgi-portal/msg-broker.patch b/uwsgi-portal/msg-broker.patch new file mode 100644 index 0000000..f984b07 --- /dev/null +++ b/uwsgi-portal/msg-broker.patch @@ -0,0 +1,15 @@ +diff --git a/usr/share/perl5/Lemonldap/NG/Portal/Main/Run.pm b/usr/share/perl5/Lemonldap/NG/Portal/Main/Run.pm +index 14c5ec1eb..06e188d67 100644 +--- a/usr/share/perl5/Lemonldap/NG/Portal/Main/Run.pm ++++ b/usr/share/perl5/Lemonldap/NG/Portal/Main/Run.pm +@@ -742,7 +742,9 @@ sub _deleteSession { + ) unless ($preserveCookie); + } + +- HANDLER->localUnlog( $req, $session->id ); ++ # Publishing an "unlog" event will automatically remove the corresponding ++ # session from handler cache. See publishEvent into Handler::Main::Run ++ HANDLER->publishEvent( $req, { action => 'unlog', id => $session->id } ); + $session->remove; + + # Create an obsolete cookie to remove it