Merge branch 'v2.0'

This commit is contained in:
Christophe Maudoux 2020-02-25 20:26:05 +01:00
commit d67f32d2b2
308 changed files with 4756 additions and 8252 deletions

View File

@ -115,6 +115,11 @@ License: CC-3
Comment: This work, "star1.png", is a derivative of Comment: This work, "star1.png", is a derivative of
"Golden star with red border.png" by ANGELUS, under CC-BYSA-3.0. "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 Files: lemonldap-ng-portal/site/htdocs/static/common/icons/decryptValue.png
Copyright: Christophe Maudoux <chrmdx@gmail.com> Copyright: Christophe Maudoux <chrmdx@gmail.com>
License: CC-3 License: CC-3

View File

@ -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 {} \; ; \ 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 ;\ else echo "Wrong perltidy version, please install Perl::Tidy@20181120" ; exit 1 ;\
fi 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 find lemon*/ -name '*.bak' -delete
$(MAKE) json $(MAKE) json

View File

@ -34,6 +34,9 @@
RewriteCond "%{REQUEST_URI}" "!^/(?:(?:static|javascript|favicon).*|.*\.fcgi(?:/.*)?)$" RewriteCond "%{REQUEST_URI}" "!^/(?:(?:static|javascript|favicon).*|.*\.fcgi(?:/.*)?)$"
RewriteRule "^/(.+)$" "/index.fcgi/$1" [PT] 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 # Note that Content-Security-Policy header is generated by portal itself
<Files *.fcgi> <Files *.fcgi>
SetHandler fcgid-script SetHandler fcgid-script

View File

@ -35,6 +35,9 @@
RewriteCond "%{REQUEST_URI}" "!^/(?:(?:static|javascript|favicon).*|.*\.fcgi(?:/.*)?)$" RewriteCond "%{REQUEST_URI}" "!^/(?:(?:static|javascript|favicon).*|.*\.fcgi(?:/.*)?)$"
RewriteRule "^/(.+)$" "/index.fcgi/$1" [PT] 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 # Note that Content-Security-Policy header is generated by portal itself
<Files *.fcgi> <Files *.fcgi>
SetHandler fcgid-script SetHandler fcgid-script

View File

@ -30,6 +30,9 @@
RewriteCond "%{REQUEST_URI}" "!^/(?:(?:static|javascript|favicon).*|.*\.fcgi(?:/.*)?)$" RewriteCond "%{REQUEST_URI}" "!^/(?:(?:static|javascript|favicon).*|.*\.fcgi(?:/.*)?)$"
RewriteRule "^/(.+)$" "/index.fcgi/$1" [PT] 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 # Note that Content-Security-Policy header is generated by portal itself
<Files *.fcgi> <Files *.fcgi>
SetHandler fcgid-script SetHandler fcgid-script

2
debian/control vendored
View File

@ -43,7 +43,9 @@ Build-Depends-Indep: libapache-session-perl <!nocheck>,
libstring-random-perl <!nocheck>, libstring-random-perl <!nocheck>,
libtest-mockobject-perl <!nocheck>, libtest-mockobject-perl <!nocheck>,
libtest-pod-perl <!nocheck>, libtest-pod-perl <!nocheck>,
libtest-output-perl <!nocheck>,
libtext-unidecode-perl <!nocheck>, libtext-unidecode-perl <!nocheck>,
libtime-fake-perl <!nocheck>,
libunicode-string-perl <!nocheck>, libunicode-string-perl <!nocheck>,
liburi-perl <!nocheck>, liburi-perl <!nocheck>,
libwww-perl <!nocheck>, libwww-perl <!nocheck>,

View File

@ -21,3 +21,14 @@ GROUP=__GROUP__
#ENGINE=FCGI::EV #ENGINE=FCGI::EV
#ENGINE=FCGI::Engine #ENGINE=FCGI::Engine
#ENGINE=FCGI::Engine::ProcManager #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

View File

@ -10,9 +10,9 @@ use Lemonldap::NG::Handler::Main::Reload;
our $VERSION = '2.1.0'; our $VERSION = '2.1.0';
our ( our (
$foreground, $engine, $nproc, $pidFile, $foreground, $engine, $nproc, $pidFile,
$socket, $user, $listen, $group, $socket, $user, $listen, $group,
$customFunctionsFile, %plackOptions $procmanager, $customFunctionsFile, %plackOptions
); );
my %_apps; my %_apps;
@ -29,6 +29,9 @@ $user ||= $ENV{USER};
$group ||= $ENV{GROUP}; $group ||= $ENV{GROUP};
$customFunctionsFile ||= $ENV{CUSTOM_FUNCTIONS_FILE}; $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"); #Getopt::Long::Configure ("bundling_values");
GetOptions( GetOptions(
'foreground' => \$foreground, 'foreground' => \$foreground,
@ -93,7 +96,7 @@ my %builder = (
die "Unable to load $_[0]->{SCRIPT_FILENAME}"; die "Unable to load $_[0]->{SCRIPT_FILENAME}";
} }
return $_apps{$script}->(@_); return $_apps{$script}->(@_);
} }
}, },
); );
@ -126,6 +129,7 @@ $server->parse_options(
'--proc-title' => 'llng-fastcgi-server', '--proc-title' => 'llng-fastcgi-server',
( $foreground ? () : '--daemonize' ), ( $foreground ? () : '--daemonize' ),
'--no-default-middleware', '--no-default-middleware',
( $procmanager ? ( '--manager', $procmanager ) : () ),
%plackOptions, %plackOptions,
); );
@ -216,14 +220,10 @@ Plack::Handler engine, default to FCGI (see below)
=item --plackOptions: =item --plackOptions:
other options to pass to Plack. This multi-valued parameter must have other options to pass to the Plack handler. This multi-valued parameter must
"key=value" values. have "key=value" values.
Example to use L<FCGI::ProcManager::Constrained> instead of default FCGI manager See Plack::Handler::FCGI for a list of options for the default FCGI engine
(L<FCGI::ProcManager>):
llng-fastcgi-server -u nobody -g nobody -s /run/llng.sock -e FCGI -n 10 \
--plackOptions manager=FCGI::ProcManager::Constrained
=back =back
@ -236,22 +236,6 @@ other engines can be used:
It uses L<FCGI::ProcManager> as manager. Other managers: 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> =item L<FCGI::ProcManager::Dynamic>
llng-fastcgi-server -u nobody -g nobody -s /run/llng.sock -e FCGI -n 10 \ llng-fastcgi-server -u nobody -g nobody -s /run/llng.sock -e FCGI -n 10 \

View File

