Commit d67f32d2 authored by Christophe Maudoux's avatar Christophe Maudoux 🐛

Merge branch 'v2.0'

parents cc4e53c3 683d5f0d
Pipeline #7924 failed with stages
in 23 minutes and 16 seconds
......@@ -115,6 +115,11 @@ License: CC-3
Comment: This work, "star1.png", is a derivative of
"Golden star with red border.png" by ANGELUS, under CC-BYSA-3.0.
Files: lemonldap-ng-portal/site/htdocs/static/common/icons/notifsExplorer.png
Copyright: Various artists
License: CC-BY-NC-ND-3.0 or GFDL-1.3
Comment: downloaded from https://commons.wikimedia.org
Files: lemonldap-ng-portal/site/htdocs/static/common/icons/decryptValue.png
Copyright: Christophe Maudoux <chrmdx@gmail.com>
License: CC-3
......
......@@ -1130,6 +1130,11 @@ tidy: clean
find lemon*/ -type f \( -name '*.pm' -or -name '*.pl' -or -name '*.fcgi' -or -name '*.t' \) -print -exec perltidy -se -b {} \; ; \
else echo "Wrong perltidy version, please install Perl::Tidy@20181120" ; exit 1 ;\
fi
for f in `find lemon*/ -type f \( -name '*.pm' -or -name '*.pl' -or -name '*.fcgi' -or -name '*.t' \)`; do \
echo -n $$f; \
perltidy -se -b $$f; \
echo; \
done
find lemon*/ -name '*.bak' -delete
$(MAKE) json
......
......@@ -34,6 +34,9 @@
RewriteCond "%{REQUEST_URI}" "!^/(?:(?:static|javascript|favicon).*|.*\.fcgi(?:/.*)?)$"
RewriteRule "^/(.+)$" "/index.fcgi/$1" [PT]
# Uncomment this to mitigate memory leaks when using Perl 5.16
# FcgidMaxRequestsPerProcess 500
# Note that Content-Security-Policy header is generated by portal itself
<Files *.fcgi>
SetHandler fcgid-script
......
......@@ -35,6 +35,9 @@
RewriteCond "%{REQUEST_URI}" "!^/(?:(?:static|javascript|favicon).*|.*\.fcgi(?:/.*)?)$"
RewriteRule "^/(.+)$" "/index.fcgi/$1" [PT]
# Uncomment this to mitigate memory leaks when using Perl 5.16
# FcgidMaxRequestsPerProcess 500
# Note that Content-Security-Policy header is generated by portal itself
<Files *.fcgi>
SetHandler fcgid-script
......
......@@ -30,6 +30,9 @@
RewriteCond "%{REQUEST_URI}" "!^/(?:(?:static|javascript|favicon).*|.*\.fcgi(?:/.*)?)$"
RewriteRule "^/(.+)$" "/index.fcgi/$1" [PT]
# Uncomment this to mitigate memory leaks when using Perl 5.16
# FcgidMaxRequestsPerProcess 500
# Note that Content-Security-Policy header is generated by portal itself
<Files *.fcgi>
SetHandler fcgid-script
......
......@@ -43,7 +43,9 @@ Build-Depends-Indep: libapache-session-perl <!nocheck>,
libstring-random-perl <!nocheck>,
libtest-mockobject-perl <!nocheck>,
libtest-pod-perl <!nocheck>,
libtest-output-perl <!nocheck>,
libtext-unidecode-perl <!nocheck>,
libtime-fake-perl <!nocheck>,
libunicode-string-perl <!nocheck>,
liburi-perl <!nocheck>,
libwww-perl <!nocheck>,
......
......@@ -21,3 +21,14 @@ GROUP=__GROUP__
#ENGINE=FCGI::EV
#ENGINE=FCGI::Engine
#ENGINE=FCGI::Engine::ProcManager
# Process recycling
# When running with Perl 5.16, you might encounter memory
# leaks when running the FastCGI server
# By default, we restart each worker after 500 requests to mitigate
# the leak. You can finetune these settings here.
# See also FCGI::ProcManager::Constrained(3)
PM_MAX_REQUESTS=500
#PM_SIZECHECK_NUM_REQUESTS=10
#PM_MAX_SIZE=100000
......@@ -10,9 +10,9 @@ use Lemonldap::NG::Handler::Main::Reload;
our $VERSION = '2.1.0';
our (
$foreground, $engine, $nproc, $pidFile,
$socket, $user, $listen, $group,
$customFunctionsFile, %plackOptions
$foreground, $engine, $nproc, $pidFile,
$socket, $user, $listen, $group,
$procmanager, $customFunctionsFile, %plackOptions
);
my %_apps;
......@@ -29,6 +29,9 @@ $user ||= $ENV{USER};
$group ||= $ENV{GROUP};
$customFunctionsFile ||= $ENV{CUSTOM_FUNCTIONS_FILE};
# If the user specified any PM_ constrains, run under ::Constrained
$procmanager = "FCGI::ProcManager::Constrained" if grep /^PM_/, keys %ENV;
#Getopt::Long::Configure ("bundling_values");
GetOptions(
'foreground' => \$foreground,
......@@ -93,7 +96,7 @@ my %builder = (
die "Unable to load $_[0]->{SCRIPT_FILENAME}";
}
return $_apps{$script}->(@_);
}
}
},
);
......@@ -126,6 +129,7 @@ $server->parse_options(
'--proc-title' => 'llng-fastcgi-server',
( $foreground ? () : '--daemonize' ),
'--no-default-middleware',
( $procmanager ? ( '--manager', $procmanager ) : () ),
%plackOptions,
);
......@@ -216,14 +220,10 @@ Plack::Handler engine, default to FCGI (see below)
=item --plackOptions:
other options to pass to Plack. This multi-valued parameter must have
"key=value" values.
Example to use L<FCGI::ProcManager::Constrained> instead of default FCGI manager
(L<FCGI::ProcManager>):
other options to pass to the Plack handler. This multi-valued parameter must
have "key=value" values.
llng-fastcgi-server -u nobody -g nobody -s /run/llng.sock -e FCGI -n 10 \
--plackOptions manager=FCGI::ProcManager::Constrained
See Plack::Handler::FCGI for a list of options for the default FCGI engine
=back
......@@ -236,22 +236,6 @@ other engines can be used:
It uses L<FCGI::ProcManager> as manager. Other managers:
=over
=item L<FCGI::ProcManager::Constrained>
Example to launch it:
llng-fastcgi-server -u nobody -g nobody -s /run/llng.sock -e FCGI -n 10 \
--plackOptions manager=FCGI::ProcManager::Constrained
You can then set environment values (in /etc/default/llng-fastcgi-server file
for example):
PM_MAX_REQUESTS=10000
PM_SIZECHECK_NUM_REQUESTS=100
PM_MAX_SIZE=300000
=item L<FCGI::ProcManager::Dynamic>
llng-fastcgi-server -u nobody -g nobody -s /run/llng.sock -e FCGI -n 10 \
......
......@@ -107,6 +107,7 @@ sub store {
$req->content( to_json($conf) );
$req->header( 'Content-Type' => 'application/json' );
my $resp = $self->ua->request($req);
if ( $resp->is_success ) {
my $res;
eval { $res = from_json( $resp->content, { allow_nonref => 1 } ) };
......
......@@ -41,7 +41,7 @@ sub available {
closedir D;
@conf =
sort { $a <=> $b }
map { /lmConf-(\d+)\.yaml/ ? ( $1 + 0 ) : () } @conf;
map { /lmConf-(\d+)\.yaml/ ? ( $1 + 0 ) : () } @conf;
return @conf;
}
......
......@@ -24,7 +24,7 @@ use constant MANAGERSECTION => "manager";
use constant SESSIONSEXPLORERSECTION => "sessionsExplorer";
use constant APPLYSECTION => "apply";
our $hashParameters = qr/^(?:(?:l(?:o(?:ca(?:lSessionStorageOption|tionRule)|goutService)|dapExportedVar|wp(?:Ssl)?Opt)|(?:(?:d(?:emo|bi)|facebook|webID)ExportedVa|exported(?:Heade|Va)|issuerDBGetParamete)r|re(?:moteGlobalStorageOption|st2f(?:Verify|Init)Arg|loadUrl)|g(?:r(?:antSessionRule|oup)|lobalStorageOption)|n(?:otificationStorageOption|ginxCustomHandler)|macro)s|o(?:idc(?:S(?:ervice(?:DynamicRegistrationEx(?:portedVar|traClaim)s|MetaDataAuthnContext)|torageOptions)|RPMetaData(?:(?:Option(?:sExtraClaim)?|ExportedVar|Macro)s|Node)|OPMetaData(?:(?:ExportedVar|Option)s|J(?:SON|WKS)|Node))|penIdExportedVars)|s(?:aml(?:S(?:PMetaData(?:(?:ExportedAttribute|Option|Macro)s|Node|XML)|torageOptions)|IDPMetaData(?:(?:ExportedAttribute|Option)s|Node|XML))|essionDataToRemember|laveExportedVars|fExtra)|c(?:as(?:A(?:ppMetaData(?:(?:ExportedVar|Option|Macro)s|Node)|ttributes)|S(?:rvMetaData(?:(?:ExportedVar|Option)s|Node)|torageOptions))|(?:ustom(?:Plugins|Add)Param|ombModule)s)|p(?:ersistentStorageOptions|o(?:rtalSkinRules|st))|a(?:ut(?:hChoiceMod|oSigninR)ules|pplicationList)|v(?:hostOptions|irtualHost)|S(?:MTPTLSOpts|SLVarIf))$/;
our $boolKeys = qr/^(?:s(?:aml(?:IDP(?:MetaDataOptions(?:(?:Check(?:S[LS]OMessageSignatur|Audienc|Tim)|IsPassiv)e|A(?:llow(?:LoginFromIDP|ProxiedAuthn)|daptSessionUtime)|Force(?:Authn|UTF8)|StoreSAMLToken|RelayStateURL)|SSODescriptorWantAuthnRequestsSigned)|S(?:P(?:MetaDataOptions(?:(?:CheckS[LS]OMessageSignatur|OneTimeUs)e|EnableIDPInitiatedURL|ForceUTF8)|SSODescriptor(?:WantAssertion|AuthnRequest)sSigned)|erviceUseCertificateInResponse)|DiscoveryProtocol(?:Activation|IsPassive)|CommonDomainCookieActivation|UseQueryStringSpecific|MetadataForceUTF8)|ingle(?:Session(?:UserByIP)?|(?:UserBy)?IP)|oap(?:Session|Config)Server|t(?:ayConnecte|orePasswor)d|kipRenewConfirmation|fRemovedUseNotif|laveDisplayLogo|howLanguages|slByAjax)|o(?:idc(?:RPMetaDataOptions(?:Re(?:freshToken|quirePKCE)|LogoutSessionRequired|IDTokenForceClaims|BypassConsent|AllowOffline|Public)|ServiceAllow(?:(?:AuthorizationCode|Implicit|Hybrid)Flow|DynamicRegistration)|OPMetaDataOptions(?:(?:CheckJWTSignatur|UseNonc)e|StoreIDToken))|ldNotifFormat)|p(?:ortal(?:Display(?:Re(?:setPassword|gister)|GeneratePassword|PasswordPolicy)|ErrorOn(?:ExpiredSession|MailNotFound)|(?:CheckLogin|Statu)s|OpenLinkInNewWindow|RequireOldPassword|ForceAuthn|AntiFrame)|roxyUseSoap)|c(?:a(?:ptcha_(?:register|login|mail)_enabled|sSrvMetaDataOptions(?:Gateway|Renew))|heck(?:User(?:Display(?:PersistentInfo|EmptyValues))?|State|XSS)|o(?:ntextSwitchingStopWithLogout|mpactConf|rsEnabled)|da)|l(?:dap(?:(?:Group(?:DecodeSearchedValu|Recursiv)|UsePasswordResetAttribut)e|(?:AllowResetExpired|Set)Password|ChangePasswordAsUser|PpolicyControl|ITDS)|oginHistoryEnabled)|i(?:ssuerDB(?:OpenID(?:Connect)?|SAML|CAS|Get)Activation|mpersonationSkipEmptyValues)|no(?:tif(?:ication(?:Server(?:(?:POS|GE)T|DELETE)?)?|y(?:Deleted|Other))|AjaxHook)|to(?:tp2f(?:UserCan(?:Chang|Remov)eKey|DisplayExistingSecret)|kenUseGlobalStorage)|u(?:se(?:RedirectOn(?:Forbidden|Error)|SafeJail)|2fUserCanRemoveKey|pgradeSession)|re(?:st(?:(?:Session|Config)Server|ExportSecretKeys)|freshSessions)|(?:mai(?:lOnPasswordChang|ntenanc)|vhostMaintenanc)e|d(?:isablePersistentStorage|biDynamicHashEnabled)|br(?:owsersDontStorePassword|uteForceProtection)|(?:(?:globalLogout|active)Tim|wsdlServ)er|h(?:ideOldPassword|ttpOnly)|yubikey2fUserCanRemoveKey|krb(?:RemoveDomain|ByJs))$/;
our $boolKeys = qr/^(?:s(?:aml(?:IDP(?:MetaDataOptions(?:(?:Check(?:S[LS]OMessageSignatur|Audienc|Tim)|IsPassiv)e|A(?:llow(?:LoginFromIDP|ProxiedAuthn)|daptSessionUtime)|Force(?:Authn|UTF8)|StoreSAMLToken|RelayStateURL)|SSODescriptorWantAuthnRequestsSigned)|S(?:P(?:MetaDataOptions(?:(?:CheckS[LS]OMessageSignatur|OneTimeUs)e|EnableIDPInitiatedURL|ForceUTF8)|SSODescriptor(?:WantAssertion|AuthnRequest)sSigned)|erviceUseCertificateInResponse)|DiscoveryProtocol(?:Activation|IsPassive)|CommonDomainCookieActivation|UseQueryStringSpecific|MetadataForceUTF8)|ingle(?:Session(?:UserByIP)?|(?:UserBy)?IP)|oap(?:Session|Config)Server|t(?:ayConnecte|orePasswor)d|kipRenewConfirmation|fRemovedUseNotif|laveDisplayLogo|howLanguages|slByAjax)|o(?:idc(?:RPMetaDataOptions(?:Re(?:freshToken|quirePKCE)|LogoutSessionRequired|IDTokenForceClaims|BypassConsent|AllowOffline|Public)|ServiceAllow(?:(?:AuthorizationCode|Implicit|Hybrid)Flow|DynamicRegistration)|OPMetaDataOptions(?:(?:CheckJWTSignatur|UseNonc)e|StoreIDToken))|ldNotifFormat)|p(?:ortal(?:Display(?:Re(?:setPassword|gister)|GeneratePassword|PasswordPolicy)|ErrorOn(?:ExpiredSession|MailNotFound)|(?:CheckLogin|Statu)s|OpenLinkInNewWindow|RequireOldPassword|ForceAuthn|AntiFrame)|roxyUseSoap)|c(?:a(?:ptcha_(?:register|login|mail)_enabled|sSrvMetaDataOptions(?:Gateway|Renew))|heck(?:User(?:Display(?:PersistentInfo|EmptyValues))?|State|XSS)|o(?:ntextSwitchingStopWithLogout|mpactConf|rsEnabled)|da)|l(?:dap(?:(?:Group(?:DecodeSearchedValu|Recursiv)|UsePasswordResetAttribut)e|(?:AllowResetExpired|Set)Password|ChangePasswordAsUser|PpolicyControl|ITDS)|oginHistoryEnabled)|no(?:tif(?:ication(?:Server(?:(?:POS|GE)T|DELETE)?|sExplorer)?|y(?:Deleted|Other))|AjaxHook)|i(?:ssuerDB(?:OpenID(?:Connect)?|SAML|CAS|Get)Activation|mpersonationSkipEmptyValues)|to(?:tp2f(?:UserCan(?:Chang|Remov)eKey|DisplayExistingSecret)|kenUseGlobalStorage)|u(?:se(?:RedirectOn(?:Forbidden|Error)|SafeJail)|2fUserCanRemoveKey|pgradeSession)|br(?:uteForceProtection(?:IncrementalTempo)?|owsersDontStorePassword)|re(?:st(?:(?:Session|Config)Server|ExportSecretKeys)|freshSessions)|(?:mai(?:lOnPasswordChang|ntenanc)|vhostMaintenanc)e|d(?:isablePersistentStorage|biDynamicHashEnabled)|g(?:roupsBeforeMacros|lobalLogoutTimer)|h(?:ideOldPassword|ttpOnly)|yubikey2fUserCanRemoveKey|(?:activeTim|wsdlServ)er|krb(?:RemoveDomain|ByJs))$/;
our @sessionTypes = ( 'remoteGlobal', 'global', 'localSession', 'persistent', 'saml', 'oidc', 'cas' );
......
......@@ -19,8 +19,10 @@ sub defaultValues {
'authentication' => 'Demo',
'available2F' => 'UTOTP,TOTP,U2F,REST,Mail2F,Ext2F,Yubikey,Radius',
'available2FSelfRegistration' => 'TOTP,U2F,Yubikey',
'bruteForceProtectionLockTimes' => '5 15 60 300 600',
'bruteForceProtectionMaxAge' => 300,
'bruteForceProtectionMaxFailed' => 3,
'bruteForceProtectionMaxLockTime' => 900,
'bruteForceProtectionTempo' => 30,
'captcha_mail_enabled' => 1,
'captcha_register_enabled' => 1,
......@@ -172,6 +174,7 @@ sub defaultValues {
'notificationServerPOST' => 1,
'notificationServerSentAttributes' =>
'uid reference date title subtitle text check',
'notificationsMaxRetrieve' => 3,
'notificationStorage' => 'File',
'notificationStorageOptions' => {
'dirName' => '/var/lib/lemonldap-ng/notifications'
......@@ -220,6 +223,8 @@ sub defaultValues {
'passwordPolicyMinSize' => 0,
'passwordPolicyMinUpper' => 0,
'passwordResetAllowedRetries' => 3,
'persistentSessionAttributes' =>
'_loginHistory _2fDevices notification_',
'port' => -1,
'portal' => 'http://auth.example.com/',
'portalAntiFrame' => 1,
......@@ -321,6 +326,7 @@ sub defaultValues {
'slaveExportedVars' => {},
'SMTPServer' => '',
'SMTPTLS' => '',
'soapProxyUrn' => 'urn:Lemonldap/NG/Common/PSGI/SOAPService',
'SSLAuthnLevel' => 5,
'SSLVar' => 'SSL_CLIENT_S_DN_Email',
'SSLVarIf' => {},
......
......@@ -198,8 +198,8 @@ sub virtualHosts {
# If rule contains a comment or an AuthLevel, split them
if ( $query eq 'locationRules' ) {
$res->{comment} = '';
$res->{level} = '';
$res->{level} = $1 if ( $r =~ s/\(\?#AuthnLevel=(-?\d+)\)// );
$res->{level} = '';
$res->{level} = $1 if ( $r =~ s/\(\?#AuthnLevel=(-?\d+)\)// );
if ( $r =~ s/\(\?#(.*?)\)// ) {
$res->{title} = $res->{comment} = $1;
}
......@@ -710,9 +710,9 @@ sub combModules {
my $res = [];
foreach my $mod ( keys %$val ) {
my $tmp;
$tmp->{title} = $mod;
$tmp->{id} = "combModules/$mod";
$tmp->{type} = 'cmbModule';
$tmp->{title} = $mod;
$tmp->{id} = "combModules/$mod";
$tmp->{type} = 'cmbModule';
$tmp->{data}->{$_} = $val->{$mod}->{$_} foreach (qw(type for));
my $over = $val->{$mod}->{over} // {};
$tmp->{data}->{over} = [ map { [ $_, $over->{$_} ] } keys %$over ];
......@@ -786,8 +786,8 @@ sub metadata {
}
# Find next and previous conf
my @a = $self->confAcc->available;
my $id = -1;
my @a = $self->confAcc->available;
my $id = -1;
my ($ind) = map { $id++; $_ == $res->{cfgNum} ? ($id) : () } @a;
if ($ind) { $res->{prev} = $a[ $ind - 1 ]; }
if ( defined $ind and $ind < $#a ) {
......
......@@ -166,9 +166,9 @@ sub serviceToXML {
foreach (@param_assertion) {
my @_tab = split( /;/, $self->getValue( $_, $conf ) );
$template->param( $_ . 'Default', $_tab[0] ? 'true' : 'false' );
$template->param( $_ . 'Index', $_tab[1] );
$template->param( $_ . 'Binding', $_tab[2] );
$template->param( $_ . 'Default', $_tab[0] ? 'true' : 'false' );
$template->param( $_ . 'Index', $_tab[1] );
$template->param( $_ . 'Binding', $_tab[2] );
$template->param( $_ . 'Location', $_tab[3] );
}
......
......@@ -19,6 +19,11 @@ sub import {
}
}
has extension => (
is => 'rw',
default => 'json'
);
has notifField => (
is => 'rw',
builder => sub {
......@@ -31,6 +36,14 @@ has notifField => (
}
);
sub BUILD {
my $self = shift;
$self->extension('xml') if $self->p->conf->{oldNotifFormat};
$self->logger->debug( 'Use extension "'
. $self->extension
. '" to store notification files' );
}
sub getNotifications {
my ( $self, $uid ) = @_;
my $forAll = $self->get( $self->conf->{notificationWildcard} );
......@@ -49,4 +62,17 @@ sub getNotifications {
}
}
sub getAcceptedNotifs {
my ( $self, $uid, $ref ) = @_;
my $forAll =
$self->getAccepted( $self->conf->{notificationWildcard}, $ref );
my $forUser = $self->getAccepted( $uid, $ref );
if ( $forUser and $forAll ) {
return { %$forUser, %$forAll };
}
else {
return ( ( $forUser ? $forUser : $forAll ), $forUser );
}
}
1;
......@@ -92,6 +92,38 @@ sub get {
return $result;
}
# Returns accepted notifications corresponding to the user $uid.
# If $ref is set, returns only notification corresponding to this reference.
sub getAccepted {
my ( $self, $uid, $ref ) = @_;
return () unless ($uid);
$self->_execute(
"SELECT * FROM "
. $self->dbiTable
. " WHERE done IS NOT NULL AND uid=?"
. ( $ref ? " AND ref=?" : '' )
. " ORDER BY date",
$uid,
( $ref ? $ref : () )
) or return ();
my $result;
while ( my $h = $self->sth->fetchrow_hashref() ) {
# Get XML message
my $xml = $h->{xml};
# Decode it to get the correct uncoded string
Encode::from_to( $xml, "utf8", "iso-8859-1", Encode::FB_CROAK );
# Store message in result
my $identifier =
&getIdentifier( $self, $h->{uid}, $h->{ref}, $h->{date} );
$result->{$identifier} = $xml;
}
$self->logger->warn( $self->sth->err() ) if ( $self->sth->err() );
return $result;
}
## @method hashref getAll()
# Return all pending notifications.
# @return hashref where keys are internal reference and values are hashref with
......
......@@ -14,11 +14,8 @@ our $VERSION = '2.1.0';
extends 'Lemonldap::NG::Common::Notifications';
our $ext = 'json';
sub import {
shift;
$ext = 'xml' if ( $_[0] eq 'XML' );
return Lemonldap::NG::Common::Notifications->import(@_);
}
......@@ -38,6 +35,7 @@ has fileNameSeparator => ( is => 'rw', default => '_' );
# If $ref is set, returns only notification corresponding to this reference.
sub get {
my ( $self, $uid, $ref ) = @_;
my $ext = $self->extension;
return () unless ($uid);
my $fns = $self->{fileNameSeparator};
my $identifier = &getIdentifier( $self, $uid, $ref );
......@@ -58,12 +56,37 @@ sub get {
return $files;
}
# Returns accepted notification corresponding to the user $uid.
# If $ref is set, returns only notification corresponding to this reference.
sub getAccepted {
my ( $self, $uid, $ref ) = @_;
return () unless ($uid);
my $fns = $self->{fileNameSeparator};
my $identifier = &getIdentifier( $self, $uid, $ref );
opendir D, $self->{dirName};
my @notif = grep /^\d{8}${fns}${identifier}\S*\.done$/, readdir(D);
closedir D;
my $files;
foreach my $file (@notif) {
unless ( open F, '<', $self->{dirName} . "/$file" ) {
$self->logger->error(
"Unable to read notification $self->{dirName}/$file");
next;
}
$files->{$file} = join( '', <F> );
}
return $files;
}
## @method hashref getAll()
# Return all pending notifications.
# @return hashref where keys are internal reference and values are hashref with
# keys date, uid, ref and condition.
sub getAll {
my $self = shift;
my $ext = $self->extension;
opendir D, $self->{dirName};
my @notif;
my $fns = $self->{fileNameSeparator};
......@@ -89,6 +112,7 @@ sub getAll {
# keys date, uid, ref and condition.
sub getExisting {
my $self = shift;
my $ext = $self->extension;
opendir D, $self->{dirName};
my @notif;
my $fns = $self->{fileNameSeparator};
......@@ -113,6 +137,7 @@ sub getExisting {
# @param $myref identifier returned by get() or getAll()
sub delete {
my ( $self, $myref ) = @_;
my $ext = $self->extension;
my $new = ( $myref =~ /(.*?)(?:\.$ext)$/ )[0] . '.done';
return rename( $self->{dirName} . "/$myref", $self->{dirName} . "/$new" );
}
......@@ -129,6 +154,7 @@ sub purge {
# Insert a new notification
sub newNotif {
my ( $self, $date, $uid, $ref, $condition, $content ) = @_;
my $ext = $self->extension;
my $fns = $self->{fileNameSeparator};
$fns ||= '_';
my @t = split( /\D+/, $date );
......
......@@ -42,10 +42,11 @@ sub newNotification {
unless ( exists $notif->{condition} ) {
$self->userLogger->info(
"Set defaultCondition ($defaultCond) for notification $notif->{reference}");
"Set defaultCondition ($defaultCond) for notification $notif->{reference}"
);
$notif->{condition} = $defaultCond;
}
push @data, ( $notif->{condition} );
$notif->{date} =~ s/^(\d{4}-\d{2}-\d{2}).*$/$1/;
my $body = to_json($notif);
......
......@@ -9,7 +9,7 @@ package Lemonldap::NG::Common::Notifications::LDAP;
use strict;
use Mouse;
use Time::Local;
use MIME::Base64;
use MIME::Base64 qw/encode_base64url/;
use Net::LDAP;
use utf8;
......@@ -45,6 +45,16 @@ has ldapBindDN => (
}
);
has ldapBindPassword => (
is => 'ro',
lazy => 1,
default => sub {
$_[0]
->p->logger->warn('Warning: "ldapBindPassword" parameter is not set');
return '';
}
);
# Returns notifications corresponding to the user $uid.
# If $ref is set, returns only notification corresponding to this reference.
sub get {
......@@ -57,7 +67,39 @@ sub get {
. ( $ref ? '(description={ref}' . $ref . ')' : '' ) . ')';
my @entries = _search( $self, $filter );
my $result = {};
my $result;
foreach my $entry (@entries) {
my @notifValues = $entry->get_value('description');
my $f = {};
foreach (@notifValues) {
my ( $k, $v ) = ( $_ =~ /\{(.*?)\}(.*)/smg );
$v = decodeLdapValue($v);
$f->{$k} = $v;
}
my $xml = $f->{xml};
utf8::encode($xml);
my $identifier =
&getIdentifier( $self, $f->{uid}, $f->{ref}, $f->{date} );
$result->{$identifier} = "$xml";
$self->logger->info("notification $identifier found");
}
return $result;
}
# Returns accepted notifications corresponding to the user $uid.
# If $ref is set, returns only notification corresponding to this reference.
sub getAccepted {
my ( $self, $uid, $ref ) = @_;
return () unless ($uid);
my $filter =
'(&(objectClass=applicationProcess)(description={done}*)'
. "(description={uid}$uid)"
. ( $ref ? '(description={ref}' . $ref . ')' : '' ) . ')';
my @entries = _search( $self, $filter );
my $result;
foreach my $entry (@entries) {
my @notifValues = $entry->get_value('description');
my $f = {};
......@@ -195,9 +237,8 @@ sub newNotif {
return ( 0, "Bad date" ) if ($@);
$date =~ s/-//g;
return ( 0, "Bad date" ) unless ( $date =~ /^\d{8}/ );
my $cn = "${date}${fns}${uid}${fns}" . encode_base64( $ref, '' );
$cn .= "${fns}" . encode_base64( $condition, '' ) if $condition;
$xml = $xml->serialize();
my $cn = "${date}${fns}${uid}${fns}" . encode_base64url( $ref, '' );
$cn .= "${fns}" . encode_base64url( $condition, '' ) if $condition;
my $fields =
$condition =~ /.+/
......@@ -347,7 +388,7 @@ sub _store {
);
if ( $add->code ) {
$self->logError($add);
$self->logger->error( $add->error );
return 0;
}
......
......@@ -45,7 +45,7 @@ sub newNotification {
$self->logger->error("$err");
return 0;