@ -107,6 +107,7 @@ sub store {
$req->content( to_json($conf) ); $req->content( to_json($conf) );
$req->header( 'Content-Type' => 'application/json' ); $req->header( 'Content-Type' => 'application/json' );
my $resp = $self->ua->request($req); my $resp = $self->ua->request($req);
if ( $resp->is_success ) { if ( $resp->is_success ) {
my $res; my $res;
eval { $res = from_json( $resp->content, { allow_nonref => 1 } ) }; eval { $res = from_json( $resp->content, { allow_nonref => 1 } ) };

View File

@ -41,7 +41,7 @@ sub available {
closedir D; closedir D;
@conf = @conf =
sort { $a <=> $b } sort { $a <=> $b }
map { /lmConf-(\d+)\.yaml/ ? ( $1 + 0 ) : () } @conf; map { /lmConf-(\d+)\.yaml/ ? ( $1 + 0 ) : () } @conf;
return @conf; return @conf;
} }

View File

@ -24,7 +24,7 @@ use constant MANAGERSECTION => "manager";
use constant SESSIONSEXPLORERSECTION => "sessionsExplorer"; use constant SESSIONSEXPLORERSECTION => "sessionsExplorer";
use constant APPLYSECTION => "apply"; 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 $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' ); our @sessionTypes = ( 'remoteGlobal', 'global', 'localSession', 'persistent', 'saml', 'oidc', 'cas' );

View File

@ -19,8 +19,10 @@ sub defaultValues {
'authentication' => 'Demo', 'authentication' => 'Demo',
'available2F' => 'UTOTP,TOTP,U2F,REST,Mail2F,Ext2F,Yubikey,Radius', 'available2F' => 'UTOTP,TOTP,U2F,REST,Mail2F,Ext2F,Yubikey,Radius',
'available2FSelfRegistration' => 'TOTP,U2F,Yubikey', 'available2FSelfRegistration' => 'TOTP,U2F,Yubikey',
'bruteForceProtectionLockTimes' => '5 15 60 300 600',
'bruteForceProtectionMaxAge' => 300, 'bruteForceProtectionMaxAge' => 300,
'bruteForceProtectionMaxFailed' => 3, 'bruteForceProtectionMaxFailed' => 3,
'bruteForceProtectionMaxLockTime' => 900,
'bruteForceProtectionTempo' => 30, 'bruteForceProtectionTempo' => 30,
'captcha_mail_enabled' => 1, 'captcha_mail_enabled' => 1,
'captcha_register_enabled' => 1, 'captcha_register_enabled' => 1,
@ -172,6 +174,7 @@ sub defaultValues {
'notificationServerPOST' => 1, 'notificationServerPOST' => 1,
'notificationServerSentAttributes' => 'notificationServerSentAttributes' =>
'uid reference date title subtitle text check', 'uid reference date title subtitle text check',
'notificationsMaxRetrieve' => 3,
'notificationStorage' => 'File', 'notificationStorage' => 'File',
'notificationStorageOptions' => { 'notificationStorageOptions' => {
'dirName' => '/var/lib/lemonldap-ng/notifications' 'dirName' => '/var/lib/lemonldap-ng/notifications'
@ -220,6 +223,8 @@ sub defaultValues {
'passwordPolicyMinSize' => 0, 'passwordPolicyMinSize' => 0,
'passwordPolicyMinUpper' => 0, 'passwordPolicyMinUpper' => 0,
'passwordResetAllowedRetries' => 3, 'passwordResetAllowedRetries' => 3,
'persistentSessionAttributes' =>
'_loginHistory _2fDevices notification_',
'port' => -1, 'port' => -1,
'portal' => 'http://auth.example.com/', 'portal' => 'http://auth.example.com/',
'portalAntiFrame' => 1, 'portalAntiFrame' => 1,
@ -321,6 +326,7 @@ sub defaultValues {
'slaveExportedVars' => {}, 'slaveExportedVars' => {},
'SMTPServer' => '', 'SMTPServer' => '',
'SMTPTLS' => '', 'SMTPTLS' => '',
'soapProxyUrn' => 'urn:Lemonldap/NG/Common/PSGI/SOAPService',
'SSLAuthnLevel' => 5, 'SSLAuthnLevel' => 5,
'SSLVar' => 'SSL_CLIENT_S_DN_Email', 'SSLVar' => 'SSL_CLIENT_S_DN_Email',
'SSLVarIf' => {}, 'SSLVarIf' => {},

View File

@ -198,8 +198,8 @@ sub virtualHosts {
# If rule contains a comment or an AuthLevel, split them # If rule contains a comment or an AuthLevel, split them
if ( $query eq 'locationRules' ) { if ( $query eq 'locationRules' ) {
$res->{comment} = ''; $res->{comment} = '';
$res->{level} = ''; $res->{level} = '';
$res->{level} = $1 if ( $r =~ s/\(\?#AuthnLevel=(-?\d+)\)// ); $res->{level} = $1 if ( $r =~ s/\(\?#AuthnLevel=(-?\d+)\)// );
if ( $r =~ s/\(\?#(.*?)\)// ) { if ( $r =~ s/\(\?#(.*?)\)// ) {
$res->{title} = $res->{comment} = $1; $res->{title} = $res->{comment} = $1;
} }
@ -710,9 +710,9 @@ sub combModules {
my $res = []; my $res = [];
foreach my $mod ( keys %$val ) { foreach my $mod ( keys %$val ) {
my $tmp; my $tmp;
$tmp->{title} = $mod; $tmp->{title} = $mod;
$tmp->{id} = "combModules/$mod"; $tmp->{id} = "combModules/$mod";
$tmp->{type} = 'cmbModule'; $tmp->{type} = 'cmbModule';
$tmp->{data}->{$_} = $val->{$mod}->{$_} foreach (qw(type for)); $tmp->{data}->{$_} = $val->{$mod}->{$_} foreach (qw(type for));
my $over = $val->{$mod}->{over} // {}; my $over = $val->{$mod}->{over} // {};
$tmp->{data}->{over} = [ map { [ $_, $over->{$_} ] } keys %$over ]; $tmp->{data}->{over} = [ map { [ $_, $over->{$_} ] } keys %$over ];
@ -786,8 +786,8 @@ sub metadata {
} }
# Find next and previous conf # Find next and previous conf
my @a = $self->confAcc->available; my @a = $self->confAcc->available;
my $id = -1; my $id = -1;
my ($ind) = map { $id++; $_ == $res->{cfgNum} ? ($id) : () } @a; my ($ind) = map { $id++; $_ == $res->{cfgNum} ? ($id) : () } @a;
if ($ind) { $res->{prev} = $a[ $ind - 1 ]; } if ($ind) { $res->{prev} = $a[ $ind - 1 ]; }
if ( defined $ind and $ind < $#a ) { if ( defined $ind and $ind < $#a ) {

View File

@ -166,9 +166,9 @@ sub serviceToXML {
foreach (@param_assertion) { foreach (@param_assertion) {
my @_tab = split( /;/, $self->getValue( $_, $conf ) ); my @_tab = split( /;/, $self->getValue( $_, $conf ) );
$template->param( $_ . 'Default', $_tab[0] ? 'true' : 'false' ); $template->param( $_ . 'Default', $_tab[0] ? 'true' : 'false' );
$template->param( $_ . 'Index', $_tab[1] ); $template->param( $_ . 'Index', $_tab[1] );
$template->param( $_ . 'Binding', $_tab[2] ); $template->param( $_ . 'Binding', $_tab[2] );
$template->param( $_ . 'Location', $_tab[3] ); $template->param( $_ . 'Location', $_tab[3] );
} }

View File

@ -19,6 +19,11 @@ sub import {
} }
} }
has extension => (
is => 'rw',
default => 'json'
);
has notifField => ( has notifField => (
is => 'rw', is => 'rw',
builder => sub { 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 { sub getNotifications {
my ( $self, $uid ) = @_; my ( $self, $uid ) = @_;
my $forAll = $self->get( $self->conf->{notificationWildcard} ); 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; 1;

View File

@ -92,6 +92,38 @@ sub get {
return $result; 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() ## @method hashref getAll()
# Return all pending notifications. # Return all pending notifications.
# @return hashref where keys are internal reference and values are hashref with # @return hashref where keys are internal reference and values are hashref with

View File

@ -14,11 +14,8 @@ our $VERSION = '2.1.0';
extends 'Lemonldap::NG::Common::Notifications'; extends 'Lemonldap::NG::Common::Notifications';
our $ext = 'json';
sub import { sub import {
shift; shift;
$ext = 'xml' if ( $_[0] eq 'XML' );
return Lemonldap::NG::Common::Notifications->import(@_); 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. # If $ref is set, returns only notification corresponding to this reference.
sub get { sub get {
my ( $self, $uid, $ref ) = @_; my ( $self, $uid, $ref ) = @_;
my $ext = $self->extension;
return () unless ($uid); return () unless ($uid);
my $fns = $self->{fileNameSeparator}; my $fns = $self->{fileNameSeparator};
my $identifier = &getIdentifier( $self, $uid, $ref ); my $identifier = &getIdentifier( $self, $uid, $ref );
@ -58,12 +56,37 @@ sub get {
return $files; 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() ## @method hashref getAll()
# Return all pending notifications. # Return all pending notifications.
# @return hashref where keys are internal reference and values are hashref with # @return hashref where keys are internal reference and values are hashref with
# keys date, uid, ref and condition. # keys date, uid, ref and condition.
sub getAll { sub getAll {
my $self = shift; my $self = shift;
my $ext = $self->extension;
opendir D, $self->{dirName}; opendir D, $self->{dirName};
my @notif; my @notif;
my $fns = $self->{fileNameSeparator}; my $fns = $self->{fileNameSeparator};
@ -89,6 +112,7 @@ sub getAll {
# keys date, uid, ref and condition. # keys date, uid, ref and condition.
sub getExisting { sub getExisting {
my $self = shift; my $self = shift;
my $ext = $self->extension;
opendir D, $self->{dirName}; opendir D, $self->{dirName};
my @notif; my @notif;
my $fns = $self->{fileNameSeparator}; my $fns = $self->{fileNameSeparator};
@ -113,6 +137,7 @@ sub getExisting {
# @param $myref identifier returned by get() or getAll() # @param $myref identifier returned by get() or getAll()
sub delete { sub delete {
my ( $self, $myref ) = @_; my ( $self, $myref ) = @_;
my $ext = $self->extension;
my $new = ( $myref =~ /(.*?)(?:\.$ext)$/ )[0] . '.done'; my $new = ( $myref =~ /(.*?)(?:\.$ext)$/ )[0] . '.done';
return rename( $self->{dirName} . "/$myref", $self->{dirName} . "/$new" ); return rename( $self->{dirName} . "/$myref", $self->{dirName} . "/$new" );
} }
@ -129,6 +154,7 @@ sub purge {
# Insert a new notification # Insert a new notification
sub newNotif { sub newNotif {
my ( $self, $date, $uid, $ref, $condition, $content ) = @_; my ( $self, $date, $uid, $ref, $condition, $content ) = @_;
my $ext = $self->extension;
my $fns = $self->{fileNameSeparator}; my $fns = $self->{fileNameSeparator};
$fns ||= '_'; $fns ||= '_';
my @t = split( /\D+/, $date ); my @t = split( /\D+/, $date );

View File

@ -42,10 +42,11 @@ sub newNotification {
unless ( exists $notif->{condition} ) { unless ( exists $notif->{condition} ) {
$self->userLogger->info( $self->userLogger->info(
"Set defaultCondition ($defaultCond) for notification $notif->{reference}"); "Set defaultCondition ($defaultCond) for notification $notif->{reference}"
);
$notif->{condition} = $defaultCond; $notif->{condition} = $defaultCond;
} }
push @data, ( $notif->{condition} ); push @data, ( $notif->{condition} );
$notif->{date} =~ s/^(\d{4}-\d{2}-\d{2}).*$/$1/; $notif->{date} =~ s/^(\d{4}-\d{2}-\d{2}).*$/$1/;
my $body = to_json($notif); my $body = to_json($notif);

View File

@ -9,7 +9,7 @@ package Lemonldap::NG::Common::Notifications::LDAP;
use strict; use strict;
use Mouse; use Mouse;
use Time::Local; use Time::Local;
use MIME::Base64; use MIME::Base64 qw/encode_base64url/;
use Net::LDAP; use Net::LDAP;
use utf8; 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. # Returns notifications corresponding to the user $uid.
# If $ref is set, returns only notification corresponding to this reference. # If $ref is set, returns only notification corresponding to this reference.
sub get { sub get {
@ -57,7 +67,39 @@ sub get {
. ( $ref ? '(description={ref}' . $ref . ')' : '' ) . ')'; . ( $ref ? '(description={ref}' . $ref . ')' : '' ) . ')';
my @entries = _search( $self, $filter ); 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) { foreach my $entry (@entries) {
my @notifValues = $entry->get_value('description'); my @notifValues = $entry->get_value('description');
my $f = {}; my $f = {};
@ -195,9 +237,8 @@ sub newNotif {
return ( 0, "Bad date" ) if ($@); return ( 0, "Bad date" ) if ($@);
$date =~ s/-//g; $date =~ s/-//g;
return ( 0, "Bad date" ) unless ( $date =~ /^\d{8}/ ); return ( 0, "Bad date" ) unless ( $date =~ /^\d{8}/ );
my $cn = "${date}${fns}${uid}${fns}" . encode_base64( $ref, '' ); my $cn = "${date}${fns}${uid}${fns}" . encode_base64url( $ref, '' );
$cn .= "${fns}" . encode_base64( $condition, '' ) if $condition; $cn .= "${fns}" . encode_base64url( $condition, '' ) if $condition;
$xml = $xml->serialize();
my $fields = my $fields =
$condition =~ /.+/ $condition =~ /.+/
@ -347,7 +388,7 @@ sub _store {
); );
if ( $add->code ) { if ( $add->code ) {
$self->logError($add); $self->logger->error( $add->error );
return 0; return 0;
} }

View File

@ -45,7 +45,7 @@ sub newNotification {
$self->logger->error("$err"); $self->logger->error("$err");
return 0; return 0;
} }
# Prevent to store time. Keep date only # Prevent to store time. Keep date only
$tmp =~ s/^(\d{4}-\d{2}-\d{2}).*$/$1/; $tmp =~ s/^(\d{4}-\d{2}-\d{2}).*$/$1/;
push @data, $tmp; push @data, $tmp;
@ -59,8 +59,8 @@ sub newNotification {
} }
else { else {
$self->userLogger->info( $self->userLogger->info(
"Set defaultCondition ($defaultCond) for notification " . $notif->{reference} "Set defaultCondition ($defaultCond) for notification "
); . $notif->{reference} );
push @data, $defaultCond; push @data, $defaultCond;
} }
} }

View File

@ -35,17 +35,17 @@ sub init {
foreach my $k ( keys %$args ) { foreach my $k ( keys %$args ) {
$self->{$k} = $args->{$k} unless ( $k eq 'logger' ); $self->{$k} = $args->{$k} unless ( $k eq 'logger' );
} }
unless ( $self->logger and $self->userLogger ) { unless ( ref( $self->logger ) and ref( $self->userLogger ) ) {
my $logger = my $logger =
$args->{logger} $args->{logger}
|| $ENV{LLNG_DEFAULTLOGGER} || $ENV{LLNG_DEFAULTLOGGER}
|| 'Lemonldap::NG::Common::Logger::Std'; || 'Lemonldap::NG::Common::Logger::Std';
unless ( $self->logger ) { unless ( ref $self->logger ) {
eval "require $logger"; eval "require $logger";
die $@ if ($@); die $@ if ($@);
$self->logger( $logger->new($self) ); $self->logger( $logger->new($self) );
} }
unless ( $self->userLogger ) { unless ( ref $self->userLogger ) {
$logger = $ENV{LLNG_USERLOGGER} || $args->{userLogger} || $logger; $logger = $ENV{LLNG_USERLOGGER} || $args->{userLogger} || $logger;
eval "require $logger"; eval "require $logger";
die $@ if ($@); die $@ if ($@);

View File

@ -52,7 +52,8 @@ sub userData {
return $self->{userData} return $self->{userData}
|| { || {
( $Lemonldap::NG::Handler::Main::tsv->{whatToTrace} ( $Lemonldap::NG::Handler::Main::tsv->{whatToTrace}
|| '_whatToTrace' ) => $self->{user}, }; || '_whatToTrace' ) => $self->{user},
};
} }
sub respHeaders { sub respHeaders {

View File

@ -64,8 +64,8 @@ sub checkLogonHours {
# Use time_correction # Use time_correction
if ($time_correction) { if ($time_correction) {
my ( $sign, $time ) = ( $time_correction =~ /([+|-]?)(\d+)/ ); my ( $sign, $time ) = ( $time_correction =~ /([+|-]?)(\d+)/ );
if ( $sign =~ /-/ ) { $hourpos -= $time; } if ( $sign =~ /-/ ) { $hourpos -= $time; }
else { $hourpos += $time; } else { $hourpos += $time; }
} }
# Get the corresponding byte # Get the corresponding byte

View File

@ -131,6 +131,7 @@ sub BUILD {
if ( $self->{info} ) { if ( $self->{info} ) {
foreach ( keys %{ $self->{info} } ) { foreach ( keys %{ $self->{info} } ) {
next if ( $_ eq "_session_id" and $data->{_session_id} );
if ( defined $self->{info}->{$_} ) { if ( defined $self->{info}->{$_} ) {
$data->{$_} = $self->{info}->{$_}; $data->{$_} = $self->{info}->{$_};
} }

View File

@ -283,11 +283,11 @@ sub getMod {
my ( $self, $req ) = @_; my ( $self, $req ) = @_;
my ( $s, $m ); my ( $s, $m );
unless ( $s = $req->params('sessionType') ) { unless ( $s = $req->params('sessionType') ) {
$self->error($req->error('Session type is required')); $self->error( $req->error('Session type is required') );
return (); return ();
} }
unless ( $m = $self->sessionTypes->{$s} ) { unless ( $m = $self->sessionTypes->{$s} ) {
$self->error($req->error('Unknown (or unconfigured) session type')); $self->error( $req->error('Unknown (or unconfigured) session type') );
return (); return ();
} }
if ( my $kind = $req->params('kind') ) { if ( my $kind = $req->params('kind') ) {

View File

@ -22,7 +22,7 @@ for ( my $i = 0 ; $i < @ARGV ; $i++ ) {
$action ||= "help"; $action ||= "help";
if ( $action =~ /^(?:[gs]et|(?:add|del)Key|save|restore)$/ ) { if ( $action =~ /^(?:[gs]et|(?:add|del)Key|save|restore|rollback)$/ ) {
eval { require Lemonldap::NG::Manager::Cli; }; eval { require Lemonldap::NG::Manager::Cli; };
die "Manager libraries not available, aborting ($@)" if ($@); die "Manager libraries not available, aborting ($@)" if ($@);
Lemonldap::NG::Manager::Cli->run(@ARGV); Lemonldap::NG::Manager::Cli->run(@ARGV);
@ -50,6 +50,14 @@ Available actions:
- save : export configuration to STDOUT - save : export configuration to STDOUT
- restore - : import configuration from STDIN - restore - : import configuration from STDIN
- restore <file> : import configuration from file - restore <file> : import configuration from file
- rollback : restore previous configuration
Options:
- yes <0|1> : accept confirmation prompt automatically
- log <msg> : set configuration log message
- safe <0|1> : fail in case the requested configuration is invalid
- force <0|1> : allow overwrite of existing config number
- cfgNum <num> : set new configuration number (requires -force 1)
See Lemonldap::NG::Manager::Cli(3) for more See Lemonldap::NG::Manager::Cli(3) for more
}; };
@ -75,6 +83,7 @@ Update local configuration cache
Save configuration Save configuration
$ lemonldap-ng-cli save >conf.json $ lemonldap-ng-cli save >conf.json
$ lemonldap-ng-cli -cfgNum 19 save >conf-19.json
Restore configuration Restore configuration
@ -82,6 +91,10 @@ Restore configuration
# OR # OR
$ lemonldap-ng-cli restore - <conf.json $ lemonldap-ng-cli restore - <conf.json
Cancel the last configuration change
$ lemonldap-ng-cli rollback
Get a configuration parameter value Get a configuration parameter value
$ lemonldap-ng-cli get portal domain cookieName $ lemonldap-ng-cli get portal domain cookieName
@ -92,6 +105,12 @@ Set some values
# add or set a key # add or set a key
$ lemonldap-ng-cli addKey macro fullname '$givenName." ".$lastName' $ lemonldap-ng-cli addKey macro fullname '$givenName." ".$lastName'
# without changing the version number
$ lemonldap-ng-cli -force 1 -cfgNum 1 set portal http://auth.e.com/ domain e.com
# without asking for confirmation
$ lemonldap-ng-cli -yes 1 set portal http://auth.e.com/ domain e.com
=head1 DESCRIPTION =head1 DESCRIPTION
lemonldap-ng-cli is a command line interface to interact with Lemonldap::NG lemonldap-ng-cli is a command line interface to interact with Lemonldap::NG
@ -120,6 +139,32 @@ and L<Lemonldap::NG::Common::Cli>
=back =back
=head2 Available options
=over
=item -yes
Confirm modification automatically (default: 0)
=item -log
Allows you to set the log message that will be displayed in the manager
=item -safe
The configuration change will be aborted if it contains errors (default: 0)
=item -cfgNum
Choose a particular configuration number (default: latest)
=item -force
Allows you to force overwriting an existing configuration (default: 0)
=back
=head1 SEE ALSO =head1 SEE ALSO
L<Lemonldap::NG::Manager::Cli>, L<Lemonldap::NG::Common::Cli> L<Lemonldap::NG::Manager::Cli>, L<Lemonldap::NG::Common::Cli>

View File

@ -64,4 +64,3 @@ t/lmConf-1.json
t/sessions/lock/Apache-Session-f5eec18ebb9bc96352595e2d8ce962e8ecf7af7c9a98cb9a43f9cd181cf4b545.lock t/sessions/lock/Apache-Session-f5eec18ebb9bc96352595e2d8ce962e8ecf7af7c9a98cb9a43f9cd181cf4b545.lock
t/test-psgi-lib.pm t/test-psgi-lib.pm
t/test.pm t/test.pm
t/Time-Fake.pm

View File

@ -66,7 +66,7 @@ q"I refuse to compile rules.json when useSafeJail isn't activated! Yes I know, I
$json->{rules} ||= { default => 1 }; $json->{rules} ||= { default => 1 };
$json->{headers} //= { 'Auth-User' => '$uid' }; $json->{headers} //= { 'Auth-User' => '$uid' };
$class->locationRulesInit( undef, { $vhost => $json->{rules} } ); $class->locationRulesInit( undef, { $vhost => $json->{rules} } );
$class->headersInit( undef, { $vhost => $json->{headers} } ); $class->headersInit( undef, { $vhost => $json->{headers} } );
$class->tsv->{lastVhostUpdate}->{$vhost} = time; $class->tsv->{lastVhostUpdate}->{$vhost} = time;
return; return;
} }

View File

@ -75,11 +75,13 @@ sub fetchId {
# Get access token session # Get access token session
my $infos = $class->getOIDCInfos($access_token); my $infos = $class->getOIDCInfos($access_token);
# If this token is tied to a regular session ID # If this token is tied to a regular session ID
if ( my $_session_id = $infos->{user_session_id} ) { if ( my $_session_id = $infos->{user_session_id} ) {
$class->logger->debug( 'Get user session id ' . $_session_id ); $class->logger->debug( 'Get user session id ' . $_session_id );
return $_session_id; return $_session_id;
} }
# If this token is tied to an Offline session # If this token is tied to an Offline session
if ( my $_session_id = $infos->{offline_session_id} ) { if ( my $_session_id = $infos->{offline_session_id} ) {
$class->logger->debug( 'Get offline session id ' . $_session_id ); $class->logger->debug( 'Get offline session id ' . $_session_id );

View File

@ -45,7 +45,8 @@ sub fetchId {
} }
# Is token in good interval ? # Is token in good interval ?
my $ttl = $class->localConfig->{vhostOptions}->{$vhost}->{vhostServiceTokenTTL} my $ttl =
$class->localConfig->{vhostOptions}->{$vhost}->{vhostServiceTokenTTL}
|| $class->tsv->{serviceTokenTTL}->{$vhost}; || $class->tsv->{serviceTokenTTL}->{$vhost};
$ttl = $class->tsv->{handlerServiceTokenTTL} unless ( $ttl and $ttl > 0 ); $ttl = $class->tsv->{handlerServiceTokenTTL} unless ( $ttl and $ttl > 0 );
my $now = time; my $now = time;

View File

@ -68,7 +68,8 @@ sub run {
my ( $user, $uri, $code ) = ( $1, $2, $3 ); my ( $user, $uri, $code ) = ( $1, $2, $3 );
# Portal error translation # Portal error translation
$code = portalConsts->{$code} || $code if ( $code =~ /^\-?\d+$/ ); $code = portalConsts->{$code} || $code
if ( $code =~ /^\-?\d+$/ );
# Per user activity # Per user activity
$status->{user}->{$user}->{$code}++; $status->{user}->{$user}->{$code}++;

View File

@ -440,14 +440,15 @@ sub fetchId {
my ( $class, $req ) = @_; my ( $class, $req ) = @_;
my $t = $req->{env}->{HTTP_COOKIE} or return 0; my $t = $req->{env}->{HTTP_COOKIE} or return 0;
my $vhost = $class->resolveAlias($req); my $vhost = $class->resolveAlias($req);
$class->logger->debug("VH $vhost is HTTPS") if $class->_isHttps( $req, $vhost ); $class->logger->debug("VH $vhost is HTTPS")
if $class->_isHttps( $req, $vhost );
my $lookForHttpCookie = ( $class->tsv->{securedCookie} =~ /^(2|3)$/ my $lookForHttpCookie = ( $class->tsv->{securedCookie} =~ /^(2|3)$/
and not $class->_isHttps( $req, $vhost ) ); and not $class->_isHttps( $req, $vhost ) );
my $cn = $class->tsv->{cookieName}; my $cn = $class->tsv->{cookieName};
my $value = my $value =
$lookForHttpCookie $lookForHttpCookie
? ( $t =~ /${cn}http=([^,; ]+)/o ? $1 : 0 ) ? ( $t =~ /${cn}http=([^,; ]+)/o ? $1 : 0 )
: ( $t =~ /$cn=([^,; ]+)/o ? $1 : 0 ); : ( $t =~ /$cn=([^,; ]+)/o ? $1 : 0 );
if ( $value && $lookForHttpCookie && $class->tsv->{securedCookie} == 3 ) { if ( $value && $lookForHttpCookie && $class->tsv->{securedCookie} == 3 ) {
$value = $class->tsv->{cipher}->decryptHex( $value, "http" ); $value = $class->tsv->{cipher}->decryptHex( $value, "http" );

View File

@ -49,7 +49,7 @@ sub addAuthRouteWithRedirect {
sub _auth_and_redirect { sub _auth_and_redirect {
my ( $self, $req ) = @_; my ( $self, $req ) = @_;
$self->api->goToPortal( $req, $req->{env}->{REQUEST_URI} ); $self->api->goToPortal( $req, $req->{env}->{REQUEST_URI} );
return [ 302, [$req->spliceHdrs], [] ]; return [ 302, [ $req->spliceHdrs ], [] ];
} }
sub defaultAuthRoute { sub defaultAuthRoute {
@ -73,7 +73,7 @@ sub _run {
if ( $res->[0] < 300 ) { if ( $res->[0] < 300 ) {
$self->routes( $self->authRoutes ); $self->routes( $self->authRoutes );
$req->userData( $self->api->data ); $req->userData( $self->api->data );
$req->respHeaders($res->[1]); $req->respHeaders( $res->[1] );
} }
elsif ( $res->[0] != 403 and not $req->data->{noTry} ) { elsif ( $res->[0] != 403 and not $req->data->{noTry} ) {

View File

@ -36,17 +36,17 @@ ok(
( defined($code) and ref($code) eq 'CODE' ), ( defined($code) and ref($code) eq 'CODE' ),
'encode_base64 function is defined' 'encode_base64 function is defined'
); );
ok( $res = &$code, "Function works" ); ok( $res = &$code, "Function works" );
ok( $res eq 'dGVzdA==', 'Get good result' ); ok( $res eq 'dGVzdA==', 'Get good result' );
$sub = "sub { return ( listMatch('ABC; DEF; GHI','abc',1) ) }"; $sub = "sub { return ( listMatch('ABC; DEF; GHI','abc',1) ) }";
my $code = $jail->jail_reval($sub); $code = $jail->jail_reval($sub);
ok( ( defined($code) and ref($code) eq 'CODE' ), ok( ( defined($code) and ref($code) eq 'CODE' ),
'listMatch function is defined' ); 'listMatch function is defined' );
ok( &$code eq '1', 'Get good result' ); ok( &$code eq '1', 'Get good result' );
$sub = "sub { return ( listMatch('ABC; DEF; GHI','ab',1) ) }"; $sub = "sub { return ( listMatch('ABC; DEF; GHI','ab',1) ) }";
my $code = $jail->jail_reval($sub); $code = $jail->jail_reval($sub);
ok( ( defined($code) and ref($code) eq 'CODE' ), ok( ( defined($code) and ref($code) eq 'CODE' ),
'listMatch function is defined' ); 'listMatch function is defined' );
ok( &$code eq '0', 'Get good result' ); ok( &$code eq '0', 'Get good result' );
@ -58,5 +58,5 @@ ok(
'checkDate extended function is defined' 'checkDate extended function is defined'
); );
ok( $res = &$code, "Function works" ); ok( $res = &$code, "Function works" );
ok( $res == 1, 'Get good result' ); ok( $res == 1, 'Get good result' );

View File

@ -49,8 +49,8 @@ ok( ( defined($listMatch) and ref($listMatch) eq 'CODE' ),
'listMatch function is defined' ); 'listMatch function is defined' );
ok( &$listMatch eq '1', 'Get good result' ); ok( &$listMatch eq '1', 'Get good result' );
my $sub5 = "sub { return ( listMatch('ABC; DEF; GHI','ab', 1) ) }"; my $sub5 = "sub { return ( listMatch('ABC; DEF; GHI','ab', 1) ) }";
my $listMatch = $jail->jail_reval($sub5); $listMatch = $jail->jail_reval($sub5);
ok( ( defined($listMatch) and ref($listMatch) eq 'CODE' ), ok( ( defined($listMatch) and ref($listMatch) eq 'CODE' ),
'listMatch function is defined' ); 'listMatch function is defined' );
ok( &$listMatch eq '0', 'Get good result' ); ok( &$listMatch eq '0', 'Get good result' );

View File

@ -41,33 +41,55 @@ ok( $h{'Auth-User'} eq 'dwho', 'Header Auth-User is set to "dwho"' )
count(1); count(1);
# Request an URI protected by custom function -> allowed # Request an URI protected by custom function -> allowed
ok( $res = $client->_get( '/test-restricted_uri/dwho/', undef, undef, "lemonldap=$sessionId" ), ok(
'Authentified query' ); $res = $client->_get(
ok( $res->[0] == 200, '/test-restricted_uri -> Code is 200' ) or explain( $res, 200 ); '/test-restricted_uri/dwho/', undef, undef, "lemonldap=$sessionId"
),
'Authentified query'
);
ok( $res->[0] == 200, '/test-restricted_uri -> Code is 200' )
or explain( $res, 200 );
count(2); count(2);
# Request an URI protected by custom function -> denied # Request an URI protected by custom function -> denied
ok( $res = $client->_get( '/test-restricted_uri/dwho', undef, undef, "lemonldap=$sessionId" ), ok(
'Denied query' ); $res = $client->_get(
ok( $res->[0] == 403, '/test-restricted_uri -> Code is 403' ) or explain( $res->[0], 403 ); '/test-restricted_uri/dwho', undef, undef, "lemonldap=$sessionId"
),
'Denied query'
);
ok( $res->[0] == 403, '/test-restricted_uri -> Code is 403' )
or explain( $res->[0], 403 );
count(2); count(2);
# Request an URI protected by custom function -> allowed # Request an URI protected by custom function -> allowed
ok( $res = $client->_get( '/test-uri2/dwho/dummy', undef, undef, "lemonldap=$sessionId" ), ok(
'Authentified query' ); $res = $client->_get(
'/test-uri2/dwho/dummy', undef, undef, "lemonldap=$sessionId"
),
'Authentified query'
);
ok( $res->[0] == 200, '/test-uri2 -> Code is 200' ) or explain( $res, 200 ); ok( $res->[0] == 200, '/test-uri2 -> Code is 200' ) or explain( $res, 200 );
count(2); count(2);
# Request an URI protected by custom function -> denied # Request an URI protected by custom function -> denied
ok( $res = $client->_get( '/test-uri1/dwho/', undef, undef, "lemonldap=$sessionId" ), ok(
'Denied query' ); $res =
ok( $res->[0] == 403, '/test-uri1 -> Code is 403' ) or explain( $res->[0], 403 ); $client->_get( '/test-uri1/dwho/', undef, undef, "lemonldap=$sessionId" ),
'Denied query'
);
ok( $res->[0] == 403, '/test-uri1 -> Code is 403' )
or explain( $res->[0], 403 );
count(2); count(2);
# Request an URI protected by custom function -> denied # Request an URI protected by custom function -> denied
ok( $res = $client->_get( '/test-uri1/dwh', undef, undef, "lemonldap=$sessionId" ), ok(
'Denied query' ); $res =
ok( $res->[0] == 403, '/test-uri1 -> Code is 403' ) or explain( $res->[0], 403 ); $client->_get( '/test-uri1/dwh', undef, undef, "lemonldap=$sessionId" ),
'Denied query'
);
ok( $res->[0] == 403, '/test-uri1 -> Code is 403' )
or explain( $res->[0], 403 );
count(2); count(2);
# Denied query # Denied query

View File

@ -45,27 +45,42 @@ ok( $h{'Headervalue1'} eq 'dwho', 'Headervalue1 is set to "dwho"' )
count(2); count(2);
# Request an URI protected by custom function -> allowed # Request an URI protected by custom function -> allowed
ok( $res = $client->_get( '/test-uri1/dwho', undef, undef, "lemonldap=$sessionId" ), ok(
'Authentified query' ); $res =
$client->_get( '/test-uri1/dwho', undef, undef, "lemonldap=$sessionId" ),
'Authentified query'
);
ok( $res->[0] == 200, '/test-uri1 -> Code is 200' ) or explain( $res, 200 ); ok( $res->[0] == 200, '/test-uri1 -> Code is 200' ) or explain( $res, 200 );
count(2); count(2);
# Request an URI protected by custom function -> allowed # Request an URI protected by custom function -> allowed
ok( $res = $client->_get( '/test-uri2/dwho/dummy', undef, undef, "lemonldap=$sessionId" ), ok(
'Authentified query' ); $res = $client->_get(
'/test-uri2/dwho/dummy', undef, undef, "lemonldap=$sessionId"
),
'Authentified query'
);
ok( $res->[0] == 200, '/test-uri2 -> Code is 200' ) or explain( $res, 200 ); ok( $res->[0] == 200, '/test-uri2 -> Code is 200' ) or explain( $res, 200 );
count(2); count(2);
# Request an URI protected by custom function -> denied # Request an URI protected by custom function -> denied
ok( $res = $client->_get( '/test-uri1/dwho/', undef, undef, "lemonldap=$sessionId" ), ok(
'Denied query' ); $res =
ok( $res->[0] == 403, '/test-uri1 -> Code is 403' ) or explain( $res->[0], 403 ); $client->_get( '/test-uri1/dwho/', undef, undef, "lemonldap=$sessionId" ),
'Denied query'
);
ok( $res->[0] == 403, '/test-uri1 -> Code is 403' )
or explain( $res->[0], 403 );
count(2); count(2);
# Request an URI protected by custom function -> denied # Request an URI protected by custom function -> denied
ok( $res = $client->_get( '/test-uri1/dwh', undef, undef, "lemonldap=$sessionId" ), ok(
'Denied query' ); $res =
ok( $res->[0] == 403, '/test-uri1 -> Code is 403' ) or explain( $res->[0], 403 ); $client->_get( '/test-uri1/dwh', undef, undef, "lemonldap=$sessionId" ),
'Denied query'
);
ok( $res->[0] == 403, '/test-uri1 -> Code is 403' )
or explain( $res->[0], 403 );
count(2); count(2);
# Denied query # Denied query

View File

@ -39,7 +39,7 @@ my $res;
# Unauth tests # Unauth tests
ok( $res = $client->_get('/test'), 'Get response' ); ok( $res = $client->_get('/test'), 'Get response' );
ok( $res->[0] == 200, 'Response code is 200' ) ok( $res->[0] == 200, 'Response code is 200' )
or print "Expect 200, got $res->[0]\n"; or print "Expect 200, got $res->[0]\n";
ok( $res->[2]->[0] eq 'Unauth', 'Get unauth result' ) ok( $res->[2]->[0] eq 'Unauth', 'Get unauth result' )
or print "Expect Unauth, got $res->[2]->[0]\n"; or print "Expect Unauth, got $res->[2]->[0]\n";
@ -64,7 +64,7 @@ count(3);
# Bad path test # Bad path test
ok( $res = $client->_get('/[]/test'), 'Try a bad path' ); ok( $res = $client->_get('/[]/test'), 'Try a bad path' );
ok( $res->[0] == 400, 'Response is 400' ); ok( $res->[0] == 400, 'Response is 400' );
count(2); count(2);
clean(); clean();

View File

@ -119,7 +119,7 @@ ok(
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res->[0], 200 ); ok( $res->[0] == 200, 'Code is 200' ) or explain( $res->[0], 200 );
count(2); count(2);
my %headers = @{ $res->[1] }; %headers = @{ $res->[1] };
ok( $headers{'zero'} eq '0', 'Found "zero" header with "0"' ) ok( $headers{'zero'} eq '0', 'Found "zero" header with "0"' )
or print STDERR Data::Dumper::Dumper( $res->[1] ); or print STDERR Data::Dumper::Dumper( $res->[1] );
ok( $headers{'empty'} eq '', 'Found "empty" header without value' ) ok( $headers{'empty'} eq '', 'Found "empty" header without value' )

View File

@ -38,8 +38,9 @@ my $crypt = Lemonldap::NG::Common::Crypto->new('qwertyui');
my $token = $crypt->encrypt( my $token = $crypt->encrypt(
join ':', time, join ':', time,
$sessionId, 'test1.example.com', $sessionId, 'test1.example.com',
'XFromVH=app1-auth.example.com', "serviceHeader1=$sessionId","serviceHeader2=$sessionId", 'XFromVH=app1-auth.example.com', "serviceHeader1=$sessionId",
'test2.example.com', '*.example.com' "serviceHeader2=$sessionId", 'test2.example.com',
'*.example.com'
); );
ok( ok(

View File

@ -1,235 +0,0 @@
package Time::Fake;
use Carp;
use strict;
use vars '$VERSION';
$VERSION = "0.11";
#####################
my $OFFSET = 0;
*CORE::GLOBAL::time = sub() { CORE::time() + $OFFSET };
*CORE::GLOBAL::localtime = sub(;$) {
@_ ? CORE::localtime($_[0])
: CORE::localtime(CORE::time() + $OFFSET);
};
*CORE::GLOBAL::gmtime = sub(;$) {
@_ ? CORE::gmtime($_[0])
: CORE::gmtime(CORE::time() + $OFFSET);
};
sub import {
my $pkg = shift;
$pkg->offset(shift);
}
sub offset {
my $pkg = shift;
return $OFFSET if !@_;
my $old_offset = $OFFSET;
$OFFSET = _to_offset(shift);
return $old_offset;
}
sub reset {
shift->offset(0);
}
my %mult = (
s => 1,
m => 60,
h => 60*60,
d => 60*60*24,
M => 60*60*24*30,
y => 60*60*24*365,
);
sub _to_offset {
my $t = shift || return 0;
if ($t =~ m/^([+-]\d+)([smhdMy]?)$/) {
$t = $1 * $mult{ $2 || "s" };
} elsif ($t !~ m/\D/) {
$t = $t - CORE::time;
} else {
croak "Invalid time offset: `$t'";
}
return $t;
}
1;
__END__
=head1 NAME
Time::Fake - Simulate different times without changing your system clock
=head1 SYNOPSIS
Pretend we are running 1 day in the future:
use Time::Fake '+1d';
Pretend we are running 1 year in the past:
use Time::Fake '-1y';
Pretend the script started at epoch time 1234567:
use Time::Fake 1234567;
See what an existing script would do if run 20 years in the future:
% perl -MTime::Fake="+20y" test.pl
Run a section of code in a time warp:
use Time::Fake;
# do some setup
Time::Fake->offset("+1y");
run_tests(); # thinks it's a year ahead
Time::Fake->reset; # back to the present
=head1 DESCRIPTION
Use this module to achieve the effect of changing your system clock, but
without actually changing your system clock. It overrides the Perl builtin
subs C<time>, C<localtime>, and C<gmtime>, causing them to return a
"faked" time of your choice. From the script's point of view, time still
flows at the normal rate, but it is just offset as if it were executing
in the past or present.
You may find this module useful in writing test scripts for code that has
time-sensitive logic.
=head1 USAGE
=head2 Using and importing:
use Time::Fake $t;
Is equivalent to:
use Time::Fake;
Time::Fake->offset($t);
See below for arguments to C<offset>. This usage makes it easy to
fake the time for existing scripts, as in:
% perl -MTime::Fake=+1y script.pl
=head2 offset
Time::Fake->offset( [$t] );
C<$t> is either an epoch time, or a relative offset of the following
form:
+3 # 3 seconds in the future
-3s # 3 seconds in the past
+1h # 1 hour in the future
etc..
Relative offsets must begin with a plus or minus symbol. The supported
units are:
s second
m minute
h hour
d day (24 hours)
M month (30 days)
y year (365 days)
If C<$t> is an epoch time, then C<time>, C<localtime>, and C<gmtime>
will act as though the the current time (when C<offset> was called) was
actually at C<$t> epoch seconds.
Otherwise, the offset C<$t> will be added to the times returned by these
builtin subs.
When C<$t> is false, C<time>, C<localtime>, C<gmtime>
remain overridden, but their behavior resets to reflect the actual
system time.
When C<$t> is omitted, nothing is changed, but C<offset> returns the
current additive offset (in seconds). Otherwise, its return value is
the I<previous> offset.
C<offset> may be called several times. However, I<The effect of multiple
calls is NOT CUMULATIVE.> That is:
Time::Fake->offset("+1h");
Time::Fake->offset("+1h");
## same as
# Time::Fake->offset("+1h");
## NOT the same as
# Time::Fake->offset("+2h");
Each call to C<offset> completely cancels out the effect of any
previous calls. To make the effect cumulative, use the return value
of calling C<offset> with no arguments:
Time::Fake->offset("+1h");
...
Time::Fake->offset( Time::Fake->offset + 3600 ); # add another hour
=head2 reset
Time::Fake->reset;
Is the same as:
Time::Fake->offset(0);
That is, it returns all the affected builtin subs to their
default behavior -- reporing the actual system time.
=head1 KNOWN CAVEATS
Time::Fake must be loaded at C<BEGIN>-time (e.g., with a standard
C<use> statement). It must be loaded before perl I<compiles> any code
that uses C<time>, C<localtime>, or C<gmtime>. Due to inherent
limitations in overriding builtin subs, any code that was compiled
before loading Time::Fake will not be affected.
Because the system clock is not being changed, only Perl code that
uses C<time>, C<localtime>, or C<gmtime> will be fooled about the date.
In particular, the operating system is not fooled,
nor are other programs. If your Perl code modifies a file for example,
the file's modification time will reflect the B<actual> (not faked) time.
Along the same lines, if your Perl script obtains the time from somewhere
other than the affected builtins subs (e.g., C<qx/date/>), the actual
(not faked) time will be reflected.
Time::Fake doesn't affect -M, -A, -C filetest operators in the way you'd
probably want. These still report the B<actual> (not faked) script start
time minus file access time.
Time::Fake has not been tested with other modules that override the time
builtins, e.g., Time::HiRes.
=head1 SEE ALSO
Time::Warp, which uses XS to fool more of Perl.
=head1 AUTHOR
Time::Fake is written by Mike Rosulek E<lt>mike@mikero.comE<gt>. Feel
free to contact me with comments, questions, patches, or whatever.
=head1 COPYRIGHT
Copyright (c) 2008 Mike Rosulek. All rights reserved. This module is free
software; you can redistribute it and/or modify it under the same terms as Perl
itself.

View File

@ -4,15 +4,12 @@ use strict;
use 5.10.0; use 5.10.0;
use POSIX 'strftime'; use POSIX 'strftime';
use Data::Dumper; use Data::Dumper;
use Time::Fake;
use_ok('Lemonldap::NG::Common::PSGI::Cli::Lib'); use_ok('Lemonldap::NG::Common::PSGI::Cli::Lib');
our $client; our $client;
our $count = 1; our $count = 1;
BEGIN {
require 't/Time-Fake.pm';
}
no warnings 'redefine'; no warnings 'redefine';
my $module; my $module;

View File

@ -26,6 +26,7 @@ lib/Lemonldap/NG/Manager/Conf/Parser.pm
lib/Lemonldap/NG/Manager/Conf/Tests.pm lib/Lemonldap/NG/Manager/Conf/Tests.pm
lib/Lemonldap/NG/Manager/Conf/Zero.pm lib/Lemonldap/NG/Manager/Conf/Zero.pm
lib/Lemonldap/NG/Manager/Notifications.pm lib/Lemonldap/NG/Manager/Notifications.pm
lib/Lemonldap/NG/Manager/Plugin.pm
lib/Lemonldap/NG/Manager/Sessions.pm lib/Lemonldap/NG/Manager/Sessions.pm
lib/Lemonldap/NG/Manager/Viewer.pm lib/Lemonldap/NG/Manager/Viewer.pm
Makefile.PL Makefile.PL

View File

@ -24,6 +24,9 @@ extends 'Lemonldap::NG::Common::Conf::AccessLib',
has csp => ( is => 'rw' ); has csp => ( is => 'rw' );
has loadedPlugins => ( is => 'rw', default => sub { [] } );
has hLoadedPlugins => ( is => 'rw', default => sub { {} } );
## @method boolean init($args) ## @method boolean init($args)
# Launch initialization method # Launch initialization method
# #
@ -52,32 +55,50 @@ sub init {
return 0; return 0;
} }
my $conf = $self->confAcc->getConf;
$conf->{$_} = $args->{$_} foreach ( keys %$args );
$self->{enabledModules} ||= "conf, sessions, notifications, 2ndFA, api"; $self->{enabledModules} ||= "conf, sessions, notifications, 2ndFA, api";
my @links; my @links;
my @enabledModules = my @enabledModules =
map { push @links, $_; "Lemonldap::NG::Manager::" . ucfirst($_) } map {
my @res = ( "Lemonldap::NG::Manager::" . ucfirst($_) );
if ( my $tmp = $self->loadPlugin( @res, $conf ) ) {
$self->logger->debug("Plugin $_ loaded");
push @links, $_;
push @{ $self->loadedPlugins }, $tmp;
$self->hLoadedPlugins->{$_} = $tmp;
}
else {
$self->logger->error("Unable to load $_, skipping");
@res = ();
}
(@res);
}
split( /[,\s]+/, $self->{enabledModules} ); split( /[,\s]+/, $self->{enabledModules} );
extends 'Lemonldap::NG::Handler::PSGI::Router', @enabledModules; unless (@enabledModules) {
my @working; $self->logger->error('No plugins loaded, aborting');
my $conf = $self->confAcc->getConf; return 0;
}
unless ($conf) { unless ($conf) {
require Lemonldap::NG::Manager::Conf::Zero; require Lemonldap::NG::Manager::Conf::Zero;
$conf = Lemonldap::NG::Manager::Conf::Zero::zeroConf(); $conf = Lemonldap::NG::Manager::Conf::Zero::zeroConf();
} }
for ( my $i = 0 ; $i < @enabledModules ; $i++ ) {
my $mod = $enabledModules[$i]; # TODO: -> loadPlugin
no strict 'refs'; #for ( my $i = 0 ; $i < @enabledModules ; $i++ ) {
if ( &{"${mod}::addRoutes"}( $self, $conf ) ) { # my $mod = $enabledModules[$i];
$self->logger->debug("Module $mod enabled"); # no strict 'refs';
push @working, $mod; # if ( &{"${mod}::addRoutes"}( $self, $conf ) ) {
} # $self->logger->debug("Module $mod enabled");
else { # push @working, $mod;
$links[$i] = undef; # }
$self->logger->error( # else {
"Module $mod can not be enabled: " . $self->error ); # $links[$i] = undef;
} # $self->logger->error(
} # "Module $mod can not be enabled: " . $self->error );
return 0 unless (@working); # }
#}
$self->addRoute( links => 'links', ['GET'] ); $self->addRoute( links => 'links', ['GET'] );
$self->addRoute( 'psgi.js' => 'sendJs', ['GET'] ); $self->addRoute( 'psgi.js' => 'sendJs', ['GET'] );
@ -88,13 +109,14 @@ sub init {
); );
# Avoid restricted users to access configuration by default route # Avoid restricted users to access configuration by default route
my $defaultMod = $self->{defaultModule} || 'conf'; my $defaultMod = $self->{defaultModule} =
$self->{defaultModule} || $enabledModules[0];
$self->logger->debug("Default module -> $defaultMod"); $self->logger->debug("Default module -> $defaultMod");
my ($index) = my ($index) =
grep { $working[$_] =~ /::$defaultMod$/i } ( 0 .. $#working ); grep { $enabledModules[$_] eq $defaultMod } ( 0 .. $#enabledModules );
$index //= $#working; $index //= 0;
$self->logger->debug("Default index -> $index"); $self->logger->debug("Default index -> $index");
$self->defaultRoute( $working[$index]->defaultRoute ); $self->defaultRoute( $self->loadedPlugins->[$index]->defaultRoute );
# Find out more glyphicones at https://www.w3schools.com/icons/bootstrap_icons_glyphicons.asp # Find out more glyphicones at https://www.w3schools.com/icons/bootstrap_icons_glyphicons.asp
my $linksIcons = { my $linksIcons = {
@ -110,7 +132,7 @@ sub init {
next unless ( defined $links[$i] ); next unless ( defined $links[$i] );
push @{ $self->links }, push @{ $self->links },
{ {
target => $enabledModules[$i]->defaultRoute, target => $self->loadedPlugins->[$i]->defaultRoute,
title => $links[$i], title => $links[$i],
icon => $linksIcons->{ $links[$i] } icon => $linksIcons->{ $links[$i] }
}; };
@ -141,15 +163,20 @@ sub init {
sub tplParams { sub tplParams {
my ( $self, $req ) = @_; my ( $self, $req ) = @_;
my $res = $self->brwRule->( $req, $req->{userData} ) || 0; my $res = eval {
$self->hLoadedPlugins->{viewer}->brwRule->( $req, $req->{userData} );
} || 0;
return ( VERSION => $VERSION, ALLOWBROWSER => $res ); return ( VERSION => $VERSION, ALLOWBROWSER => $res );
} }
sub javascript { sub javascript {
my ( $self, $req ) = @_; my ( $self, $req ) = @_;
my $res = $self->diffRule->( $req, $req->{userData} ) || 0; my $res = eval {
my $impPrefix = $self->{impersonationPrefix}; $self->hLoadedPlugins->{viewer}->diffRule->( $req, $req->{userData} );
my $ttl = $self->{timeout} || 72000; } || 0;
print STDERR $@ if $@;
my $impPrefix = $self->{impersonationPrefix} || 'real_';
my $ttl = $self->{timeout} || 72000;
return return
'var formPrefix=staticPrefix+"forms/";var confPrefix=scriptname+"confs/";var viewPrefix=scriptname+"view/";' 'var formPrefix=staticPrefix+"forms/";var confPrefix=scriptname+"confs/";var viewPrefix=scriptname+"view/";'
@ -176,6 +203,32 @@ sub sendHtml {
return $res; return $res;
} }
sub loadPlugin {
my ( $self, $plugin, $conf ) = @_;
unless ($plugin) {
require Carp;
Carp::confess('Calling loadPugin without arg !');
}
my $obj;
$plugin = "Lemonldap::NG::Manager$plugin" if ( $plugin =~ /^::/ );
eval "require $plugin";
if ($@) {
$self->logger->error("$plugin load error: $@");
return 0;
}
eval {
$obj = $plugin->new( { p => $self, conf => $conf } );
$self->logger->debug("Module $plugin loaded");
};
if ($@) {
$self->error("Unable to build $plugin object: $@");
return 0;
}
( $obj and $obj->init($conf) ) or return 0;
return $obj;
}
1; 1;
__END__ __END__

View File

@ -13,7 +13,8 @@ use Lemonldap::NG::Common::Conf::ReConstants;
use feature 'state'; use feature 'state';
extends 'Lemonldap::NG::Common::Conf::AccessLib', extends 'Lemonldap::NG::Manager::Plugin',
'Lemonldap::NG::Common::Conf::AccessLib',
'Lemonldap::NG::Common::Session::REST'; 'Lemonldap::NG::Common::Session::REST';
our $VERSION = '2.1.0'; our $VERSION = '2.1.0';
@ -24,7 +25,7 @@ our $VERSION = '2.1.0';
use constant defaultRoute => '2ndfa.html'; use constant defaultRoute => '2ndfa.html';
sub addRoutes { sub init {
my ( $self, $conf ) = @_; my ( $self, $conf ) = @_;
# Remote Procedure are defined in Lemonldap::NG::Common::Session::REST # Remote Procedure are defined in Lemonldap::NG::Common::Session::REST
@ -46,6 +47,7 @@ sub addRoutes {
$self->{multiValuesSeparator} ||= '; '; $self->{multiValuesSeparator} ||= '; ';
$self->{hiddenAttributes} //= "_password"; $self->{hiddenAttributes} //= "_password";
$self->{TOTPCheck} = $self->{U2FCheck} = $self->{UBKCheck} = '1'; $self->{TOTPCheck} = $self->{U2FCheck} = $self->{UBKCheck} = '1';
return 1;
} }
################### ###################

View File

@ -5,14 +5,15 @@ use 5.10.0;
use utf8; use utf8;
use Mouse; use Mouse;
extends 'Lemonldap::NG::Common::Conf::RESTServer', extends 'Lemonldap::NG::Manager::Plugin',
'Lemonldap::NG::Common::Conf::RESTServer',
'Lemonldap::NG::Common::Session::REST'; 'Lemonldap::NG::Common::Session::REST';
use Lemonldap::NG::Manager::Api::2F; use Lemonldap::NG::Manager::Api::2F;
use Lemonldap::NG::Manager::Api::Providers::OidcRp; use Lemonldap::NG::Manager::Api::Providers::OidcRp;
use Lemonldap::NG::Manager::Api::Providers::SamlSp; use Lemonldap::NG::Manager::Api::Providers::SamlSp;
our $VERSION = '2.0.7'; our $VERSION = '2.0.8';
############################# #############################
# I. INITIALIZATION METHODS # # I. INITIALIZATION METHODS #
@ -20,7 +21,7 @@ our $VERSION = '2.0.7';
use constant defaultRoute => 'api.html'; use constant defaultRoute => 'api.html';
sub addRoutes { sub init {
my ( $self, $conf ) = @_; my ( $self, $conf ) = @_;
# HTML template # HTML template
@ -148,6 +149,7 @@ sub addRoutes {
$self->{multiValuesSeparator} ||= '; '; $self->{multiValuesSeparator} ||= '; ';
$self->{hiddenAttributes} //= "_password"; $self->{hiddenAttributes} //= "_password";
$self->{TOTPCheck} = $self->{U2FCheck} = $self->{UBKCheck} = '1'; $self->{TOTPCheck} = $self->{U2FCheck} = $self->{UBKCheck} = '1';
return 1;
} }
1; 1;

View File

@ -60,8 +60,8 @@ sub _hasAllowedAttributes {
sub _listAttributes { sub _listAttributes {
my ( $self, $rootNode ) = @_; my ( $self, $rootNode ) = @_;
my $mainTree = Lemonldap::NG::Manager::Build::CTrees::cTrees(); my $mainTree = Lemonldap::NG::Manager::Build::CTrees::cTrees();
my $rootNodes = [ grep { ref($_) eq "HASH" } @{ $mainTree->{$rootNode} } ]; my $rootNodes = [ grep { ref($_) eq "HASH" } @{ $mainTree->{$rootNode} } ];
my @attributes = map { $self->_listNodeAttributes($_) } @$rootNodes; my @attributes = map { $self->_listNodeAttributes($_) } @$rootNodes;
return @attributes; return @attributes;

View File

@ -621,6 +621,14 @@ sub attributes {
'default' => 0, 'default' => 0,
'type' => 'bool' 'type' => 'bool'
}, },
'bruteForceProtectionIncrementalTempo' => {
'default' => 0,
'type' => 'bool'
},
'bruteForceProtectionLockTimes' => {
'default' => '5 15 60 300 600',
'type' => 'text'
},
'bruteForceProtectionMaxAge' => { 'bruteForceProtectionMaxAge' => {
'default' => 300, 'default' => 300,
'type' => 'int' 'type' => 'int'
@ -629,6 +637,10 @@ sub attributes {
'default' => 3, 'default' => 3,
'type' => 'int' 'type' => 'int'
}, },
'bruteForceProtectionMaxLockTime' => {
'default' => 900,
'type' => 'int'
},
'bruteForceProtectionTempo' => { 'bruteForceProtectionTempo' => {
'default' => 30, 'default' => 30,
'type' => 'int' 'type' => 'int'
@ -1326,6 +1338,10 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
}, },
'type' => 'keyTextContainer' 'type' => 'keyTextContainer'
}, },
'groupsBeforeMacros' => {
'default' => 0,
'type' => 'bool'
},
'handlerInternalCache' => { 'handlerInternalCache' => {
'default' => 15, 'default' => 15,
'type' => 'int' 'type' => 'int'
@ -1883,6 +1899,14 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
'default' => 'uid reference date title subtitle text check', 'default' => 'uid reference date title subtitle text check',
'type' => 'text' 'type' => 'text'
}, },
'notificationsExplorer' => {
'default' => 0,
'type' => 'bool'
},
'notificationsMaxRetrieve' => {
'default' => 3,
'type' => 'int'
},
'notificationStorage' => { 'notificationStorage' => {
'default' => 'File', 'default' => 'File',
'type' => 'PerlModule' 'type' => 'PerlModule'
@ -2422,6 +2446,10 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
qr/^(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9]|[a-zA-Z])[.]?))?$/, qr/^(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9]|[a-zA-Z])[.]?))?$/,
'type' => 'text' 'type' => 'text'
}, },
'persistentSessionAttributes' => {
'default' => '_loginHistory _2fDevices notification_',
'type' => 'text'
},
'persistentStorage' => { 'persistentStorage' => {
'type' => 'PerlModule' 'type' => 'PerlModule'
}, },
@ -3631,6 +3659,10 @@ qr/^(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-
'default' => 0, 'default' => 0,
'type' => 'bool' 'type' => 'bool'
}, },
'soapProxyUrn' => {
'default' => 'urn:Lemonldap/NG/Common/PSGI/SOAPService',
'type' => 'text'
},
'soapSessionServer' => { 'soapSessionServer' => {
'default' => 0, 'default' => 0,
'type' => 'bool' 'type' => 'bool'
@ -3652,7 +3684,10 @@ qr/^(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-
}, },
'SSLVarIf' => { 'SSLVarIf' => {
'default' => {}, 'default' => {},
'type' => 'keyTextContainer' 'keyTest' => sub {
1;
},
'type' => 'keyTextContainer'
}, },
'staticPrefix' => { 'staticPrefix' => {
'type' => 'text' 'type' => 'text'

View File

@ -456,15 +456,16 @@ our \$VERSION = '$Lemonldap::NG::Manager::Build::Attributes::VERSION';
use constant HANDLER => 'Lemonldap::NG::Handler::PSGI::Main'; use constant HANDLER => 'Lemonldap::NG::Handler::PSGI::Main';
use constant { use constant {
EOF EOF
for my $pe ( sort { $portalConstants{$a} <=> $portalConstants{$b} } for my $pe (
keys %portalConstants ) sort { $portalConstants{$a} <=> $portalConstants{$b} }
keys %portalConstants
)
{ {
my $str = $portalConstants{$pe}; my $str = $portalConstants{$pe};
$content .= " $pe => $str,\n"; $content .= " $pe => $str,\n";
} }
my $exports = join ", ", my $exports = join ", ", map { "'$_'" }
map { "'$_'" }
sort { $portalConstants{$a} <=> $portalConstants{$b} } sort { $portalConstants{$a} <=> $portalConstants{$b} }
keys %portalConstants; keys %portalConstants;
@ -569,7 +570,7 @@ sub scanTree {
# Subnode # Subnode
elsif ( ref($leaf) ) { elsif ( ref($leaf) ) {
$jleaf->{title} = $jleaf->{id} = $leaf->{title}; $jleaf->{title} = $jleaf->{id} = $leaf->{title};
$jleaf->{type} = $leaf->{form} if ( $leaf->{form} ); $jleaf->{type} = $leaf->{form} if ( $leaf->{form} );
if ( $leaf->{title} =~ /^((?:oidc|saml|cas)Service)MetaData$/ ) { if ( $leaf->{title} =~ /^((?:oidc|saml|cas)Service)MetaData$/ ) {
no strict 'refs'; no strict 'refs';
my @tmp = $self->scanLeaf( $leaf->{nodes} ); my @tmp = $self->scanLeaf( $leaf->{nodes} );

View File

@ -358,7 +358,7 @@ sub attributes {
msgFail => '__badUrl__', msgFail => '__badUrl__',
}, },
portalCustomCss => { portalCustomCss => {
type => 'text', type => 'text',
documentation => 'Path to custom CSS file', documentation => 'Path to custom CSS file',
}, },
portalStatus => { portalStatus => {
@ -413,6 +413,11 @@ sub attributes {
type => 'text', type => 'text',
documentation => 'Prefix of static files for HTML templates', documentation => 'Prefix of static files for HTML templates',
}, },
groupsBeforeMacros => {
type => 'bool',
default => 0,
documentation => 'Compute groups before macros',
},
multiValuesSeparator => { multiValuesSeparator => {
type => 'authParamsText', type => 'authParamsText',
default => '; ', default => '; ',
@ -788,6 +793,23 @@ sub attributes {
documentation => documentation =>
'Brute force attack protection -> Max allowed failed login', 'Brute force attack protection -> Max allowed failed login',
}, },
bruteForceProtectionMaxLockTime => {
default => 900,
type => 'int',
documentation =>
'Brute force attack protection -> Max lock time',
},
bruteForceProtectionIncrementalTempo => {
default => 0,
help => 'bruteforceprotection.html',
type => 'bool',
documentation => 'Enable incremental lock time for brute force attack protection',
},
bruteForceProtectionLockTimes => {
type => 'text',
default => '5 15 60 300 600',
documentation => 'Incremental lock time values for brute force attack protection',
},
grantSessionRules => { grantSessionRules => {
type => 'grantContainer', type => 'grantContainer',
keyTest => sub { return perlExpr(@_) }, keyTest => sub { return perlExpr(@_) },
@ -800,6 +822,11 @@ sub attributes {
default => '_password _2fDevices', default => '_password _2fDevices',
documentation => 'Name of attributes to hide in logs', documentation => 'Name of attributes to hide in logs',
}, },
persistentSessionAttributes => {
type => 'text',
default => '_loginHistory _2fDevices notification_',
documentation => 'Persistent session attributes to hide',
},
key => { key => {
type => 'password', type => 'password',
documentation => 'Secret key', documentation => 'Secret key',
@ -1142,6 +1169,16 @@ sub attributes {
type => 'bool', type => 'bool',
documentation => 'Notification activation', documentation => 'Notification activation',
}, },
notificationsExplorer => {
default => 0,
type => 'bool',
documentation => 'Notifications explorer activation',
},
notificationsMaxRetrieve => {
default => 3,
type => 'int',
documentation => 'Max number of displayed notifications',
},
notificationServer => { notificationServer => {
default => 0, default => 0,
type => 'bool', type => 'bool',
@ -1985,6 +2022,13 @@ sub attributes {
documentation => 'Enable /portal.wsdl server', documentation => 'Enable /portal.wsdl server',
}, },
# SOAP Procy client
soapProxyUrn => {
default => 'urn:Lemonldap/NG/Common/PSGI/SOAPService',
type => 'text',
documentation => 'SOAP URN for Proxy',
},
# AutoSignin # AutoSignin
autoSigninRules => { autoSigninRules => {
type => 'keyTextContainer', type => 'keyTextContainer',
@ -3159,6 +3203,7 @@ m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
}, },
SSLVarIf => { SSLVarIf => {
type => 'keyTextContainer', type => 'keyTextContainer',
keyTest => sub { 1 },
default => {} default => {}
}, },
sslByAjax => { sslByAjax => {

View File

@ -584,7 +584,7 @@ sub tree {
{ {
title => 'reloadParams', title => 'reloadParams',
help => 'configlocation.html#configuration_reload', help => 'configlocation.html#configuration_reload',
nodes => [ 'reloadUrls', 'reloadTimeout', 'compactConf' ] nodes => [ 'reloadTimeout', 'compactConf', 'reloadUrls' ]
}, },
{ {
title => 'plugins', title => 'plugins',
@ -620,11 +620,12 @@ sub tree {
help => 'notifications.html', help => 'notifications.html',
nodes => [ nodes => [
'notification', 'notification',
'notificationsExplorer',
'notificationWildcard',
'oldNotifFormat', 'oldNotifFormat',
'notificationXSLTfile',
'notificationStorage', 'notificationStorage',
'notificationStorageOptions', 'notificationStorageOptions',
'notificationWildcard',
'notificationXSLTfile',
{ {
title => 'serverNotification', title => 'serverNotification',
help => 'notifications.html#server', help => 'notifications.html#server',
@ -818,6 +819,25 @@ sub tree {
'u2fLogo', 'u2fLogo',
] ]
}, },
{
title => 'yubikey2f',
help => 'yubikey2f.html',
form => 'simpleInputContainer',
nodes => [
'yubikey2fActivation',
'yubikey2fSelfRegistration',
'yubikey2fClientID',
'yubikey2fSecretKey',
'yubikey2fNonce',
'yubikey2fUrl',
'yubikey2fPublicIDSize',
'yubikey2fUserCanRemoveKey',
'yubikey2fTTL',
'yubikey2fAuthnLevel',
'yubikey2fLabel',
'yubikey2fLogo',
],
},
{ {
title => 'mail2f', title => 'mail2f',
help => 'mail2f.html', help => 'mail2f.html',
@ -865,25 +885,6 @@ sub tree {
'rest2fLabel', 'rest2fLogo', 'rest2fLabel', 'rest2fLogo',
] ]
}, },
{
title => 'yubikey2f',
help => 'yubikey2f.html',
form => 'simpleInputContainer',
nodes => [
'yubikey2fActivation',
'yubikey2fSelfRegistration',
'yubikey2fClientID',
'yubikey2fSecretKey',
'yubikey2fNonce',
'yubikey2fUrl',
'yubikey2fPublicIDSize',
'yubikey2fUserCanRemoveKey',
'yubikey2fTTL',
'yubikey2fAuthnLevel',
'yubikey2fLabel',
'yubikey2fLogo',
],
},
'sfExtra', 'sfExtra',
{ {
title => 'sfRemovedNotification', title => 'sfRemovedNotification',
@ -904,6 +905,7 @@ sub tree {
nodes => [ nodes => [
'customFunctions', 'customFunctions',
'multiValuesSeparator', 'multiValuesSeparator',
'groupsBeforeMacros',
{ {
title => 'SMTP', title => 'SMTP',
help => 'smtp.html', help => 'smtp.html',
@ -937,12 +939,21 @@ sub tree {
'trustedDomains', 'trustedDomains',
'useSafeJail', 'useSafeJail',
'checkXSS', 'checkXSS',
'bruteForceProtection',
'requireToken', 'requireToken',
'formTimeout', 'formTimeout',
'tokenUseGlobalStorage', 'tokenUseGlobalStorage',
{
title => 'bruteForceAttackProtection',
help => 'bruteforceprotection.html',
form => 'simpleInputContainer',
nodes => [
'bruteForceProtection',
'bruteForceProtectionIncrementalTempo',
]
},
'lwpOpts', 'lwpOpts',
'lwpSslOpts', 'lwpSslOpts',
{ {
title => 'contentSecurityPolicy', title => 'contentSecurityPolicy',
help => 'security.html#portal', help => 'security.html#portal',

View File

@ -26,6 +26,7 @@ has req => ( is => 'ro' );
has sep => ( is => 'rw', isa => 'Str', default => '/' ); has sep => ( is => 'rw', isa => 'Str', default => '/' );
has format => ( is => 'rw', isa => 'Str', default => "%-25s | %-25s | %-25s" ); has format => ( is => 'rw', isa => 'Str', default => "%-25s | %-25s | %-25s" );
has yes => ( is => 'rw', isa => 'Bool', default => 0 ); has yes => ( is => 'rw', isa => 'Bool', default => 0 );
has safe => ( is => 'rw', isa => 'Bool', default => 0 );
has force => ( is => 'rw', isa => 'Bool', default => 0 ); has force => ( is => 'rw', isa => 'Bool', default => 0 );
has logger => ( is => 'ro', lazy => 1, builder => sub { $_[0]->mgr->logger } ); has logger => ( is => 'ro', lazy => 1, builder => sub { $_[0]->mgr->logger } );
has userLogger => has userLogger =>
@ -75,7 +76,7 @@ sub set {
} }
} }
require Clone; require Clone;
my $new = Clone::clone( $self->mgr->currentConf ); my $new = Clone::clone( $self->mgr->hLoadedPlugins->{conf}->currentConf );
foreach my $key ( keys %pairs ) { foreach my $key ( keys %pairs ) {
$self->_setKey( $new, $key, $pairs{$key} ); $self->_setKey( $new, $key, $pairs{$key} );
} }
@ -100,7 +101,7 @@ sub addKey {
push @list, [ $root, $newKey, $value ]; push @list, [ $root, $newKey, $value ];
} }
require Clone; require Clone;
my $new = Clone::clone( $self->mgr->currentConf ); my $new = Clone::clone( $self->mgr->hLoadedPlugins->{conf}->currentConf );
foreach my $el (@list) { foreach my $el (@list) {
my @path = split $sep, $el->[0]; my @path = split $sep, $el->[0];
if ( $#path == 0 ) { if ( $#path == 0 ) {
@ -141,7 +142,7 @@ sub delKey {
push @list, [ $root, $key ]; push @list, [ $root, $key ];
} }
require Clone; require Clone;
my $new = Clone::clone( $self->mgr->currentConf ); my $new = Clone::clone( $self->mgr->hLoadedPlugins->{conf}->currentConf );
foreach my $el (@list) { foreach my $el (@list) {
my @path = split $sep, $el->[0]; my @path = split $sep, $el->[0];
if ( $#path == 0 ) { if ( $#path == 0 ) {
@ -199,7 +200,7 @@ sub lastCfg {
sub save { sub save {
my ($self) = @_; my ($self) = @_;
my $conf = $self->jsonResponse( '/confs/latest', 'full=1' ); my $conf = $self->jsonResponse( '/confs/' . $self->cfgNum, 'full=1' );
my $json = JSON->new->indent->canonical; my $json = JSON->new->indent->canonical;
print $json->encode($conf); print $json->encode($conf);
} }
@ -228,6 +229,33 @@ sub restore {
print STDERR Dumper($res); print STDERR Dumper($res);
} }
sub rollback {
my ($self) = @_;
my $lastCfg = $self->mgr->confAcc->lastCfg;
my $previousCfg = $lastCfg - 1;
my $conf =
$self->mgr->confAcc->getConf( { cfgNum => $previousCfg, raw => 1 } )
or die $Lemonldap::NG::Common::Conf::msg;
$conf->{cfgNum} = $lastCfg;
$conf->{cfgAuthor} = scalar( getpwuid $< ) . '(command-line-interface)';
chomp $conf->{cfgAuthor};
$conf->{cfgAuthorIP} = '127.0.0.1';
$conf->{cfgDate} = time;
$conf->{cfgVersion} = $Lemonldap::NG::Manager::VERSION;
$conf->{cfgLog} = $self->log // "Rolled back configuration $lastCfg";
my $s = $self->mgr->confAcc->saveConf($conf);
if ( $s > 0 ) {
$self->logger->info("CLI: Configuration $lastCfg has been rolled back");
print STDERR "Configuration $lastCfg has been rolled back\n";
}
else {
$self->logger->error("CLI: Failed to rollback configuration $lastCfg");
print STDERR "Failed to rollback configuration $lastCfg\n";
}
}
sub _getKey { sub _getKey {
my ( $self, $key ) = @_; my ( $self, $key ) = @_;
my $sep = $self->sep; my $sep = $self->sep;
@ -236,7 +264,8 @@ sub _getKey {
warn "Malformed key $base"; warn "Malformed key $base";
return (); return ();
} }
my $value = $self->mgr->getConfKey( $self->req, $base, noCache => 1 ); my $value = $self->mgr->hLoadedPlugins->{conf}
->getConfKey( $self->req, $base, noCache => 1 );
if ( $self->req->error ) { if ( $self->req->error ) {
die $self->req->error; die $self->req->error;
} }
@ -272,14 +301,19 @@ sub _save {
require Lemonldap::NG::Manager::Conf::Parser; require Lemonldap::NG::Manager::Conf::Parser;
my $parser = Lemonldap::NG::Manager::Conf::Parser->new( { my $parser = Lemonldap::NG::Manager::Conf::Parser->new( {
newConf => $new, newConf => $new,
refConf => $self->mgr->currentConf, refConf => $self->mgr->hLoadedPlugins->{conf}->currentConf,
req => $self->req req => $self->req
} }
); );
unless ( $parser->testNewConf( $self->localConf ) ) { unless ( $parser->testNewConf( $self->localConf ) ) {
$self->logger->error( my $msg = "Configuration rejected with message: " . $parser->message;
"CLI: Configuration rejected with message: $parser->{message}"); $self->logger->error("CLI: $msg");
printf STDERR "Modifications rejected: %s:\n", $parser->{message}; if ( $self->safe ) {
die "$msg";
}
else {
print STDERR "$msg\n";
}
} }
my $saveParams = { force => $self->force }; my $saveParams = { force => $self->force };
if ( $self->force and $self->cfgNum ) { if ( $self->force and $self->cfgNum ) {
@ -304,11 +338,13 @@ sub _save {
"CLI: Configuration $s has been saved by $new->{cfgAuthor}"); "CLI: Configuration $s has been saved by $new->{cfgAuthor}");
$self->logger->info("CLI: Configuration $s saved"); $self->logger->info("CLI: Configuration $s saved");
print STDERR "Saved under number $s\n"; print STDERR "Saved under number $s\n";
$parser->{status} = [ $self->mgr->applyConf($new) ]; $parser->{status} =
[ $self->mgr->hLoadedPlugins->{conf}->applyConf($new) ];
} }
else { else {
$self->logger->error("CLI: Configuration not saved!"); $self->logger->error("CLI: Configuration not saved!");
printf STDERR "Modifications rejected: %s:\n", $parser->{message}; printf STDERR "Modifications rejected: %s:\n", $parser->{message}
if $parser->{message};
print STDERR Dumper($parser); print STDERR Dumper($parser);
} }
foreach (qw(errors warnings status)) { foreach (qw(errors warnings status)) {
@ -346,7 +382,7 @@ sub run {
} }
$self->cfgNum( $self->lastCfg ) unless ( $self->cfgNum ); $self->cfgNum( $self->lastCfg ) unless ( $self->cfgNum );
my $action = shift; my $action = shift;
unless ( $action =~ /^(?:get|set|addKey|delKey|save|restore)$/ ) { unless ( $action =~ /^(?:get|set|addKey|delKey|save|restore|rollback)$/ ) {
die "Unknown action $action. Only get, set, addKey or delKey allowed"; die "Unknown action $action. Only get, set, addKey or delKey allowed";
} }

View File

@ -17,7 +17,8 @@ use URI::URL;
use feature 'state'; use feature 'state';
extends 'Lemonldap::NG::Common::Conf::RESTServer'; extends 'Lemonldap::NG::Manager::Plugin',
'Lemonldap::NG::Common::Conf::RESTServer';
our $VERSION = '2.1.0'; our $VERSION = '2.1.0';
@ -27,36 +28,11 @@ our $VERSION = '2.1.0';
use constant defaultRoute => 'manager.html'; use constant defaultRoute => 'manager.html';
has ua => ( is => 'rw' ); has ua => ( is => 'rw' );
has diffRule => ( is => 'rw', default => sub { 0 } );
has brwRule => ( is => 'rw', default => sub { 0 } );
sub addRoutes { sub init {
my ( $self, $conf ) = @_; my ( $self, $conf ) = @_;
$self->ua( Lemonldap::NG::Common::UserAgent->new($conf) ); $self->ua( Lemonldap::NG::Common::UserAgent->new($conf) );
my $hd = "Lemonldap::NG::Handler::PSGI::Main";
# Parse Diff activation rule
$self->logger->debug(
"Diff activation rule -> " . ( $self->{viewerAllowDiff} // 0 ) );
my $rule = $hd->buildSub( $hd->substitute( $self->{viewerAllowDiff} ) );
unless ($rule) {
$self->error(
"Bad Diff activation rule -> " . $hd->tsv->{jail}->error );
return 0;
}
$self->diffRule($rule);
# Parse Browser activation rule
$self->logger->debug(
"Browser activation rule -> " . ( $self->{viewerAllowBrowser} // 0 ) );
$rule = $hd->buildSub( $hd->substitute( $self->{viewerAllowBrowser} ) );
unless ($rule) {
$self->error(
"Bad Browser activation rule -> " . $hd->tsv->{jail}->error );
return 0;
}
$self->brwRule($rule);
# HTML template # HTML template
$self->addRoute( 'manager.html', undef, ['GET'] ) $self->addRoute( 'manager.html', undef, ['GET'] )
@ -95,6 +71,7 @@ sub addRoutes {
# Url loader # Url loader
->addRoute( 'prx', undef, ['POST'] ); ->addRoute( 'prx', undef, ['POST'] );
return 1;
} }
# 35 - New RSA key pair on demand # 35 - New RSA key pair on demand
@ -268,7 +245,7 @@ sub newConf {
} }
} }
if ( $res->{result} ) { if ( $res->{result} ) {
if ( $self->{demoMode} ) { if ( $self->p->{demoMode} ) {
$res->{message} = '__demoModeOn__'; $res->{message} = '__demoModeOn__';
} }
else { else {
@ -279,7 +256,7 @@ sub newConf {
unless ( @{ $parser->{needConfirmation} } && !$args{force} ); unless ( @{ $parser->{needConfirmation} } && !$args{force} );
if ( $s > 0 ) { if ( $s > 0 ) {
$self->userLogger->notice( $self->userLogger->notice(
'User ' . $self->userId($req) . " has stored conf $s" ); 'User ' . $self->p->userId($req) . " has stored conf $s" );
$res->{result} = 1; $res->{result} = 1;
$res->{cfgNum} = $s; $res->{cfgNum} = $s;
if ( my $status = $self->applyConf( $parser->newConf ) ) { if ( my $status = $self->applyConf( $parser->newConf ) ) {
@ -291,7 +268,7 @@ sub newConf {
else { else {
$self->userLogger->notice( $self->userLogger->notice(
'Saving attempt rejected, asking for confirmation to ' 'Saving attempt rejected, asking for confirmation to '
. $self->userId($req) ); . $self->p->userId($req) );
$res->{result} = 0; $res->{result} = 0;
if ( $s == CONFIG_WAS_CHANGED ) { if ( $s == CONFIG_WAS_CHANGED ) {
$res->{needConfirm} = 1; $res->{needConfirm} = 1;
@ -324,7 +301,7 @@ sub newRawConf {
} }
my $res = {}; my $res = {};
if ( $self->{demoMode} ) { if ( $self->p->{demoMode} ) {
$res->{message} = '__demoModeOn__'; $res->{message} = '__demoModeOn__';
} }
else { else {
@ -332,15 +309,16 @@ sub newRawConf {
# chances to be equal to last config cfgNum # chances to be equal to last config cfgNum
my $s = $self->confAcc->saveConf( $new, force => 1 ); my $s = $self->confAcc->saveConf( $new, force => 1 );
if ( $s > 0 ) { if ( $s > 0 ) {
$self->userLogger->notice( $self->userLogger->notice( 'User '
'User ' . $self->userId($req) . " has stored (raw) conf $s" ); . $self->p->userId($req)
. " has stored (raw) conf $s" );
$res->{result} = 1; $res->{result} = 1;
$res->{cfgNum} = $s; $res->{cfgNum} = $s;
} }
else { else {
$self->userLogger->notice( $self->userLogger->notice(
'Raw saving attempt rejected, asking for confirmation to ' 'Raw saving attempt rejected, asking for confirmation to '
. $self->userId($req) ); . $self->p->userId($req) );
$res->{result} = 0; $res->{result} = 0;
$res->{needConfirm} = 1 if ( $s == CONFIG_WAS_CHANGED ); $res->{needConfirm} = 1 if ( $s == CONFIG_WAS_CHANGED );
$res->{message} .= '__needConfirmation__'; $res->{message} .= '__needConfirmation__';
@ -359,7 +337,7 @@ sub applyConf {
my $status; my $status;
# 1 Apply conf locally # 1 Apply conf locally
$self->api->checkConf(); $self->p->api->checkConf();
# Get apply section values # Get apply section values
my %reloadUrls = my %reloadUrls =

View File

@ -51,7 +51,10 @@ has changes => ( is => 'rw', isa => 'ArrayRef', default => sub { return [] } );
has message => ( has message => (
is => 'rw', is => 'rw',
isa => 'Str', isa => 'Str',
default => '', lazy => 1,
default => sub {
return join( ', ', map { $_->{message} } @{ $_[0]->errors } );
},
trigger => sub { trigger => sub {
hdebug( "Message becomes " . $_[0]->{message} ); hdebug( "Message becomes " . $_[0]->{message} );
} }
@ -1111,7 +1114,8 @@ sub _unitTest {
my $res = 1; my $res = 1;
foreach my $key ( keys %$conf ) { foreach my $key ( keys %$conf ) {
if ( $localConf->{skippedUnitTests} if ( $localConf
and $localConf->{skippedUnitTests}
and $localConf->{skippedUnitTests} =~ /\b$key\b/ ) and $localConf->{skippedUnitTests} =~ /\b$key\b/ )
{ {
$localConf->logger->debug("-> Ignore test for $key\n"); $localConf->logger->debug("-> Ignore test for $key\n");
@ -1121,7 +1125,8 @@ sub _unitTest {
my $attr = $attrs->{$key}; my $attr = $attrs->{$key};
my $type = $types->{ $attr->{type} } if $attr; my $type = $types->{ $attr->{type} } if $attr;
unless ( $type or $attr->{test} ) { unless ( $type or $attr->{test} ) {
$localConf->logger->debug("Unknown attribute $key, deleting it\n"); $localConf->logger->debug("Unknown attribute $key, deleting it\n")
if $localConf;
delete $conf->{$key}; delete $conf->{$key};
next; next;
} }
@ -1156,8 +1161,6 @@ sub _unitTest {
if ( $key =~ /^(?:$simpleHashKeys|$doubleHashKeys)$/o if ( $key =~ /^(?:$simpleHashKeys|$doubleHashKeys)$/o
or $attr->{type} =~ /Container$/ ) or $attr->{type} =~ /Container$/ )
{ {
my $keyMsg = $attr->{keyMsgFail} // $type->{keyMsgFail};
my $msg = $attr->{msgFail} // $type->{msgFail};
$res = 0 $res = 0
unless ( unless (
$self->_execTest( { $self->_execTest( {
@ -1244,8 +1247,7 @@ sub _execTest {
# #
#@return true if tests succeed #@return true if tests succeed
sub _globalTest { sub _globalTest {
my $self = shift; my ( $self, $localConf ) = @_;
my $localConf = shift;
require Lemonldap::NG::Manager::Conf::Tests; require Lemonldap::NG::Manager::Conf::Tests;
hdebug('# _globalTest()'); hdebug('# _globalTest()');
@ -1253,10 +1255,12 @@ sub _globalTest {
my $tests = &Lemonldap::NG::Manager::Conf::Tests::tests( $self->newConf ); my $tests = &Lemonldap::NG::Manager::Conf::Tests::tests( $self->newConf );
foreach my $name ( keys %$tests ) { foreach my $name ( keys %$tests ) {
if ( $localConf->{skippedGlobalTests} if ( $localConf
and $localConf->{skippedGlobalTests}
and $localConf->{skippedGlobalTests} =~ /\b$name\b/ ) and $localConf->{skippedGlobalTests} =~ /\b$name\b/ )
{ {
$localConf->logger->debug("-> Ignore test for $name\n"); $localConf->logger->debug("-> Ignore test for $name\n")
if $localConf;
next; next;
} }
my $sub = $tests->{$name}; my $sub = $tests->{$name};
@ -1278,7 +1282,7 @@ sub _globalTest {
}; };
if ($@) { if ($@) {
push @{ $self->warnings }, "Test $name failed: $@"; push @{ $self->warnings }, "Test $name failed: $@";
$localConf->logger->debug("Test $name failed: $@\n"); $localConf->logger->debug("Test $name failed: $@\n") if $localConf;
} }
} }
return $result; return $result;

View File

@ -607,11 +607,11 @@ sub tests {
# Warn if issuers token TTL is higher than 30s # Warn if issuers token TTL is higher than 30s
issuersTimeout => sub { issuersTimeout => sub {
return 1 unless ( defined $conf->{issuerTimeout} ); return 1 unless ( defined $conf->{issuersTimeout} );
return ( 0, "Issuers token TTL must be higher than 30s" ) return ( 0, "Issuers token TTL must be higher than 30s" )
unless ( $conf->{issuerTimeout} > 30 ); unless ( $conf->{issuersTimeout} > 30 );
return ( 1, "Issuers token TTL should not be higher than 2mn" ) return ( 1, "Issuers token TTL should not be higher than 2mn" )
if ( $conf->{issuerTimeout} > 120 ); if ( $conf->{issuersTimeout} > 120 );
# Return # Return
return 1; return 1;
@ -755,6 +755,27 @@ sub tests {
return ( $res, join( ', ', @msg ) ); return ( $res, join( ', ', @msg ) );
}, },
# RS* OIDC algs require a signing key
oidcRPNeedRSAKey => sub {
return 1
unless ( $conf->{oidcRPMetaDataOptions}
and %{ $conf->{oidcRPMetaDataOptions} } );
my @usingRSA = grep {
$conf->{oidcRPMetaDataOptions}->{$_}
->{oidcRPMetaDataOptionsIDTokenSignAlg}
and $conf->{oidcRPMetaDataOptions}->{$_}
->{oidcRPMetaDataOptionsIDTokenSignAlg} =~ /^RS/
} keys %{ $conf->{oidcRPMetaDataOptions} };
if ( @usingRSA and not $conf->{oidcServicePrivateKeySig} ) {
my $msg =
join( ", ", @usingRSA )
. ": using RS-type encryption, but no RSA key is defined in global OIDC configuration";
return ( 0, $msg );
}
return 1;
},
}; };
} }

View File

@ -13,7 +13,8 @@ require Lemonldap::NG::Common::Notifications;
use feature 'state'; use feature 'state';
extends 'Lemonldap::NG::Common::Conf::AccessLib'; extends 'Lemonldap::NG::Manager::Plugin',
'Lemonldap::NG::Common::Conf::AccessLib';
our $VERSION = '2.1.0'; our $VERSION = '2.1.0';
@ -26,7 +27,7 @@ has notifFormat => ( is => 'rw' );
use constant defaultRoute => 'notifications.html'; use constant defaultRoute => 'notifications.html';
sub addRoutes { sub init {
my ( $self, $conf ) = @_; my ( $self, $conf ) = @_;
if ( $conf->{oldNotifFormat} ) { if ( $conf->{oldNotifFormat} ) {
@ -74,6 +75,7 @@ sub addRoutes {
{ done => { ':notificationId' => 'deleteDoneNotification' } }, { done => { ':notificationId' => 'deleteDoneNotification' } },
['DELETE'] ['DELETE']
); );
return 1;
} }
sub setNotifAccess { sub setNotifAccess {

View File

@ -0,0 +1,51 @@
package Lemonldap::NG::Manager::Plugin;
use strict;
use Mouse;
our $VERSION = '2.0.8';
extends 'Lemonldap::NG::Common::Module';
has _confAcc => (
is => 'rw',
lazy => 1,
default => sub { return $_[0]->p->{_confAcc} },
);
sub sendError {
my $self = shift;
return $self->p->sendError(@_);
}
sub sendJSONresponse {
my $self = shift;
return $self->p->sendJSONresponse(@_);
}
sub addRoute {
my ( $self, $word, $subName, $methods, $transform ) = @_;
$transform //= sub {
my ($sub) = @_;
if ( ref $sub ) {
return sub {
shift;
return $sub->( $self, @_ );
}
}
else {
return sub {
shift;
return $self->$sub(@_);
}
}
};
$self->p->addRoute( $word, $subName, $methods, $transform );
return $self;
}
sub loadTemplate {
my $self = shift;
return $self->p->loadTemplate(@_);
}
1;

View File

@ -14,7 +14,8 @@ use Lemonldap::NG::Common::IPv6;
use feature 'state'; use feature 'state';
extends 'Lemonldap::NG::Common::Conf::AccessLib', extends 'Lemonldap::NG::Manager::Plugin',
'Lemonldap::NG::Common::Conf::AccessLib',
'Lemonldap::NG::Common::Session::REST'; 'Lemonldap::NG::Common::Session::REST';
our $VERSION = '2.1.0'; our $VERSION = '2.1.0';
@ -25,7 +26,7 @@ our $VERSION = '2.1.0';
use constant defaultRoute => 'sessions.html'; use constant defaultRoute => 'sessions.html';
sub addRoutes { sub init {
my ( $self, $conf ) = @_; my ( $self, $conf ) = @_;
# HTML template # HTML template
@ -55,6 +56,7 @@ sub addRoutes {
$self->{multiValuesSeparator} ||= '; '; $self->{multiValuesSeparator} ||= '; ';
$self->{impersonationPrefix} = $conf->{impersonationPrefix} || 'real_'; $self->{impersonationPrefix} = $conf->{impersonationPrefix} || 'real_';
$self->{hiddenAttributes} //= "_password"; $self->{hiddenAttributes} //= "_password";
return 1;
} }
####################### #######################
@ -98,7 +100,7 @@ sub sessions {
or return $self->sendError( $req, undef, 400 ); or return $self->sendError( $req, undef, 400 );
my $params = $req->parameters(); my $params = $req->parameters();
my $type = delete $params->{sessionType}; my $type = delete $params->{sessionType};
$type = $type eq 'global' ? 'SSO' : ucfirst($type); $type = $type eq 'global' ? 'SSO' : ucfirst($type);
$type = $type eq 'Offline' ? 'OIDCI' : ucfirst($type); $type = $type eq 'Offline' ? 'OIDCI' : ucfirst($type);
my $res; my $res;

View File

@ -11,6 +11,9 @@ use feature 'state';
extends 'Lemonldap::NG::Manager::Conf'; extends 'Lemonldap::NG::Manager::Conf';
has diffRule => ( is => 'rw', default => sub { 0 } );
has brwRule => ( is => 'rw', default => sub { 0 } );
our $VERSION = '2.1.0'; our $VERSION = '2.1.0';
############################# #############################
@ -21,11 +24,36 @@ use constant defaultRoute => 'viewer.html';
has ua => ( is => 'rw' ); has ua => ( is => 'rw' );
sub addRoutes { sub init {
my ( $self, $conf ) = @_; my ( $self, $conf ) = @_;
$self->ua( Lemonldap::NG::Common::UserAgent->new($conf) ); $self->ua( Lemonldap::NG::Common::UserAgent->new($conf) );
my $hd = "Lemonldap::NG::Handler::PSGI::Main";
my $hiddenKeys = $self->{viewerHiddenKeys} || ''; # Parse Diff activation rule
$conf->{viewerAllowDiff} //= 0;
$self->logger->debug(
"Diff activation rule -> " . ( $conf->{viewerAllowDiff} ) );
my $rule = $hd->buildSub( $hd->substitute( $conf->{viewerAllowDiff} ) );
unless ($rule) {
$self->logger->error(
"Bad Diff activation rule -> " . $hd->tsv->{jail}->error );
return 0;
}
$self->diffRule($rule);
# Parse Browser activation rule
$conf->{viewerAllowBrowser} //= 0;
$self->logger->debug(
"Browser activation rule -> " . ( $conf->{viewerAllowBrowser} ) );
$rule = $hd->buildSub( $hd->substitute( $conf->{viewerAllowBrowser} ) );
unless ($rule) {
$self->logger->error(
"Bad Browser activation rule -> " . $hd->tsv->{jail}->error );
return 0;
}
$self->brwRule($rule);
my $hiddenKeys = $conf->{viewerHiddenKeys} || '';
my @enabledKeys = (); my @enabledKeys = ();
my @keys = qw(virtualHosts samlIDPMetaDataNodes samlSPMetaDataNodes my @keys = qw(virtualHosts samlIDPMetaDataNodes samlSPMetaDataNodes
applicationList oidcOPMetaDataNodes oidcRPMetaDataNodes applicationList oidcOPMetaDataNodes oidcRPMetaDataNodes
@ -67,6 +95,7 @@ sub addRoutes {
# Other keys # Other keys
->addRoute( view => { ':cfgNum' => { '*' => 'viewKey' } }, ['GET'] ); ->addRoute( view => { ':cfgNum' => { '*' => 'viewKey' } }, ['GET'] );
return 1;
} }
sub getConfByNum { sub getConfByNum {
@ -80,7 +109,8 @@ sub viewDiff {
# Check Diff activation rule # Check Diff activation rule
unless ( $self->diffRule->( $req, $req->{userData} ) ) { unless ( $self->diffRule->( $req, $req->{userData} ) ) {
my $user = $req->{userData}->{_whatToTrace} || 'anonymous'; my $user = $req->{userData}->{_whatToTrace} || 'anonymous';
$self->userLogger->warn("$user is not authorized to compare configurations"); $self->userLogger->warn(
"$user is not authorized to compare configurations");
return $self->sendJSONresponse( $req, { 'value' => '_Hidden_' } ); return $self->sendJSONresponse( $req, { 'value' => '_Hidden_' } );
} }
@ -144,7 +174,8 @@ sub viewKey {
$self->logger->debug( $self->logger->debug(
" $req->{env}->{REQUEST_URI} -> URI FORBIDDEN"); " $req->{env}->{REQUEST_URI} -> URI FORBIDDEN");
my $user = $req->{userData}->{_whatToTrace} || 'anonymous'; my $user = $req->{userData}->{_whatToTrace} || 'anonymous';
$self->userLogger->warn("$user is not authorized to browse configurations"); $self->userLogger->warn(
"$user is not authorized to browse configurations");
$self->rejectKey( $req, @args ); $self->rejectKey( $req, @args );
} }
} }

View File

@ -63,18 +63,17 @@ if (`diff $refFile $editFile`) {
# Update author and date # Update author and date
$VAR1->{cfgAuthor} = $ENV{SUDO_USER} || $ENV{LOGNAME} || "lmConfigEditor"; $VAR1->{cfgAuthor} = $ENV{SUDO_USER} || $ENV{LOGNAME} || "lmConfigEditor";
$VAR1->{cfgAuthorIP} = $ENV{SSH_CONNECTION} || "localhost"; $VAR1->{cfgAuthorIP} = $ENV{SSH_CONNECTION} || "localhost";
$VAR1->{cfgDate} = time(); $VAR1->{cfgDate} = time();
$VAR1->{cfgLog} ||= 'Edited by lmConfigEditor'; $VAR1->{cfgLog} ||= 'Edited by lmConfigEditor';
# Test new configuration # Test new configuration
my $parser = Lemonldap::NG::Manager::Conf::Parser->new( my $parser = Lemonldap::NG::Manager::Conf::Parser->new( {
{
refConf => $refConf, refConf => $refConf,
newConf => $VAR1, newConf => $VAR1,
req => 1, req => 1,
} }
); );
unless ( $parser->testNewConf ) { unless ( $parser->testNewConf(undef) ) {
print STDERR "Configuration seems to have some errors:\n "; print STDERR "Configuration seems to have some errors:\n ";
print STDERR Dumper( print STDERR Dumper(
{ errors => $parser->errors, warnings => $parser->warnings } ); { errors => $parser->errors, warnings => $parser->warnings } );

View File

@ -13,7 +13,7 @@ displayError = (j, status, err) ->
console.log 'Error', err console.log 'Error', err
res = JSON.parse j.responseText res = JSON.parse j.responseText
if res and res.error if res and res.error
res = res.error.replace /.* /, '' res = res.error.replace(/.* /, '')
console.log 'Returned error', res console.log 'Returned error', res
setMsg res, 'warning' setMsg res, 'warning'

View File

@ -193,6 +193,14 @@ llapp.controller 'NotificationsExplorerCtrl', [ '$scope', '$translator', '$locat
, (resp) -> , (resp) ->
$scope.waiting = false $scope.waiting = false
# Highlight current selection
console.log "Selection", $scope.type
$scope.activesStyle = {color: '#777'}
$scope.doneStyle = {color: '#777'}
$scope.newStyle = {color: '#777'}
$scope.activesStyle = {color: '#333'} if $scope.type == 'actives'
$scope.doneStyle = {color: '#333'} if $scope.type == 'done'
$scope.displayNotification = (scope) -> $scope.displayNotification = (scope) ->
$scope.waiting = true $scope.waiting = true
$scope.currentScope = scope $scope.currentScope = scope
@ -206,11 +214,16 @@ llapp.controller 'NotificationsExplorerCtrl', [ '$scope', '$translator', '$locat
reference: node.reference reference: node.reference
condition: node.condition condition: node.condition
if $scope.type == 'actives' if $scope.type == 'actives'
notif = JSON.parse response.data.notifications try
$scope.currentNotification.text = notif.text console.log "Try to parse a JSON formated notification..."
$scope.currentNotification.title = notif.title notif = JSON.parse response.data.notifications
$scope.currentNotification.subtitle = notif.subtitle $scope.currentNotification.date = $scope.notifDate(notif.date)
$scope.currentNotification.notifications = response.data.notifications $scope.currentNotification.text = notif.text
$scope.currentNotification.title = notif.title
$scope.currentNotification.subtitle = notif.subtitle
catch e
console.log "Unable to parse JSON"
$scope.currentNotification.notifications = response.data.notifications
else else
$scope.currentNotification.done = response.data.done $scope.currentNotification.done = response.data.done
$scope.waiting = false $scope.waiting = false
@ -266,16 +279,19 @@ llapp.controller 'NotificationsExplorerCtrl', [ '$scope', '$translator', '$locat
message: data.error message: data.error
$scope.showModal "alert.html" $scope.showModal "alert.html"
$scope.waiting = false $scope.waiting = false
$scope.form.date = new Date()
, (response) -> , (response) ->
$scope.message = $scope.message =
title: 'notificationNotCreated' title: 'notificationNotCreated'
message: response.statusText message: response.statusText
$scope.showModal "alert.html" $scope.showModal "alert.html"
$scope.waiting = false $scope.waiting = false
$scope.form.date = new Date()
else else
$scope.message = $scope.message =
title: 'incompleteForm' title: 'incompleteForm'
$scope.showModal "alert.html" $scope.showModal "alert.html"
$scope.form.date = new Date()
$scope.init = -> $scope.init = ->
$scope.waiting = true $scope.waiting = true
@ -297,6 +313,9 @@ llapp.controller 'NotificationsExplorerCtrl', [ '$scope', '$translator', '$locat
$scope.myStyle = {color: '#ffb84d'} $scope.myStyle = {color: '#ffb84d'}
$scope.displayCreateForm = -> $scope.displayCreateForm = ->
$scope.activesStyle = {color: '#777'}
$scope.doneStyle = {color: '#777'}
$scope.newStyle = {color: '#333'}
$scope.waiting = true $scope.waiting = true
$translator.init($scope.lang).then -> $translator.init($scope.lang).then ->
$scope.currentNotification = null $scope.currentNotification = null

View File

@ -492,6 +492,15 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location',
, (resp) -> , (resp) ->
$scope.waiting = false $scope.waiting = false
# Highlight current selection
console.log "Selection", sessionType
$scope.navssoStyle = {color: '#777'}
$scope.offlineStyle = {color: '#777'}
$scope.persistentStyle = {color: '#777'}
$scope.navssoStyle = {color: '#333'} if sessionType == 'global'
$scope.offlineStyle = {color: '#333'} if sessionType == 'offline'
$scope.persistentStyle = {color: '#333'} if sessionType == 'persistent'
# Intialization function # Intialization function
# Simply set $scope.waiting to false during $translator and tree root # Simply set $scope.waiting to false during $translator and tree root
# initialization # initialization

View File

@ -86,7 +86,7 @@
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="row text-center"> <div class="row text-center">
<div class="col-md-2" ng-repeat="i in ['attach.png', 'bell.png', 'bookmark.png', 'configure.png', 'database.png', 'demo.png', 'docs.png', 'folder.png', 'gear.png', 'help.png', 'llng.png', 'mailappt.png', 'money.png', 'network.png', 'terminal.png', 'thumbnail.png','tools.png', 'tux.png', 'web.png', 'wheels.png']"> <div class="col-md-2" ng-repeat="i in ['attach.png', 'bell.png', 'bookmark.png', 'configure.png', 'database.png', 'demo.png', 'folder.png', 'gear.png', 'help.png', 'llng.png', 'mailappt.png', 'money.png', 'terminal.png', 'thumbnail.png', 'tux.png', 'web.png']">
<button class="btn llcontainer" ng-class="{'btn-default':currentNode.data.logo!=i,'btn-info':currentNode.data.logo==i}" ng-click="ok(currentNode.data.logo=i)"> <button class="btn llcontainer" ng-class="{'btn-default':currentNode.data.logo!=i,'btn-info':currentNode.data.logo==i}" ng-click="ok(currentNode.data.logo=i)">
<img ng-src="{{elem('portal').data}}static/common/apps/{{i}}" title="{{i}}" alt="{{i}}" /> <img ng-src="{{elem('portal').data}}static/common/apps/{{i}}" title="{{i}}" alt="{{i}}" />
</button> </button>

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.7 // Generated by CoffeeScript 1.12.8
/* /*
* 2ndFA Session explorer * 2ndFA Session explorer

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -195,7 +195,7 @@
over = 0; over = 0;
} }
if ($scope.type === 'done' || $scope.type === 'actives') { if ($scope.type === 'done' || $scope.type === 'actives') {
return $http.get(scriptname + "notifications/" + $scope.type + "?" + query).then(function(response) { $http.get(scriptname + "notifications/" + $scope.type + "?" + query).then(function(response) {
var data, i, len, n, ref; var data, i, len, n, ref;
data = response.data; data = response.data;
if (data.result) { if (data.result) {
@ -221,6 +221,26 @@
return $scope.waiting = false; return $scope.waiting = false;
}); });
} }
console.log("Selection", $scope.type);
$scope.activesStyle = {
color: '#777'
};
$scope.doneStyle = {
color: '#777'
};
$scope.newStyle = {
color: '#777'
};
if ($scope.type === 'actives') {
$scope.activesStyle = {
color: '#333'
};
}
if ($scope.type === 'done') {
return $scope.doneStyle = {
color: '#333'
};
}
}; };
$scope.displayNotification = function(scope) { $scope.displayNotification = function(scope) {
var node, notificationId; var node, notificationId;
@ -232,18 +252,25 @@
notificationId = node.uid + "_" + node.reference; notificationId = node.uid + "_" + node.reference;
} }
$http.get(scriptname + "notifications/" + $scope.type + "/" + notificationId).then(function(response) { $http.get(scriptname + "notifications/" + $scope.type + "/" + notificationId).then(function(response) {
var notif; var e, notif;
$scope.currentNotification = { $scope.currentNotification = {
uid: node.uid, uid: node.uid,
reference: node.reference, reference: node.reference,
condition: node.condition condition: node.condition
}; };
if ($scope.type === 'actives') { if ($scope.type === 'actives') {
notif = JSON.parse(response.data.notifications); try {
$scope.currentNotification.text = notif.text; console.log("Try to parse a JSON formated notification...");
$scope.currentNotification.title = notif.title; notif = JSON.parse(response.data.notifications);
$scope.currentNotification.subtitle = notif.subtitle; $scope.currentNotification.date = $scope.notifDate(notif.date);
$scope.currentNotification.notifications = response.data.notifications; $scope.currentNotification.text = notif.text;
$scope.currentNotification.title = notif.title;
$scope.currentNotification.subtitle = notif.subtitle;
} catch (error) {
e = error;
console.log("Unable to parse JSON");
$scope.currentNotification.notifications = response.data.notifications;
}
} else { } else {
$scope.currentNotification.done = response.data.done; $scope.currentNotification.done = response.data.done;
} }
@ -300,7 +327,7 @@
$scope.formPost.reference = $scope.form.reference; $scope.formPost.reference = $scope.form.reference;
$scope.formPost.condition = $scope.form.condition; $scope.formPost.condition = $scope.form.condition;
$scope.formPost.xml = $scope.form.xml; $scope.formPost.xml = $scope.form.xml;
return $http.post('notifications/actives', $scope.formPost).then(function(response) { $http.post('notifications/actives', $scope.formPost).then(function(response) {
var data; var data;
data = response.data; data = response.data;
$scope.form = {}; $scope.form = {};
@ -315,21 +342,24 @@
}; };
} }
$scope.showModal("alert.html"); $scope.showModal("alert.html");
return $scope.waiting = false; $scope.waiting = false;
return $scope.form.date = new Date();
}, function(response) { }, function(response) {
$scope.message = { $scope.message = {
title: 'notificationNotCreated', title: 'notificationNotCreated',
message: response.statusText message: response.statusText
}; };
$scope.showModal("alert.html"); $scope.showModal("alert.html");
return $scope.waiting = false; $scope.waiting = false;
return $scope.form.date = new Date();
}); });
} else { } else {
$scope.message = { $scope.message = {
title: 'incompleteForm' title: 'incompleteForm'
}; };
return $scope.showModal("alert.html"); $scope.showModal("alert.html");
} }
return $scope.form.date = new Date();
}; };
$scope.init = function() { $scope.init = function() {
$scope.waiting = true; $scope.waiting = true;
@ -349,6 +379,15 @@
}; };
}; };
$scope.displayCreateForm = function() { $scope.displayCreateForm = function() {
$scope.activesStyle = {
color: '#777'
};
$scope.doneStyle = {
color: '#777'
};
$scope.newStyle = {
color: '#333'
};
$scope.waiting = true; $scope.waiting = true;
return $translator.init($scope.lang).then(function() { return $translator.init($scope.lang).then(function() {
$scope.currentNotification = null; $scope.currentNotification = null;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -537,7 +537,7 @@
} else { } else {
over = 0; over = 0;
} }
return $http.get(scriptname + "sessions/" + sessionType + "?" + query).then(function(response) { $http.get(scriptname + "sessions/" + sessionType + "?" + query).then(function(response) {
var data, i, len, n, ref; var data, i, len, n, ref;
data = response.data; data = response.data;
if (data.result) { if (data.result) {
@ -565,6 +565,31 @@
}, function(resp) { }, function(resp) {
return $scope.waiting = false; return $scope.waiting = false;
}); });
console.log("Selection", sessionType);
$scope.navssoStyle = {
color: '#777'
};
$scope.offlineStyle = {
color: '#777'
};
$scope.persistentStyle = {
color: '#777'
};
if (sessionType === 'global') {
$scope.navssoStyle = {
color: '#333'
};
}
if (sessionType === 'offline') {
$scope.offlineStyle = {
color: '#333'
};
}
if (sessionType === 'persistent') {
return $scope.persistentStyle = {
color: '#333'
};
}
}; };
$scope.init = function() { $scope.init = function() {
$scope.waiting = true; $scope.waiting = true;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -104,7 +104,9 @@
"browserIdSiteName":"اسم الموقع", "browserIdSiteName":"اسم الموقع",
"browserIdVerificationURL":"التحقق في اليو آر إل", "browserIdVerificationURL":"التحقق في اليو آر إل",
"browseTree":"تصفح الهيكل", "browseTree":"تصفح الهيكل",
"bruteForceProtection":"Brute-force attack protection", "bruteForceProtection":"Activation",
"bruteForceAttackProtection":"Brute-force attack protection",
"bruteForceProtectionIncrementalTempo":"Incremental lock times",
"cancel":"إلغاء", "cancel":"إلغاء",
"captcha_login_enabled":"التفعيل في استمارة تسجيل الدخول", "captcha_login_enabled":"التفعيل في استمارة تسجيل الدخول",
"captcha_mail_enabled":"التفعيل في إعادة تعيين كلمة المرور بواسطة استمارة البريد", "captcha_mail_enabled":"التفعيل في إعادة تعيين كلمة المرور بواسطة استمارة البريد",
@ -142,7 +144,7 @@
"casStorageOptions":" خيارات وحدة جلسات كاس", "casStorageOptions":" خيارات وحدة جلسات كاس",
"categoryName":"اسم الفئة", "categoryName":"اسم الفئة",
"cda":"نطاقات متعددة", "cda":"نطاقات متعددة",
"certificateMailContent":"Certificate mail content", "certificateMailContent":"Mail content",
"certificateResetByMailManagement":"Certificate management", "certificateResetByMailManagement":"Certificate management",
"certificateResetByMailURL":"Reset page URL", "certificateResetByMailURL":"Reset page URL",
"certificateResetByMailCeaAttribute":"Certificate attibute name", "certificateResetByMailCeaAttribute":"Certificate attibute name",
@ -175,7 +177,7 @@
"corsAllow_Origin":"Access-Control-Allow-Origin", "corsAllow_Origin":"Access-Control-Allow-Origin",
"corsExpose_Headers":"Access-Control-Expose-Headers", "corsExpose_Headers":"Access-Control-Expose-Headers",
"corsMax_Age":"Access-Control-Max-Age", "corsMax_Age":"Access-Control-Max-Age",
"cfgLog":"استئنف", "cfgLog":"Summary",
"cfgVersion":"عملية ضبط الإصدارات", "cfgVersion":"عملية ضبط الإصدارات",
"checkXSS":"تحقق من هجمات XSS", "checkXSS":"تحقق من هجمات XSS",
"clickHereToForce":"انقر هنا لإجبار", "clickHereToForce":"انقر هنا لإجبار",
@ -326,6 +328,7 @@
"gpgParams":"GPG parameters", "gpgParams":"GPG parameters",
"grantSessionRules":"ظروف الافتتاح", "grantSessionRules":"ظروف الافتتاح",
"groups":"المجموعات", "groups":"المجموعات",
"groupsBeforeMacros":"Compute groups before macros",
"hashkey":"المفتاح", "hashkey":"المفتاح",
"headers":"هيدر إتش تي تي بي ", "headers":"هيدر إتش تي تي بي ",
"hGroups":"المجموعات (هاش ريف)", "hGroups":"المجموعات (هاش ريف)",
@ -459,7 +462,7 @@
"mailCharset":"charset", "mailCharset":"charset",
"mailConfirmBody":"تأكيد محتوى البريد", "mailConfirmBody":"تأكيد محتوى البريد",
"mailConfirmSubject":"تأكيد عنوان بريد", "mailConfirmSubject":"تأكيد عنوان بريد",
"mailContent":"Password mail content", "mailContent":"Mail content",
"mailFrom":"مرسل البريد", "mailFrom":"مرسل البريد",
"mailHeaders":"هيدر البريد", "mailHeaders":"هيدر البريد",
"mailLDAPFilter":"فلتر البريد", "mailLDAPFilter":"فلتر البريد",
@ -526,6 +529,7 @@
"notificationDeleted":"تم حذف الإشعار", "notificationDeleted":"تم حذف الإشعار",
"notificationDone":"انتهى الإشعار", "notificationDone":"انتهى الإشعار",
"notificationsDone":"انتهت الإشعارات", "notificationsDone":"انتهت الإشعارات",
"notificationsExplorer":"Explorer",
"notificationNotCreated":"لم يتم إنشاء الإشعار", "notificationNotCreated":"لم يتم إنشاء الإشعار",
"notificationNotDeleted":"لم يتم وضع علامة على الإشعار بأنه تم الاطلاع عليه", "notificationNotDeleted":"لم يتم وضع علامة على الإشعار بأنه تم الاطلاع عليه",
"notificationNotFound":"لم يتم العثور على الإشعار", "notificationNotFound":"لم يتم العثور على الإشعار",
@ -801,7 +805,7 @@
"saveReport":"احفظ التقرير", "saveReport":"احفظ التقرير",
"savingConfirmation":"حفظ التأكيد", "savingConfirmation":"حفظ التأكيد",
"scope":"نطاق", "scope":"نطاق",
"search":"Search ...", "search":"Search...",
"secondFactors":"Second factors", "secondFactors":"Second factors",
"securedCookie":"ملفات تعريف الارتباط المضمونة (سسل)", "securedCookie":"ملفات تعريف الارتباط المضمونة (سسل)",
"security":"الحماية", "security":"الحماية",
@ -1111,4 +1115,4 @@
"samlRelayStateTimeout":"تناوب حالة مهلة الجلسة ", "samlRelayStateTimeout":"تناوب حالة مهلة الجلسة ",
"samlUseQueryStringSpecific":"استخدام أسلوب query_string المعين", "samlUseQueryStringSpecific":"استخدام أسلوب query_string المعين",
"samlOverrideIDPEntityID":"Override Entity ID when acting as IDP" "samlOverrideIDPEntityID":"Override Entity ID when acting as IDP"
} }

View File

@ -104,7 +104,9 @@
"browserIdSiteName":"Site name", "browserIdSiteName":"Site name",
"browserIdVerificationURL":"Verification URL", "browserIdVerificationURL":"Verification URL",
"browseTree":"Browse tree", "browseTree":"Browse tree",
"bruteForceProtection":"Brute-force attack protection", "bruteForceProtection":"Activation",
"bruteForceAttackProtection":"Brute-force attack protection",
"bruteForceProtectionIncrementalTempo":"Incremental lock times",
"cancel":"Abbrechen", "cancel":"Abbrechen",
"captcha_login_enabled":"Activation in login form", "captcha_login_enabled":"Activation in login form",
"captcha_mail_enabled":"Activation in password reset by mail form", "captcha_mail_enabled":"Activation in password reset by mail form",
@ -142,7 +144,7 @@
"casStorageOptions":"CAS sessions module options", "casStorageOptions":"CAS sessions module options",
"categoryName":"Category name", "categoryName":"Category name",
"cda":"Mehrere Domains", "cda":"Mehrere Domains",
"certificateMailContent":"Certificate mail content", "certificateMailContent":"Mail content",
"certificateResetByMailManagement":"Certificate management", "certificateResetByMailManagement":"Certificate management",
"certificateResetByMailURL":"Reset page URL", "certificateResetByMailURL":"Reset page URL",
"certificateResetByMailCeaAttribute":"Certificate CEA attibute", "certificateResetByMailCeaAttribute":"Certificate CEA attibute",
@ -175,7 +177,7 @@
"corsAllow_Origin":"Access-Control-Allow-Origin", "corsAllow_Origin":"Access-Control-Allow-Origin",
"corsExpose_Headers":"Access-Control-Expose-Headers", "corsExpose_Headers":"Access-Control-Expose-Headers",
"corsMax_Age":"Access-Control-Max-Age", "corsMax_Age":"Access-Control-Max-Age",
"cfgLog":"Resume", "cfgLog":"Summary",
"cfgVersion":"Configuration version", "cfgVersion":"Configuration version",
"checkXSS":"Check XSS attacks", "checkXSS":"Check XSS attacks",
"clickHereToForce":"Click here to force", "clickHereToForce":"Click here to force",
@ -326,6 +328,7 @@
"gpgParams":"GPG parameters", "gpgParams":"GPG parameters",
"grantSessionRules":"Opening conditions", "grantSessionRules":"Opening conditions",
"groups":"Groups", "groups":"Groups",
"groupsBeforeMacros":"Compute groups before macros",
"hashkey":"Key", "hashkey":"Key",
"headers":"HTTP Headers", "headers":"HTTP Headers",
"hGroups":"Groups (HashRef)", "hGroups":"Groups (HashRef)",
@ -459,7 +462,7 @@
"mailCharset":"Charset", "mailCharset":"Charset",
"mailConfirmBody":"Confirmation mail content", "mailConfirmBody":"Confirmation mail content",
"mailConfirmSubject":"Confirmation mail subject", "mailConfirmSubject":"Confirmation mail subject",
"mailContent":"Password mail content", "mailContent":"Mail content",
"mailFrom":"Mail sender", "mailFrom":"Mail sender",
"mailHeaders":"Mail headers", "mailHeaders":"Mail headers",
"mailLDAPFilter":"Mail filter", "mailLDAPFilter":"Mail filter",
@ -526,6 +529,7 @@
"notificationDeleted":"Notification deleted", "notificationDeleted":"Notification deleted",
"notificationDone":"Notification done", "notificationDone":"Notification done",
"notificationsDone":"Notifications done", "notificationsDone":"Notifications done",
"notificationsExplorer":"Explorer",
"notificationNotCreated":"The notification was not created", "notificationNotCreated":"The notification was not created",
"notificationNotDeleted":"The notification was not marked as done", "notificationNotDeleted":"The notification was not marked as done",
"notificationNotFound":"The notification was not found", "notificationNotFound":"The notification was not found",
@ -801,7 +805,7 @@
"saveReport":"Save report", "saveReport":"Save report",
"savingConfirmation":"Saving confirmation", "savingConfirmation":"Saving confirmation",
"scope":"Scope", "scope":"Scope",
"search":"Search ...", "search":"Search...",
"secondFactors":"Second factors", "secondFactors":"Second factors",
"securedCookie":"Secured Cookie (SSL)", "securedCookie":"Secured Cookie (SSL)",
"security":"Security", "security":"Security",
@ -1111,4 +1115,4 @@
"samlRelayStateTimeout":"RelayState session timeout", "samlRelayStateTimeout":"RelayState session timeout",
"samlUseQueryStringSpecific":"Use specific query_string method", "samlUseQueryStringSpecific":"Use specific query_string method",
"samlOverrideIDPEntityID":"Override Entity ID when acting as IDP" "samlOverrideIDPEntityID":"Override Entity ID when acting as IDP"
} }

View File

@ -104,7 +104,9 @@
"browserIdSiteName":"Site name", "browserIdSiteName":"Site name",
"browserIdVerificationURL":"Verification URL", "browserIdVerificationURL":"Verification URL",
"browseTree":"Browse tree", "browseTree":"Browse tree",
"bruteForceProtection":"Brute-force attack protection", "bruteForceProtection":"Activation",
"bruteForceAttackProtection":"Brute-force attack protection",
"bruteForceProtectionIncrementalTempo":"Incremental lock times",
"cancel":"Cancel", "cancel":"Cancel",
"captcha_login_enabled":"Activation in login form", "captcha_login_enabled":"Activation in login form",
"captcha_mail_enabled":"Activation in password reset by mail form", "captcha_mail_enabled":"Activation in password reset by mail form",
@ -142,7 +144,7 @@
"casStorageOptions":"CAS sessions module options", "casStorageOptions":"CAS sessions module options",
"categoryName":"Category name", "categoryName":"Category name",
"cda":"Multiple domains", "cda":"Multiple domains",
"certificateMailContent":"Certificate mail content", "certificateMailContent":"Mail content",
"certificateResetByMailManagement":"Certificate management", "certificateResetByMailManagement":"Certificate management",
"certificateResetByMailURL":"Reset page URL", "certificateResetByMailURL":"Reset page URL",
"certificateResetByMailCeaAttribute":"Certificate CEA attibute", "certificateResetByMailCeaAttribute":"Certificate CEA attibute",
@ -326,6 +328,7 @@
"gpgParams":"GPG parameters", "gpgParams":"GPG parameters",
"grantSessionRules":"Opening conditions", "grantSessionRules":"Opening conditions",
"groups":"Groups", "groups":"Groups",
"groupsBeforeMacros":"Compute groups before macros",
"hashkey":"Key", "hashkey":"Key",
"headers":"HTTP Headers", "headers":"HTTP Headers",
"hGroups":"Groups (HashRef)", "hGroups":"Groups (HashRef)",
@ -459,7 +462,7 @@
"mailCharset":"Charset", "mailCharset":"Charset",
"mailConfirmBody":"Confirmation mail content", "mailConfirmBody":"Confirmation mail content",
"mailConfirmSubject":"Confirmation mail subject", "mailConfirmSubject":"Confirmation mail subject",
"mailContent":"Password mail content", "mailContent":"Mail content",
"mailFrom":"Mail sender", "mailFrom":"Mail sender",
"mailHeaders":"Mail headers", "mailHeaders":"Mail headers",
"mailLDAPFilter":"Mail filter", "mailLDAPFilter":"Mail filter",
@ -526,6 +529,7 @@
"notificationDeleted":"Notification deleted", "notificationDeleted":"Notification deleted",
"notificationDone":"Notification done", "notificationDone":"Notification done",
"notificationsDone":"Notifications done", "notificationsDone":"Notifications done",
"notificationsExplorer":"Explorer",
"notificationNotCreated":"The notification was not created", "notificationNotCreated":"The notification was not created",
"notificationNotDeleted":"The notification was not marked as done", "notificationNotDeleted":"The notification was not marked as done",
"notificationNotFound":"The notification was not found", "notificationNotFound":"The notification was not found",

View File

@ -29,11 +29,11 @@
"actives":"Actives", "actives":"Actives",
"activeTimer":"Délai d'acceptation automatique", "activeTimer":"Délai d'acceptation automatique",
"addAppCasPartner":"Ajouter une application CAS", "addAppCasPartner":"Ajouter une application CAS",
"addIDPSamlPartner":"Ajouter un IDP SAML", "addIDPSamlPartner":"Ajouter un FI SAML",
"addOidcOp":"Ajouter un fournisseur OpenID Connect", "addOidcOp":"Ajouter un fournisseur OpenID Connect",
"addOidcRp":"Ajouter un client OpenID Connect", "addOidcRp":"Ajouter un client OpenID Connect",
"addSamlAttribute":"Ajouter un attribut", "addSamlAttribute":"Ajouter un attribut",
"addSPSamlPartner":"Ajouter un SP SAML", "addSPSamlPartner":"Ajouter un FS SAML",
"addSrvCasPartner":"Ajouter un serveur CAS", "addSrvCasPartner":"Ajouter un serveur CAS",
"addU2FKey":"Ajouter une clef U2F", "addU2FKey":"Ajouter une clef U2F",
"addTOTPKey":"Ajouter une clef TOTP", "addTOTPKey":"Ajouter une clef TOTP",
@ -104,7 +104,9 @@
"browserIdSiteName":"Nom du site", "browserIdSiteName":"Nom du site",
"browserIdVerificationURL":"Adresse de vérification", "browserIdVerificationURL":"Adresse de vérification",
"browseTree":"Parcourir l'arbre", "browseTree":"Parcourir l'arbre",
"bruteForceProtection":"Protection contre les attaques par force brute", "bruteForceProtection":"Activation",
"bruteForceAttackProtection":"Protection contre les attaques par force brute",
"bruteForceProtectionIncrementalTempo":"Temps de verrouillage incrémentiels",
"cancel":"Annuler", "cancel":"Annuler",
"captcha_login_enabled":"Activation dans le formulaire d'authentification", "captcha_login_enabled":"Activation dans le formulaire d'authentification",
"captcha_mail_enabled":"Activation dans le formulaire de réinitialisation par mail", "captcha_mail_enabled":"Activation dans le formulaire de réinitialisation par mail",
@ -142,7 +144,7 @@
"casStorageOptions":"Options du module des sessions CAS", "casStorageOptions":"Options du module des sessions CAS",
"categoryName":"Nom de la catégorie", "categoryName":"Nom de la catégorie",
"cda":"Domaines multiples", "cda":"Domaines multiples",
"certificateMailContent":"Contenu du mail du certificat", "certificateMailContent":"Contenu du mail",
"certificateResetByMailManagement":"Gestion des certificats", "certificateResetByMailManagement":"Gestion des certificats",
"certificateResetByMailURL":"URL de la page de réinitialisation", "certificateResetByMailURL":"URL de la page de réinitialisation",
"certificateResetByMailCeaAttribute":"Attribut CEA du certificat", "certificateResetByMailCeaAttribute":"Attribut CEA du certificat",
@ -326,6 +328,7 @@
"gpgParams":"Paramètres GPG", "gpgParams":"Paramètres GPG",
"grantSessionRules":"Conditions d'ouverture", "grantSessionRules":"Conditions d'ouverture",
"groups":"Groupes", "groups":"Groupes",
"groupsBeforeMacros":"Calculer les groupes avant les macros",
"hashkey":"Clef", "hashkey":"Clef",
"headers":"En-têtes HTTP", "headers":"En-têtes HTTP",
"hGroups":"Groupes (HashRef)", "hGroups":"Groupes (HashRef)",
@ -459,7 +462,7 @@
"mailCharset":"Charset", "mailCharset":"Charset",
"mailConfirmBody":"Contenu du message de confirmation", "mailConfirmBody":"Contenu du message de confirmation",
"mailConfirmSubject":"Sujet du message de confirmation", "mailConfirmSubject":"Sujet du message de confirmation",
"mailContent":"Contenu du mail de mot de passe", "mailContent":"Contenu du mail",
"mailFrom":"Expéditeur du message", "mailFrom":"Expéditeur du message",
"mailHeaders":"En-têtes du mail", "mailHeaders":"En-têtes du mail",
"mailLDAPFilter":"Filtre mail", "mailLDAPFilter":"Filtre mail",
@ -526,6 +529,7 @@
"notificationDeleted":"La notification a été marquée comme lue", "notificationDeleted":"La notification a été marquée comme lue",
"notificationDone":"Notification validée", "notificationDone":"Notification validée",
"notificationsDone":"Notifications validées", "notificationsDone":"Notifications validées",
"notificationsExplorer":"Explorateur",
"notificationNotCreated":"La notification n'a pas été créée", "notificationNotCreated":"La notification n'a pas été créée",
"notificationNotDeleted":"La notification n'a pas été marquée comme lue", "notificationNotDeleted":"La notification n'a pas été marquée comme lue",
"notificationNotFound":"La notification n'a pas été trouvée", "notificationNotFound":"La notification n'a pas été trouvée",
@ -817,7 +821,7 @@
"sessionStorage":"Stockage des sessions", "sessionStorage":"Stockage des sessions",
"sessionTitle":"Contenu de la session", "sessionTitle":"Contenu de la session",
"sfaTitle":"Seconds Facteurs d'Authentification", "sfaTitle":"Seconds Facteurs d'Authentification",
"sfExtra":"Seconds Facteurs additionnels", "sfExtra":"Seconds facteurs additionnels",
"sfRequired":"Exiger 2FA", "sfRequired":"Exiger 2FA",
"sfRemovedNotification":"Afficher un message si un SF expiré a été supprimé", "sfRemovedNotification":"Afficher un message si un SF expiré a été supprimé",
"sfRemovedMsgRule":"Activation", "sfRemovedMsgRule":"Activation",

View File

@ -104,7 +104,9 @@
"browserIdSiteName":"Nome del sito", "browserIdSiteName":"Nome del sito",
"browserIdVerificationURL":"URL di verifica", "browserIdVerificationURL":"URL di verifica",
"browseTree":"Naviga albero", "browseTree":"Naviga albero",
"bruteForceProtection":"Protezione da attacchi brute-force", "bruteForceProtection":"Activation",
"bruteForceAttackProtection":"Brute-force attack protection",
"bruteForceProtectionIncrementalTempo":"Incremental lock times",
"cancel":"Cancella", "cancel":"Cancella",
"captcha_login_enabled":"Attivazione nel modulo di login", "captcha_login_enabled":"Attivazione nel modulo di login",
"captcha_mail_enabled":"Attivazione della reimpostazione della password tramite modulo di posta", "captcha_mail_enabled":"Attivazione della reimpostazione della password tramite modulo di posta",
@ -142,17 +144,17 @@
"casStorageOptions":"Opzioni del modulo sessioni CAS", "casStorageOptions":"Opzioni del modulo sessioni CAS",
"categoryName":"Nome della categoria", "categoryName":"Nome della categoria",
"cda":"Domini multipli", "cda":"Domini multipli",
"certificateMailContent":"Certificate mail content", "certificateMailContent":"Mail content",
"certificateResetByMailManagement":"Certificate management", "certificateResetByMailManagement":"Certificate management",
"certificateResetByMailURL":"Reset page URL", "certificateResetByMailURL":"Reset page URL",
"certificateResetByMailCeaAttribute":"Certificat CEA attribut", "certificateResetByMailCeaAttribute":"Certificate CEA attibute",
"certificateResetByMailCertificateAttribute":"Certificate attribute name", "certificateResetByMailCertificateAttribute":"Certificate attribute name",
"certificateResetByMailStep1Subject":"Reset mail subject", "certificateResetByMailStep1Subject":"Reset mail subject",
"certificateResetByMailStep1Body":"Reset mail content", "certificateResetByMailStep1Body":"Reset mail content",
"certificateResetByMailStep2Subject":"Confirmation mail subject", "certificateResetByMailStep2Subject":"Soggetto della mail di conferma",
"certificateResetByMailStep2Body":"Confirmation mail content", "certificateResetByMailStep2Body":"Confirmation mail content",
"certificateResetByMailValidityDelay":"Minimum duration before expiration", "certificateResetByMailValidityDelay":"Minimum duration before expiration",
"portalDisplayCertificateResetByMail":"Reset your crtificate", "portalDisplayCertificateResetByMail":"Reset your certificate",
"contentSecurityPolicy":"Politica di protezione dei contenuti", "contentSecurityPolicy":"Politica di protezione dei contenuti",
"contextSwitching":"Switch context another user", "contextSwitching":"Switch context another user",
"contextSwitchingHiddenAttributes":"Attributi nascosti", "contextSwitchingHiddenAttributes":"Attributi nascosti",
@ -175,7 +177,7 @@
"corsAllow_Origin":"Access-Control-Allow-Origin", "corsAllow_Origin":"Access-Control-Allow-Origin",
"corsExpose_Headers":"Access-Control-Expose-Headers", "corsExpose_Headers":"Access-Control-Expose-Headers",
"corsMax_Age":"Access-Control-Max-Age", "corsMax_Age":"Access-Control-Max-Age",
"cfgLog":"Riprendi", "cfgLog":"Summary",
"cfgVersion":"Versione configurazione", "cfgVersion":"Versione configurazione",
"checkXSS":"Verifica attacchi XSS", "checkXSS":"Verifica attacchi XSS",
"clickHereToForce":"Clicca qui per forzare", "clickHereToForce":"Clicca qui per forzare",
@ -326,6 +328,7 @@
"gpgParams":"Parametri GPG", "gpgParams":"Parametri GPG",
"grantSessionRules":"Condizioni di apertura", "grantSessionRules":"Condizioni di apertura",
"groups":"Gruppi", "groups":"Gruppi",
"groupsBeforeMacros":"Compute groups before macros",
"hashkey":"Chiave", "hashkey":"Chiave",
"headers":"Intestazioni HTTP", "headers":"Intestazioni HTTP",
"hGroups":"Gruppi (HashRef)", "hGroups":"Gruppi (HashRef)",
@ -459,7 +462,7 @@
"mailCharset":"Charset", "mailCharset":"Charset",
"mailConfirmBody":"Contenuto della mail di conferma", "mailConfirmBody":"Contenuto della mail di conferma",
"mailConfirmSubject":"Soggetto della mail di conferma", "mailConfirmSubject":"Soggetto della mail di conferma",
"mailContent":"Contenuto della mail di ripristino della password", "mailContent":"Mail content",
"mailFrom":"Mittente", "mailFrom":"Mittente",
"mailHeaders":"Intestazioni di posta", "mailHeaders":"Intestazioni di posta",
"mailLDAPFilter":"Filtro mail", "mailLDAPFilter":"Filtro mail",
@ -526,6 +529,7 @@
"notificationDeleted":"La notifica è stata cancellata", "notificationDeleted":"La notifica è stata cancellata",
"notificationDone":"La notifica è stata effettuata", "notificationDone":"La notifica è stata effettuata",
"notificationsDone":"Notifiche effettuate", "notificationsDone":"Notifiche effettuate",
"notificationsExplorer":"Explorer",
"notificationNotCreated":"La notifica non è stata creata", "notificationNotCreated":"La notifica non è stata creata",
"notificationNotDeleted":"La notifica non è stata contrassegnata come eseguita", "notificationNotDeleted":"La notifica non è stata contrassegnata come eseguita",
"notificationNotFound":"La notifica non é stata trovata", "notificationNotFound":"La notifica non é stata trovata",
@ -801,7 +805,7 @@
"saveReport":"Salva report", "saveReport":"Salva report",
"savingConfirmation":"Salvataggio della conferma", "savingConfirmation":"Salvataggio della conferma",
"scope":"Ambito", "scope":"Ambito",
"search":"Cerca ...", "search":"Cerca...",
"secondFactors":"Secondi fattori", "secondFactors":"Secondi fattori",
"securedCookie":"Cookie protetti (SSL)", "securedCookie":"Cookie protetti (SSL)",
"security":"Sicurezza", "security":"Sicurezza",
@ -1111,4 +1115,4 @@
"samlRelayStateTimeout":"Timeout di sessione di RelayState", "samlRelayStateTimeout":"Timeout di sessione di RelayState",
"samlUseQueryStringSpecific":"Utilizza il metodo specifico query_string", "samlUseQueryStringSpecific":"Utilizza il metodo specifico query_string",
"samlOverrideIDPEntityID":"Sostituisci l'ID entità quando agisce come IDP" "samlOverrideIDPEntityID":"Sostituisci l'ID entità quando agisce come IDP"
} }

View File

@ -104,7 +104,9 @@
"browserIdSiteName":"Site adı", "browserIdSiteName":"Site adı",
"browserIdVerificationURL":"Doğrulama URL'i", "browserIdVerificationURL":"Doğrulama URL'i",
"browseTree":"Ağaca göz at", "browseTree":"Ağaca göz at",
"bruteForceProtection":"Kaba kuvvet saldırı koruması", "bruteForceProtection":"Activation",
"bruteForceAttackProtection":"Brute-force attack protection",
"bruteForceProtectionIncrementalTempo":"Incremental lock times",
"cancel":"İptal Et", "cancel":"İptal Et",
"captcha_login_enabled":"Giriş formunda aktivasyon", "captcha_login_enabled":"Giriş formunda aktivasyon",
"captcha_mail_enabled":"E-posta formu tarafından parola sıfırlamada aktivasyon", "captcha_mail_enabled":"E-posta formu tarafından parola sıfırlamada aktivasyon",
@ -142,15 +144,15 @@
"casStorageOptions":"CAS oturumları modül seçenekleri", "casStorageOptions":"CAS oturumları modül seçenekleri",
"categoryName":"Kategori ismi", "categoryName":"Kategori ismi",
"cda":"Çoklu alan adları", "cda":"Çoklu alan adları",
"certificateMailContent":"Certificate mail content", "certificateMailContent":"Mail content",
"certificateResetByMailManagement":"Certificate management", "certificateResetByMailManagement":"Certificate management",
"certificateResetByMailURL":"Reset page URL", "certificateResetByMailURL":"Sayfa URL'sini sıfırla",
"certificateResetByMailCeaAttribute":"Certificate CEA attibute", "certificateResetByMailCeaAttribute":"Certificate CEA attibute",
"certificateResetByMailCertificateAttribute":"Certificate attribute name", "certificateResetByMailCertificateAttribute":"Certificate attribute name",
"certificateResetByMailStep1Subject":"Reset mail subject", "certificateResetByMailStep1Subject":"Reset mail subject",
"certificateResetByMailStep1Body":"Reset mail content", "certificateResetByMailStep1Body":"Reset mail content",
"certificateResetByMailStep2Subject":"Confirmation mail subject", "certificateResetByMailStep2Subject":"Doğrulama e-postası konusu",
"certificateResetByMailStep2Body":"Confirmation mail content", "certificateResetByMailStep2Body":"Doğrulama e-postası içeriği",
"certificateResetByMailValidityDelay":"Minimum duration before expiration", "certificateResetByMailValidityDelay":"Minimum duration before expiration",
"portalDisplayCertificateResetByMail":"Reset your certificate", "portalDisplayCertificateResetByMail":"Reset your certificate",
"contentSecurityPolicy":"İçerik güvenlik ilkesi", "contentSecurityPolicy":"İçerik güvenlik ilkesi",
@ -175,7 +177,7 @@
"corsAllow_Origin":"Access-Control-Allow-Origin", "corsAllow_Origin":"Access-Control-Allow-Origin",
"corsExpose_Headers":"Access-Control-Expose-Headers", "corsExpose_Headers":"Access-Control-Expose-Headers",
"corsMax_Age":"Access-Control-Max-Age", "corsMax_Age":"Access-Control-Max-Age",
"cfgLog":"Sürdür", "cfgLog":"Summary",
"cfgVersion":"Yapılandırma sürümü", "cfgVersion":"Yapılandırma sürümü",
"checkXSS":"XSS saldırılarını kontrol et", "checkXSS":"XSS saldırılarını kontrol et",
"clickHereToForce":"Zorlamak için buraya tıklayın", "clickHereToForce":"Zorlamak için buraya tıklayın",
@ -326,6 +328,7 @@
"gpgParams":"GPG parametreleri", "gpgParams":"GPG parametreleri",
"grantSessionRules":"Açılış koşulları", "grantSessionRules":"Açılış koşulları",
"groups":"Gruplar", "groups":"Gruplar",
"groupsBeforeMacros":"Compute groups before macros",
"hashkey":"Anahtar", "hashkey":"Anahtar",
"headers":"HTTP Başlıkları", "headers":"HTTP Başlıkları",
"hGroups":"Gruplar (HashRef)", "hGroups":"Gruplar (HashRef)",
@ -459,7 +462,7 @@
"mailCharset":"Karakter seti", "mailCharset":"Karakter seti",
"mailConfirmBody":"Doğrulama e-postası içeriği", "mailConfirmBody":"Doğrulama e-postası içeriği",
"mailConfirmSubject":"Doğrulama e-postası konusu", "mailConfirmSubject":"Doğrulama e-postası konusu",
"mailContent":"Parola sıfırlama e-postası içeriği", "mailContent":"Mail content",
"mailFrom":"E-posta göndericisi", "mailFrom":"E-posta göndericisi",
"mailHeaders":"E-posta başlıkları", "mailHeaders":"E-posta başlıkları",
"mailLDAPFilter":"E-posta filtresi", "mailLDAPFilter":"E-posta filtresi",
@ -526,6 +529,7 @@
"notificationDeleted":"Bildirim silindi", "notificationDeleted":"Bildirim silindi",
"notificationDone":"Bildirim tamamlandı", "notificationDone":"Bildirim tamamlandı",
"notificationsDone":"Bildirimler tamamlandı", "notificationsDone":"Bildirimler tamamlandı",
"notificationsExplorer":"Explorer",
"notificationNotCreated":"Bildirim oluşturulmadı", "notificationNotCreated":"Bildirim oluşturulmadı",
"notificationNotDeleted":"Bildirim tamamlandı olarak işaretlenmedi", "notificationNotDeleted":"Bildirim tamamlandı olarak işaretlenmedi",
"notificationNotFound":"Bildirim bulunamadı", "notificationNotFound":"Bildirim bulunamadı",
@ -672,7 +676,7 @@
"passwordDB":"Parola modülü", "passwordDB":"Parola modülü",
"passwordManagement":"Parola yönetimi", "passwordManagement":"Parola yönetimi",
"passwordPolicyMinSize":"Minimum parola uzunluğu", "passwordPolicyMinSize":"Minimum parola uzunluğu",
"passwordPolicyMinLower":"Minimum küçük harf karakter sayısı:", "passwordPolicyMinLower":"Minimum küçük harf karakter sayısı",
"passwordPolicyMinUpper":"Minimum büyük harf karakter sayısı", "passwordPolicyMinUpper":"Minimum büyük harf karakter sayısı",
"passwordPolicyMinDigit":"Minimum rakam karakter sayısı", "passwordPolicyMinDigit":"Minimum rakam karakter sayısı",
"passwordResetAllowedRetries":"Maksimum parola sıfırlama denemesi", "passwordResetAllowedRetries":"Maksimum parola sıfırlama denemesi",
@ -801,7 +805,7 @@
"saveReport":"Raporu kaydet", "saveReport":"Raporu kaydet",
"savingConfirmation":"Doğrulama kaydediliyor", "savingConfirmation":"Doğrulama kaydediliyor",
"scope":"Kapsam", "scope":"Kapsam",
"search":"Ara ...", "search":"Ara...",
"secondFactors":"İki faktörlü kimlik doğrulama", "secondFactors":"İki faktörlü kimlik doğrulama",
"securedCookie":"Güvenli Çerez (SSL)", "securedCookie":"Güvenli Çerez (SSL)",
"security":"Güvenlik", "security":"Güvenlik",
@ -1111,4 +1115,4 @@
"samlRelayStateTimeout":"RelayState oturum zaman aşımı", "samlRelayStateTimeout":"RelayState oturum zaman aşımı",
"samlUseQueryStringSpecific":"Spesifik query_string metodu kullan", "samlUseQueryStringSpecific":"Spesifik query_string metodu kullan",
"samlOverrideIDPEntityID":"IDP olarak davrandığında Varlık ID'yi geçersiz kıl" "samlOverrideIDPEntityID":"IDP olarak davrandığında Varlık ID'yi geçersiz kıl"
} }

View File

@ -104,7 +104,9 @@
"browserIdSiteName":"Tên trang web", "browserIdSiteName":"Tên trang web",
"browserIdVerificationURL":"URL xác minh", "browserIdVerificationURL":"URL xác minh",
"browseTree":"Duyệt cây", "browseTree":"Duyệt cây",
"bruteForceProtection":"Brute-force attack protection", "bruteForceProtection":"Activation",
"bruteForceAttackProtection":"Brute-force attack protection",
"bruteForceProtectionIncrementalTempo":"Incremental lock times",
"cancel":"Hủy", "cancel":"Hủy",
"captcha_login_enabled":"Kích hoạt ở dạng đăng nhập", "captcha_login_enabled":"Kích hoạt ở dạng đăng nhập",
"captcha_mail_enabled":"Kích hoạt đặt lại mật khẩu bằng biểu mẫu thư", "captcha_mail_enabled":"Kích hoạt đặt lại mật khẩu bằng biểu mẫu thư",
@ -142,7 +144,7 @@
"casStorageOptions":"Các tùy chọn mô-đun phiên CAS", "casStorageOptions":"Các tùy chọn mô-đun phiên CAS",
"categoryName":"Tên thể loại", "categoryName":"Tên thể loại",
"cda":"Nhiều tên miền", "cda":"Nhiều tên miền",
"certificateMailContent":"Certificate mail content", "certificateMailContent":"Mail content",
"certificateResetByMailManagement":"Certificate management", "certificateResetByMailManagement":"Certificate management",
"certificateResetByMailURL":"Reset page URL", "certificateResetByMailURL":"Reset page URL",
"certificateResetByMailCeaAttribute":"Certificate CEA attibute", "certificateResetByMailCeaAttribute":"Certificate CEA attibute",
@ -175,7 +177,7 @@
"corsAllow_Origin":"Access-Control-Allow-Origin", "corsAllow_Origin":"Access-Control-Allow-Origin",
"corsExpose_Headers":"Access-Control-Expose-Headers", "corsExpose_Headers":"Access-Control-Expose-Headers",
"corsMax_Age":"Access-Control-Max-Age", "corsMax_Age":"Access-Control-Max-Age",
"cfgLog":"Tiếp tục", "cfgLog":"Summary",
"cfgVersion":"Phiên bản cấu hình", "cfgVersion":"Phiên bản cấu hình",
"checkXSS":"Kiểm tra tấn công XSS", "checkXSS":"Kiểm tra tấn công XSS",
"clickHereToForce":"Nhấp vào đây để bắt buộc", "clickHereToForce":"Nhấp vào đây để bắt buộc",
@ -326,6 +328,7 @@
"gpgParams":"Tham số GPG", "gpgParams":"Tham số GPG",
"grantSessionRules":"Điều kiện mở", "grantSessionRules":"Điều kiện mở",
"groups":"Nhóm", "groups":"Nhóm",
"groupsBeforeMacros":"Compute groups before macros",
"hashkey":"Khóa", "hashkey":"Khóa",
"headers":"Tiêu đề HTTP", "headers":"Tiêu đề HTTP",
"hGroups":"Nhóm (HashRef)", "hGroups":"Nhóm (HashRef)",
@ -459,7 +462,7 @@
"mailCharset":"Charset", "mailCharset":"Charset",
"mailConfirmBody":"Xác nhận nội dung thư", "mailConfirmBody":"Xác nhận nội dung thư",
"mailConfirmSubject":"Xác nhận chủ đề thư", "mailConfirmSubject":"Xác nhận chủ đề thư",
"mailContent":"Password mail content", "mailContent":"Mail content",
"mailFrom":"Người gửi thư", "mailFrom":"Người gửi thư",
"mailHeaders":"Tiêu đề thư", "mailHeaders":"Tiêu đề thư",
"mailLDAPFilter":"Bộ lọc thư", "mailLDAPFilter":"Bộ lọc thư",
@ -526,6 +529,7 @@
"notificationDeleted":"Thông báo đã xoá", "notificationDeleted":"Thông báo đã xoá",
"notificationDone":"Thông báo được thực hiện", "notificationDone":"Thông báo được thực hiện",
"notificationsDone":"Thông báo đã hoàn tất", "notificationsDone":"Thông báo đã hoàn tất",
"notificationsExplorer":"Explorer",
"notificationNotCreated":"Thông báo không được tạo ra", "notificationNotCreated":"Thông báo không được tạo ra",
"notificationNotDeleted":"Thông báo không được đánh dấu là đã hoàn tất", "notificationNotDeleted":"Thông báo không được đánh dấu là đã hoàn tất",
"notificationNotFound":"Không tìm thấy thông báo", "notificationNotFound":"Không tìm thấy thông báo",
@ -801,7 +805,7 @@
"saveReport":"Lưu báo cáo", "saveReport":"Lưu báo cáo",
"savingConfirmation":"Lưu xác nhận", "savingConfirmation":"Lưu xác nhận",
"scope":"Phạm vi", "scope":"Phạm vi",
"search":"Search ...", "search":"Search...",
"secondFactors":"Second factors", "secondFactors":"Second factors",
"securedCookie":"Cookie bảo mật (SSL)", "securedCookie":"Cookie bảo mật (SSL)",
"security":"An ninh", "security":"An ninh",
@ -1111,4 +1115,4 @@
"samlRelayStateTimeout":"Thời gian hết hạn phiên RelayState ", "samlRelayStateTimeout":"Thời gian hết hạn phiên RelayState ",
"samlUseQueryStringSpecific":"Sử dụng phương pháp query_string cụ thể", "samlUseQueryStringSpecific":"Sử dụng phương pháp query_string cụ thể",
"samlOverrideIDPEntityID":"Override Entity ID when acting as IDP" "samlOverrideIDPEntityID":"Override Entity ID when acting as IDP"
} }

View File

@ -104,7 +104,9 @@
"browserIdSiteName":"站点名称", "browserIdSiteName":"站点名称",
"browserIdVerificationURL":"验证 URL", "browserIdVerificationURL":"验证 URL",
"browseTree":"浏览树", "browseTree":"浏览树",
"bruteForceProtection":"Brute-force attack protection", "bruteForceProtection":"Activation",
"bruteForceAttackProtection":"Brute-force attack protection",
"bruteForceProtectionIncrementalTempo":"Incremental lock times",
"cancel":"取消", "cancel":"取消",
"captcha_login_enabled":" 登录激活", "captcha_login_enabled":" 登录激活",
"captcha_mail_enabled":"通过邮件进行密码重置 激活", "captcha_mail_enabled":"通过邮件进行密码重置 激活",
@ -142,7 +144,7 @@
"casStorageOptions":"CAS 会话模块选项", "casStorageOptions":"CAS 会话模块选项",
"categoryName":"分类名称", "categoryName":"分类名称",
"cda":"Multiple domains", "cda":"Multiple domains",
"certificateMailContent":"Certificate mail content", "certificateMailContent":"Mail content",
"certificateResetByMailManagement":"Certificate management", "certificateResetByMailManagement":"Certificate management",
"certificateResetByMailURL":"Reset page URL", "certificateResetByMailURL":"Reset page URL",
"certificateResetByMailCeaAttribute":"Certificate CEA attibute", "certificateResetByMailCeaAttribute":"Certificate CEA attibute",
@ -175,7 +177,7 @@
"corsAllow_Origin":"Access-Control-Allow-Origin", "corsAllow_Origin":"Access-Control-Allow-Origin",
"corsExpose_Headers":"Access-Control-Expose-Headers", "corsExpose_Headers":"Access-Control-Expose-Headers",
"corsMax_Age":"Access-Control-Max-Age", "corsMax_Age":"Access-Control-Max-Age",
"cfgLog":"档案", "cfgLog":"Summary",
"cfgVersion":"配置信息", "cfgVersion":"配置信息",
"checkXSS":"Check XSS attacks", "checkXSS":"Check XSS attacks",
"clickHereToForce":"Click here to force", "clickHereToForce":"Click here to force",
@ -326,6 +328,7 @@
"gpgParams":"GPG parameters", "gpgParams":"GPG parameters",
"grantSessionRules":"Opening conditions", "grantSessionRules":"Opening conditions",
"groups":"Groups", "groups":"Groups",
"groupsBeforeMacros":"Compute groups before macros",
"hashkey":"Key", "hashkey":"Key",
"headers":"HTTP Headers", "headers":"HTTP Headers",
"hGroups":"Groups (HashRef)", "hGroups":"Groups (HashRef)",
@ -459,7 +462,7 @@
"mailCharset":"Charset", "mailCharset":"Charset",
"mailConfirmBody":"Confirmation mail content", "mailConfirmBody":"Confirmation mail content",
"mailConfirmSubject":"Confirmation mail subject", "mailConfirmSubject":"Confirmation mail subject",
"mailContent":"Password mail content", "mailContent":"Mail content",
"mailFrom":"邮件发送者", "mailFrom":"邮件发送者",
"mailHeaders":"邮件头", "mailHeaders":"邮件头",
"mailLDAPFilter":"邮件过滤器", "mailLDAPFilter":"邮件过滤器",
@ -526,6 +529,7 @@
"notificationDeleted":"Notification deleted", "notificationDeleted":"Notification deleted",
"notificationDone":"Notification done", "notificationDone":"Notification done",
"notificationsDone":"Notifications done", "notificationsDone":"Notifications done",
"notificationsExplorer":"Explorer",
"notificationNotCreated":"The notification was not created", "notificationNotCreated":"The notification was not created",
"notificationNotDeleted":"The notification was not marked as done", "notificationNotDeleted":"The notification was not marked as done",
"notificationNotFound":"The notification was not found", "notificationNotFound":"The notification was not found",
@ -801,7 +805,7 @@
"saveReport":"Save report", "saveReport":"Save report",
"savingConfirmation":"Saving confirmation", "savingConfirmation":"Saving confirmation",
"scope":"Scope", "scope":"Scope",
"search":"Search ...", "search":"Search...",
"secondFactors":"Second factors", "secondFactors":"Second factors",
"securedCookie":"Secured Cookie (SSL)", "securedCookie":"Secured Cookie (SSL)",
"security":"Security", "security":"Security",
@ -1111,4 +1115,4 @@
"samlRelayStateTimeout":"RelayState session timeout", "samlRelayStateTimeout":"RelayState session timeout",
"samlUseQueryStringSpecific":"Use specific query_string method", "samlUseQueryStringSpecific":"Use specific query_string method",
"samlOverrideIDPEntityID":"Override Entity ID when acting as IDP" "samlOverrideIDPEntityID":"Override Entity ID when acting as IDP"
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -12,9 +12,8 @@
<div class="navbar navbar-default"> <div class="navbar navbar-default">
<div class="navbar-collapse"> <div class="navbar-collapse">
<ul class="nav navbar-nav" role="grid"> <ul class="nav navbar-nav" role="grid">
<li><a id="a-persistent" role="row"><i class="glyphicon glyphicon-filter"></i> {{translate('2faSessions')}} &nbsp;&nbsp;</a></li> <form class="navbar-form" name="filterForm">
<form name="filterForm"> <div class="form-check ">
<div class="form-check ">&nbsp;&nbsp;&nbsp;
<input type="checkbox" ng-model="U2FCheck" class="form-check-input" ng-true-value="2" ng-false-value="1" ng-change="search2FA()"/> <input type="checkbox" ng-model="U2FCheck" class="form-check-input" ng-true-value="2" ng-false-value="1" ng-change="search2FA()"/>
<label class="form-check-label" for="U2FCheck">U2F</label> <label class="form-check-label" for="U2FCheck">U2F</label>
&nbsp;&nbsp;&&nbsp;&nbsp; &nbsp;&nbsp;&&nbsp;&nbsp;
@ -26,8 +25,8 @@
</div> </div>
</form> </form>
</ul> </ul>
<div class="col-lg-6 col-md-6 col-sm-8 col-xs-14" > <div class="col-lg-6 col-md-6 col-sm-8 col-xs-14" >
<form class="navbar-form" role="search"> <form class="navbar-form" role="search">
<div class="input-group add-on"> <div class="input-group add-on">
<input class="form-control" placeholder="{{translate('search')}}" type="text" ng-model="searchString" ng-init="" ng-keyup="search2FA()"/> <input class="form-control" placeholder="{{translate('search')}}" type="text" ng-model="searchString" ng-init="" ng-keyup="search2FA()"/>
<div class="input-group-btn"> <div class="input-group-btn">

View File

@ -15,9 +15,9 @@
<div class="navbar navbar-default"> <div class="navbar navbar-default">
<div class="navbar-collapse"> <div class="navbar-collapse">
<ul class="nav navbar-nav" role="grid"> <ul class="nav navbar-nav" role="grid">
<li><a id="a-actives" href="#" role="row"><i class="glyphicon glyphicon-eye-open"></i> {{translate('actives')}}</a></li> <li><a id="a-actives" href="#" role="row" ng-style="activesStyle"><i class="glyphicon glyphicon-eye-open"></i> {{translate('actives')}}</a></li>
<li><a id="a-done" href="#!/done" role="row"><i class="glyphicon glyphicon-check"></i> {{translate('dones')}}</a></li> <li><a id="a-done" href="#!/done" role="row" ng-style="doneStyle"><i class="glyphicon glyphicon-check"></i> {{translate('dones')}}</a></li>
<li><a id="a-new" href="#!/new" role="row"><i class="glyphicon glyphicon-plus-sign"></i> {{translate('create')}}</a></li> <li><a id="a-new" href="#!/new" role="row" ng-style="newStyle"><i class="glyphicon glyphicon-plus-sign"></i> {{translate('create')}}</a></li>
</ul> </ul>
</div> </div>
</div> </div>
@ -69,6 +69,10 @@
<th>{{translate('reference')}}</th> <th>{{translate('reference')}}</th>
<td>{{currentNotification.reference}}</td> <td>{{currentNotification.reference}}</td>
</tr> </tr>
<tr ng-if="currentNotification.date">
<th>{{translate('date')}}</th>
<td>{{currentNotification.date}}</td>
</tr>
<tr ng-if="currentNotification.condition"> <tr ng-if="currentNotification.condition">
<th>{{translate('condition')}}</th> <th>{{translate('condition')}}</th>
<td>{{currentNotification.condition}}</td> <td>{{currentNotification.condition}}</td>

View File

@ -16,7 +16,7 @@
<div class="navbar-collapse"> <div class="navbar-collapse">
<ul class="nav navbar-nav" role="grid"> <ul class="nav navbar-nav" role="grid">
<li uib-dropdown> <li uib-dropdown>
<a id="navsso" name="menu" uib-dropdown-toggle data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><i class="glyphicon glyphicon-user"></i> {{translate('ssoSessions')}} <span class="caret"></span></a> <a id="navsso" name="menu" uib-dropdown-toggle data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" ng-style="navssoStyle"><i class="glyphicon glyphicon-user"></i> {{translate('ssoSessions')}} <span class="caret"></span></a>
<ul uib-dropdown-menu aria-labelled-by="navsso"> <ul uib-dropdown-menu aria-labelled-by="navsso">
<li><a id="a-users" href="#" role="row"><i class="glyphicon glyphicon-user"></i> {{translate('users')}}</a></li> <li><a id="a-users" href="#" role="row"><i class="glyphicon glyphicon-user"></i> {{translate('users')}}</a></li>
<li><a id="a-ip" href="#!/ipAddr" role="row"><i class="glyphicon glyphicon-sort-by-order"></i> {{translate('ipAddresses')}}</a></li> <li><a id="a-ip" href="#!/ipAddr" role="row"><i class="glyphicon glyphicon-sort-by-order"></i> {{translate('ipAddresses')}}</a></li>
@ -25,8 +25,8 @@
<li><a id="a-updatetime" href="#!/_updateTime" role="row"><i class="glyphicon glyphicon-hourglass"></i> {{translate('_updateTime')}}</a></li> <li><a id="a-updatetime" href="#!/_updateTime" role="row"><i class="glyphicon glyphicon-hourglass"></i> {{translate('_updateTime')}}</a></li>
</ul> </ul>
</li> </li>
<li><a id="a-persistent" href="#!/persistent" role="row"><i class="glyphicon glyphicon-lock"></i> {{translate('persistentSessions')}}</a></li> <li><a id="a-persistent" href="#!/persistent" role="row" ng-style="persistentStyle"><i class="glyphicon glyphicon-lock"></i> {{translate('persistentSessions')}}</a></li>
<li><a id="a-offline" href="#!/offline" role="row"><i class="glyphicon glyphicon-time"></i> {{translate('offlineSessions')}}</a></li> <li><a id="a-offline" href="#!/offline" role="row" ng-style="offlineStyle"><i class="glyphicon glyphicon-time"></i> {{translate('offlineSessions')}}</a></li>
</ul> </ul>
</div> </div>
</div> </div>

View File

@ -41,7 +41,8 @@ sub newSession {
_session_kind => $kind, _session_kind => $kind,
_2fDevices => to_json($sfaDevices), _2fDevices => to_json($sfaDevices),
} }
), "New $kind session for $uid" ),
"New $kind session for $uid"
); );
count(1); count(1);
} }
@ -96,7 +97,7 @@ sub checkGet {
my ( $uid, $id ) = splice @_; my ( $uid, $id ) = splice @_;
my ( $test, $res, $ret ); my ( $test, $res, $ret );
$test = "$uid should have one 2F with id \"$id\""; $test = "$uid should have one 2F with id \"$id\"";
$res = get( $test, $uid, undef, $id ); $res = get( $test, $uid, undef, $id );
check200( $test, $res ); check200( $test, $res );
#diag Dumper($res); #diag Dumper($res);
@ -110,7 +111,7 @@ sub checkGet404 {
my ( $uid, $id ) = splice @_; my ( $uid, $id ) = splice @_;
my ( $test, $res, $ret ); my ( $test, $res, $ret );
$test = "$uid should not have any 2F with id \"$id\""; $test = "$uid should not have any 2F with id \"$id\"";
$res = get( $test, $uid, undef, $id ); $res = get( $test, $uid, undef, $id );
check404( $test, $res ); check404( $test, $res );
} }
@ -138,7 +139,7 @@ sub checkGetBadType {
my ( $uid, $type ) = splice @_; my ( $uid, $type ) = splice @_;
my ( $test, $res ); my ( $test, $res );
$test = "Get for uid $uid and type \"$type\" should get rejected."; $test = "Get for uid $uid and type \"$type\" should get rejected.";
$res = get( $test, $uid, $type ); $res = get( $test, $uid, $type );
check405( $test, $res ); check405( $test, $res );
} }
@ -176,7 +177,7 @@ sub checkDelete {
my ( $uid, $id ) = splice @_; my ( $uid, $id ) = splice @_;
my ( $test, $res ); my ( $test, $res );
$test = "$uid should have a 2F with id \"$id\" to be deleted."; $test = "$uid should have a 2F with id \"$id\" to be deleted.";
$res = del( $test, $uid, undef, $id ); $res = del( $test, $uid, undef, $id );
check200( $test, $res ); check200( $test, $res );
} }
@ -184,7 +185,7 @@ sub checkDelete404 {
my ( $uid, $id ) = splice @_; my ( $uid, $id ) = splice @_;
my ( $test, $res ); my ( $test, $res );
$test = "$uid should not have a 2F with id \"$id\" to be deleted."; $test = "$uid should not have a 2F with id \"$id\" to be deleted.";
$res = del( $test, $uid, undef, $id ); $res = del( $test, $uid, undef, $id );
check404( $test, $res ); check404( $test, $res );
} }
@ -209,7 +210,7 @@ sub checkDeleteBadType {
my ( $uid, $type ) = splice @_; my ( $uid, $type ) = splice @_;
my ( $test, $res ); my ( $test, $res );
$test = "Delete for uid $uid and type \"$type\" should get rejected."; $test = "Delete for uid $uid and type \"$type\" should get rejected.";
$res = del( $test, $uid, $type ); $res = del( $test, $uid, $type );
check405( $test, $res ); check405( $test, $res );
} }
@ -218,7 +219,7 @@ my $ret;
## Sessions creation ## Sessions creation
# msmith # msmith
newSession( 'msmith', '127.10.0.1', 'SSO', $sfaDevices ); newSession( 'msmith', '127.10.0.1', 'SSO', $sfaDevices );
newSession( 'msmith', '127.10.0.1', 'Persistent', $sfaDevices ); newSession( 'msmith', '127.10.0.1', 'Persistent', $sfaDevices );
# dwho # dwho
@ -242,7 +243,7 @@ $sfaDevices = [ {
"epoch" => time "epoch" => time
} }
]; ];
newSession( 'dwho', '127.10.0.1', 'SSO', $sfaDevices ); newSession( 'dwho', '127.10.0.1', 'SSO', $sfaDevices );
newSession( 'dwho', '127.10.0.1', 'Persistent', $sfaDevices ); newSession( 'dwho', '127.10.0.1', 'Persistent', $sfaDevices );
# rtyler # rtyler
@ -266,7 +267,7 @@ $sfaDevices = [ {
"epoch" => time "epoch" => time
} }
]; ];
newSession( 'rtyler', '127.10.0.1', 'SSO', $sfaDevices ); newSession( 'rtyler', '127.10.0.1', 'SSO', $sfaDevices );
newSession( 'rtyler', '127.10.0.1', 'Persistent', $sfaDevices ); newSession( 'rtyler', '127.10.0.1', 'Persistent', $sfaDevices );
# davros # davros
@ -284,7 +285,7 @@ $sfaDevices = [ {
"epoch" => time "epoch" => time
} }
]; ];
newSession( 'davros', '127.10.0.1', 'SSO', $sfaDevices ); newSession( 'davros', '127.10.0.1', 'SSO', $sfaDevices );
newSession( 'davros', '127.10.0.1', 'Persistent', $sfaDevices ); newSession( 'davros', '127.10.0.1', 'Persistent', $sfaDevices );
# tof # tof
@ -296,7 +297,7 @@ $sfaDevices = [ {
"epoch" => time "epoch" => time
} }
]; ];
newSession( 'tof', '127.10.0.1', 'SSO', $sfaDevices ); newSession( 'tof', '127.10.0.1', 'SSO', $sfaDevices );
newSession( 'tof', '127.10.0.1', 'Persistent', $sfaDevices ); newSession( 'tof', '127.10.0.1', 'Persistent', $sfaDevices );
# dwho # dwho

View File

@ -90,7 +90,7 @@ sub checkGet {
my $res = get( $test, $type, $confKey ); my $res = get( $test, $type, $confKey );
check200( $test, $res ); check200( $test, $res );
my @path = split '/', $attrPath; my @path = split '/', $attrPath;
my $key = from_json( $res->[2]->[0] ); my $key = from_json( $res->[2]->[0] );
for (@path) { for (@path) {
$key = $key->{$_}; $key = $key->{$_};
} }

View File

@ -58,7 +58,7 @@ while (<F>) {
close F; close F;
ok( $hstruct = from_json($hstruct), 'struct.json is JSON' ); ok( $hstruct = from_json($hstruct), 'struct.json is JSON' );
ok( ref $hstruct eq 'ARRAY', 'struct.json is an array' ) ok( ref $hstruct eq 'ARRAY', 'struct.json is an array' )
or print STDERR "Expected: ARRAY, got: " . ( ref $hstruct ) . "\n"; or print STDERR "Expected: ARRAY, got: " . ( ref $hstruct ) . "\n";
count(2); count(2);

View File

@ -25,7 +25,7 @@ ok(
), ),
"Request succeed" "Request succeed"
); );
ok( $res->[0] == 200, "Result code is 200" ); ok( $res->[0] == 200, "Result code is 200" );
ok( $key = from_json( $res->[2]->[0] ), 'Response is JSON' ); ok( $key = from_json( $res->[2]->[0] ), 'Response is JSON' );
count(3); count(3);

View File

@ -20,7 +20,7 @@ mkdir 't/sessions';
my ( $res, $resBody ); my ( $res, $resBody );
ok( $res = &client->_post( '/confs/', 'cfgNum=1', &body, 'application/json' ), ok( $res = &client->_post( '/confs/', 'cfgNum=1', &body, 'application/json' ),
"Request succeed" ); "Request succeed" );
ok( $res->[0] == 200, "Result code is 200" ); ok( $res->[0] == 200, "Result code is 200" );
ok( $resBody = from_json( $res->[2]->[0] ), "Result body contains JSON text" ); ok( $resBody = from_json( $res->[2]->[0] ), "Result body contains JSON text" );
ok( $resBody->{result} == 0, "JSON response contains \"result:0\"" ) ok( $resBody->{result} == 0, "JSON response contains \"result:0\"" )
@ -248,7 +248,7 @@ sub changes {
}, },
{ {
'confCompacted' => '1', 'confCompacted' => '1',
'removedKeys' => 'some; keys' 'removedKeys' => 'some; keys'
} }
]; ];
} }

View File

@ -25,7 +25,8 @@ ok( $resBody = from_json( $res->[2]->[0] ), "Result body contains JSON text" );
ok( $resBody->{result} == 1, "JSON response contains \"result:1\"" ) ok( $resBody->{result} == 1, "JSON response contains \"result:1\"" )
or print STDERR Dumper($resBody); or print STDERR Dumper($resBody);
ok( ok(
@{ $resBody->{details}->{__warnings__} } == 2, $resBody->{details}->{__warnings__}
and @{ $resBody->{details}->{__warnings__} } == 2,
'JSON response contains 2 warnings' 'JSON response contains 2 warnings'
) or print STDERR Dumper($resBody); ) or print STDERR Dumper($resBody);
@ -38,18 +39,18 @@ foreach my $i ( 0 .. 1 ) {
} }
ok( ok(
@{ $resBody->{details}->{__changes__} } == 24, $resBody->{details}->{__changes__}
and @{ $resBody->{details}->{__changes__} } == 24,
'JSON response contains 24 changes' 'JSON response contains 24 changes'
) or print STDERR Dumper($resBody); ) or print STDERR Dumper($resBody);
ok( ok( $resBody->{details}->{__changes__}->[23]->{confCompacted} == 1,
$resBody->{details}->{__changes__}->[23]->{confCompacted} == 1, 'Conf. has been compacted' )
'Conf. has been compacted' or print STDERR Dumper($resBody);
) or print STDERR Dumper($resBody);
my @removedKeys = split /; /, $resBody->{details}->{__changes__}->[23]->{removedKeys}; my @removedKeys = split /; /,
ok( $resBody->{details}->{__changes__}->[23]->{removedKeys};
@removedKeys == 60, 'All removed keys found' ok( @removedKeys == 60, 'All removed keys found' )
) or print STDERR Dumper(\@removedKeys); or print STDERR Dumper( \@removedKeys );
#print STDERR Dumper($resBody); #print STDERR Dumper($resBody);
ok( -f $confFiles->[1], 'File is created' ); ok( -f $confFiles->[1], 'File is created' );
@ -105,8 +106,7 @@ ok( @c2 == 15, '15 keys changed or created in conf 2' )
count(5); count(5);
ok( $res = &client->jsonResponse('/confs/latest'), ok( $res = &client->jsonResponse('/confs/latest'), 'Get last config metadata' );
'Get last config metadata' );
ok( $res->{prev} == 1, ' Get previous configuration' ); ok( $res->{prev} == 1, ' Get previous configuration' );
count(2); count(2);
@ -119,8 +119,7 @@ done_testing( count() );
`rm -rf t/sessions`; `rm -rf t/sessions`;
sub changes { sub changes {
return [ return [ {
{
'key' => 'portal', 'key' => 'portal',
'new' => 'http://auth2.example.com/', 'new' => 'http://auth2.example.com/',
'old' => 'http://auth.example.com/' 'old' => 'http://auth.example.com/'

View File

@ -16,7 +16,7 @@ unlink 't/conf/lmConf-2.json';
my ( $res, $resBody ); my ( $res, $resBody );
ok( $res = &client->_post( '/confs/', 'cfgNum=1', &body, 'application/json' ), ok( $res = &client->_post( '/confs/', 'cfgNum=1', &body, 'application/json' ),
"Request succeed" ); "Request succeed" );
ok( $res->[0] == 200, "Result code is 200" ); ok( $res->[0] == 200, "Result code is 200" );
ok( $resBody = from_json( $res->[2]->[0] ), "Result body contains JSON text" ); ok( $resBody = from_json( $res->[2]->[0] ), "Result body contains JSON text" );
ok( $resBody->{result} == 0, "JSON response contains \"result:0\"" ) ok( $resBody->{result} == 0, "JSON response contains \"result:0\"" )
or print STDERR Dumper($res); or print STDERR Dumper($res);

View File

@ -17,7 +17,7 @@ mkdir 't/sessions';
my ( $res, $resBody ); my ( $res, $resBody );
ok( $res = &client->_post( '/confs/', 'cfgNum=1', &body, 'application/json' ), ok( $res = &client->_post( '/confs/', 'cfgNum=1', &body, 'application/json' ),
"Request succeed" ); "Request succeed" );
ok( $res->[0] == 200, "Result code is 200" ); ok( $res->[0] == 200, "Result code is 200" );
ok( $resBody = from_json( $res->[2]->[0] ), "Result body contains JSON text" ); ok( $resBody = from_json( $res->[2]->[0] ), "Result body contains JSON text" );
ok( $resBody->{result} == 1, "JSON response contains \"result:1\"" ) ok( $resBody->{result} == 1, "JSON response contains \"result:1\"" )
or print STDERR Dumper($res); or print STDERR Dumper($res);

View File

@ -1,112 +1,114 @@
use Test::More; use Test::More;
use Test::Output;
use JSON; use JSON;
use strict; use strict;
require 't/test-lib.pm'; require 't/test-lib.pm';
my $tests = 14; my $tests = 17;
use_ok('Lemonldap::NG::Common::Cli'); use_ok('Lemonldap::NG::Common::Cli');
use_ok('Lemonldap::NG::Manager::Cli'); use_ok('Lemonldap::NG::Manager::Cli');
&cleanConfFiles; &cleanConfFiles;
SKIP: { sub llclient {
eval 'use Test::Output;'; return Lemonldap::NG::Manager::Cli->new( iniFile => 't/lemonldap-ng.ini' );
if ($@) {
skip 'Test::Output is missing, skipping', $tests - 2;
}
my $client =
Lemonldap::NG::Manager::Cli->new( iniFile => 't/lemonldap-ng.ini' );
my $commonClient =
Lemonldap::NG::Common::Cli->new( iniFile => 't/lemonldap-ng.ini' );
my @cmd;
my $res;
# Test 'set' command
@cmd = qw(-yes 1 set notification 1);
Capture::Tiny::capture_stdout( sub { $client->run(@cmd) } );
# Test 'get' command
@cmd = qw(get notification);
$res = Capture::Tiny::capture_stdout( sub { $client->run(@cmd) } );
ok( $res =~ /^notification\s+=\s+1$/, '"get notification" OK' )
or diag " $res";
# Test 'addKey' command
@cmd = qw(-yes 1 addKey locationRules/test1.example.com ^/reject deny);
Test::Output::combined_like(
sub { $client->run(@cmd) },
qr#'\^/reject' => 'deny'#s,
'"addKey" OK'
);
# Test 'delKey' command
@cmd = qw(-yes 1 delKey locationRules/test1.example.com ^/reject);
Test::Output::combined_unlike(
sub { $client->run(@cmd) },
qr#'\^/reject' => 'deny'#s,
'"delKey" OK'
);
# Test 'get' command with key/subkey
@cmd = qw(get locationRules/test1.example.com);
$res = Capture::Tiny::capture_stdout( sub { $client->run(@cmd) } );
ok( $res =~ m#(?:/logout|default)#, '"get key/subkey" OK' )
or diag "$res";
# Test 'set' command with key/subkey
@cmd = qw(-yes 1 set locationRules/test1.example.com/default deny);
Capture::Tiny::capture_stdout( sub { $client->run(@cmd) } );
# Test 'save' command
@cmd = ('save');
$res = Capture::Tiny::capture_stdout( sub { $client->run(@cmd) } );
ok( $res =~ /^\s*(\{.*\})\s*$/s, '"save" result looks like JSON' );
eval { JSON::from_json($res) };
ok( not($@), ' result is JSON' ) or diag "error: $@";
# Test 'restore' command
close STDIN;
open STDIN, '<', \$res;
@cmd = ( 'restore', '-' );
Test::Output::combined_like( sub { $client->run(@cmd) },
qr/"cfgNum"\s*:\s*"3"/s, 'New config: 3' );
# Test 'set' command with force
@cmd = qw(-yes 1 -force 1 -cfgNum 2 set useSafeJail 0);
Test::Output::combined_like(
sub { $client->run(@cmd) },
qr#cfgNum forced with 2#s,
'"Force cfgNum" OK'
);
# Test 'update-cache' command with force
@cmd = qw(update-cache);
Test::Output::combined_like(
sub { $commonClient->run(@cmd) },
qr#Cache updated to configuration 3#s,
'"update-cache" OK'
);
# Test 'info' command with force
@cmd = qw(info);
Test::Output::combined_like(
$res = sub { $commonClient->run(@cmd) },
qr#\bAuthor IP\b#s,
'"Author IP" OK'
);
Test::Output::combined_like(
$res = sub { $commonClient->run(@cmd) },
qr#\bLog\b#s,
'"Log" OK'
);
Test::Output::combined_like(
$res = sub { $commonClient->run(@cmd) },
qr#\bVersion\b#s,
'"Version" OK'
);
} }
sub llcommonClient {
return Lemonldap::NG::Common::Cli->new( iniFile => 't/lemonldap-ng.ini' );
}
my @cmd;
my $res;
# Test 'set' command
@cmd = qw(-yes 1 set notification 1);
combined_like(
sub { llclient->run(@cmd) },
qr/Saved under/,
'"addKey" OK'
);
# Test 'get' command
@cmd = qw(get notification);
$res = Test::Output::stdout_from( sub { llclient->run(@cmd) } );
ok( $res =~ /^notification\s+=\s+1$/, '"get notification" OK' )
or diag " $res";
# Test 'addKey' command
@cmd = qw(-yes 1 addKey locationRules/test1.example.com ^/reject deny);
combined_like(
sub { llclient->run(@cmd) },
qr/Saved under/,
'"addKey" OK'
);
# Test 'delKey' command
@cmd = qw(-yes 1 delKey locationRules/test1.example.com ^/reject);
combined_unlike(
sub { llclient->run(@cmd) },
qr#'\^/reject' => 'deny'#s,
'"delKey" OK'
);
# Test 'get' command with key/subkey
@cmd = qw(get locationRules/test1.example.com/default);
$res = Test::Output::stdout_from( sub { llclient->run(@cmd) } );
ok( $res =~ m#accept#, '"get key/subkey" OK' )
or diag "$res";
# Test 'set' command with key/subkey
@cmd = qw(-yes 1 set locationRules/test1.example.com/default deny);
combined_like(
sub { llclient->run(@cmd) },
qr/Saved under/,
'"addKey" OK'
);
# Test 'save' command
@cmd = qw(-cfgNum 1 save);
$res = Test::Output::stdout_from( sub { llclient->run(@cmd) } );
ok( $res =~ /^\s*(\{.*\})\s*$/s, '"save" result looks like JSON' );
my $j;
eval { $j = JSON::from_json($res) };
is( $j->{cfgNum}, 1, "correct version number" );
ok( not($@), ' result is JSON' ) or diag "error: $@";
# Test 'restore' command
my $tmpFile = File::Temp->new();
print $tmpFile $res;
@cmd = ( 'restore', $tmpFile->filename );
combined_like( sub { llclient->run(@cmd) },
qr/"cfgNum"\s*:\s*\d*/s, 'New config' );
# Test 'set' command with force
@cmd = qw(-yes 1 -force 1 -cfgNum 2 set useSafeJail 0);
combined_like(
sub { llclient->run(@cmd) },
qr#cfgNum forced with 2#s,
'"Force cfgNum" OK'
);
# Test 'info' command with force
@cmd = qw(info);
combined_like(
sub { llcommonClient->run(@cmd) },
qr#\bAuthor IP\b#s,
'"Author IP" OK'
);
combined_like( sub { llcommonClient->run(@cmd) },
qr#\bLog\b#s, '"Log" OK' );
combined_like( sub { llcommonClient->run(@cmd) },
qr#\bVersion\b#s, '"Version" OK' );
# Test 'rollback' command
@cmd = qw(rollback);
combined_like(
sub { llclient->run(@cmd) },
qr/Configuration \d+ has been rolled back/,
'"Author IP" OK'
);
count($tests); count($tests);
done_testing( count() ); done_testing( count() );
&cleanConfFiles; &cleanConfFiles;

View File

@ -17,7 +17,7 @@ mkdir 't/sessions';
my ( $res, $resBody ); my ( $res, $resBody );
ok( $res = &client->_post( '/confs/', 'cfgNum=1', &body, 'application/json' ), ok( $res = &client->_post( '/confs/', 'cfgNum=1', &body, 'application/json' ),
"Request succeed" ); "Request succeed" );
ok( $res->[0] == 200, "Result code is 200" ); ok( $res->[0] == 200, "Result code is 200" );
ok( $resBody = from_json( $res->[2]->[0] ), "Result body contains JSON text" ); ok( $resBody = from_json( $res->[2]->[0] ), "Result body contains JSON text" );
ok( $resBody->{result} == 1, "JSON response contains \"result:1\"" ) ok( $resBody->{result} == 1, "JSON response contains \"result:1\"" )
or print STDERR Dumper($res); or print STDERR Dumper($res);

View File

@ -148,7 +148,7 @@ count(5);
foreach (@ids) { foreach (@ids) {
my $res; my $res;
ok( $res = &client->_del("/sessions/global/$_"), "Delete $_" ); ok( $res = &client->_del("/sessions/global/$_"), "Delete $_" );
ok( $res->[0] == 200, 'Result code is 200' ); ok( $res->[0] == 200, 'Result code is 200' );
ok( from_json( $res->[2]->[0] )->{result} == 1, ok( from_json( $res->[2]->[0] )->{result} == 1,
'Body is JSON and result==1' ); 'Body is JSON and result==1' );
count(3); count(3);

View File

@ -44,7 +44,8 @@ SKIP: {
$client->jsonPostResponse( 'notifications/actives', '', $client->jsonPostResponse( 'notifications/actives', '',
IO::String->new($notif), IO::String->new($notif),
'application/json', length($notif) ); 'application/json', length($notif) );
ok( $res->{error} =~ /^Notification not created: Bad date/, 'Notification not inserted' ); ok( $res->{error} =~ /^Notification not created: Bad date/,
'Notification not inserted' );
$notif = $notif =
'{"date":"2099-13-30","uid":"dwho","reference":"Test","xml":"{\"title\":\"Test\"}"}'; '{"date":"2099-13-30","uid":"dwho","reference":"Test","xml":"{\"title\":\"Test\"}"}';
@ -52,7 +53,8 @@ SKIP: {
$client->jsonPostResponse( 'notifications/actives', '', $client->jsonPostResponse( 'notifications/actives', '',
IO::String->new($notif), IO::String->new($notif),
'application/json', length($notif) ); 'application/json', length($notif) );
ok( $res->{error} =~ /^Notification not created: Bad date/, 'Notification not inserted' ); ok( $res->{error} =~ /^Notification not created: Bad date/,
'Notification not inserted' );
$notif = $notif =
'{"date":"2099-05_12","uid":"dwho","reference":"Test","xml":"{\"title\":\"Test\"}"}'; '{"date":"2099-05_12","uid":"dwho","reference":"Test","xml":"{\"title\":\"Test\"}"}';
@ -98,7 +100,7 @@ SKIP: {
$res = $res =
$client->jsonResponse( 'notifications/done', 'groupBy=substr(uid,1)' ); $client->jsonResponse( 'notifications/done', 'groupBy=substr(uid,1)' );
ok( $res->{result} == 1, 'Result = 1' ); ok( $res->{result} == 1, 'Result = 1' );
ok( $res->{count} == 0, 'Count = 0' ) or diag Dumper($res); ok( $res->{count} == 0, 'Count = 0' ) or diag Dumper($res);
#print STDERR Dumper($res); #print STDERR Dumper($res);
} }

View File

@ -16,21 +16,23 @@ my $res =
&client->jsonPostResponse( 'notifications/actives', '', &client->jsonPostResponse( 'notifications/actives', '',
IO::String->new($notif), IO::String->new($notif),
'application/json', length($notif) ); 'application/json', length($notif) );
ok( $res->{error} =~ /^Notification not created: Bad date/, 'Notification not inserted' ); ok( $res->{error} =~ /^Notification not created: Bad date/,
'Notification not inserted' );
count(1); count(1);
$notif = $notif =
'{"date":"2099-13-30","uid":"dwho","reference":"Test","xml":"{\"title\":\"Test\"}"}'; '{"date":"2099-13-30","uid":"dwho","reference":"Test","xml":"{\"title\":\"Test\"}"}';
my $res = $res =
&client->jsonPostResponse( 'notifications/actives', '', &client->jsonPostResponse( 'notifications/actives', '',
IO::String->new($notif), IO::String->new($notif),
'application/json', length($notif) ); 'application/json', length($notif) );
ok( $res->{error} =~ /^Notification not created: Bad date/, 'Notification not inserted' ); ok( $res->{error} =~ /^Notification not created: Bad date/,
'Notification not inserted' );
count(1); count(1);
$notif = $notif =
'{"date":"2099-05_12","uid":"dwho","reference":"Test","xml":"{\"title\":\"Test\"}"}'; '{"date":"2099-05_12","uid":"dwho","reference":"Test","xml":"{\"title\":\"Test\"}"}';
my $res = $res =
&client->jsonPostResponse( 'notifications/actives', '', &client->jsonPostResponse( 'notifications/actives', '',
IO::String->new($notif), IO::String->new($notif),
'application/json', length($notif) ); 'application/json', length($notif) );

View File

@ -277,7 +277,7 @@ $res = &client->jsonResponse( '/sfa/persistent',
'uid=*&groupBy=substr(uid,0)&U2FCheck=2&TOTPCheck=2&UBKCheck=2' ); 'uid=*&groupBy=substr(uid,0)&U2FCheck=2&TOTPCheck=2&UBKCheck=2' );
ok( $res->{result} == 1, ok( $res->{result} == 1,
'Search "uid"=* & UBK & TOTP & UBK - Result code = 1' ); 'Search "uid"=* & UBK & TOTP & UBK - Result code = 1' );
ok( $res->{count} == 1, 'Found 1 result' ) or print STDERR Dumper($res); ok( $res->{count} == 1, 'Found 1 result' ) or print STDERR Dumper($res);
ok( @{ $res->{values} } == 1, 'List 1 result' ); ok( @{ $res->{values} } == 1, 'List 1 result' );
ok( $res->{values}->[0]->{value} && $res->{values}->[0]->{value} eq 'd', ok( $res->{values}->[0]->{value} && $res->{values}->[0]->{value} eq 'd',
'Result match "uid=d"' ) 'Result match "uid=d"' )

Some files were not shown because too many files have changed in this diff Show More