Merge branch 'v2.0'

TODO: Fix OIDC
This commit is contained in:
Xavier 2019-12-11 08:00:40 +01:00
commit 947dd9a438
215 changed files with 6910 additions and 866 deletions

View File

@ -8,6 +8,18 @@
paths: paths:
- result/* - result/*
.build_job_centos:
stage: build
script:
- yum -y install epel-release
- make rpm-dist
- ci-build-pkg
artifacts:
expire_in: 1 day
paths:
- result/*
stages: stages:
- build - build
- sign - sign
@ -46,6 +58,10 @@ build_bionic:
image: buildpkg/ubuntu:bionic image: buildpkg/ubuntu:bionic
<<: *job_build <<: *job_build
build_centos_7:
image: buildpkg/centos:7
extends: .build_job_centos
sign: sign:
image: buildpkg/debian:stretch image: buildpkg/debian:stretch
stage: sign stage: sign
@ -60,6 +76,7 @@ sign:
- build_buster - build_buster
- build_disco - build_disco
- build_bionic - build_bionic
- build_centos_7
artifacts: artifacts:
expire_in: 1 day expire_in: 1 day
paths: paths:

View File

@ -129,8 +129,8 @@ E2E_TESTS='portal/*.js'
# LDAP backend test # LDAP backend test
LLNGTESTLDAP_SLAPD_BIN=/usr/sbin/slapd LLNGTESTLDAP_SLAPD_BIN=/usr/sbin/slapd
LLNGTESTLDAP_SLAPADD_BIN=/usr/sbin/slapdadd LLNGTESTLDAP_SLAPADD_BIN=/usr/sbin/slapadd
LLNGTESTLDAP_SCHEMA_DIR=/etc/slapd/schema LLNGTESTLDAP_SCHEMA_DIR=/etc/ldap/schema
# Other # Other
SRCCOMMONDIR=lemonldap-ng-common SRCCOMMONDIR=lemonldap-ng-common
@ -623,6 +623,7 @@ install_bin: install_conf_dir
${SRCPORTALDIR}/site/cron/purgeCentralCache \ ${SRCPORTALDIR}/site/cron/purgeCentralCache \
${SRCPORTALDIR}/scripts/llngDeleteSession \ ${SRCPORTALDIR}/scripts/llngDeleteSession \
${SRCCOMMONDIR}/scripts/convertConfig \ ${SRCCOMMONDIR}/scripts/convertConfig \
${SRCCOMMONDIR}/scripts/convertSessions \
${SRCCOMMONDIR}/scripts/lmMigrateConfFiles2ini \ ${SRCCOMMONDIR}/scripts/lmMigrateConfFiles2ini \
${SRCCOMMONDIR}/scripts/rotateOidcKeys \ ${SRCCOMMONDIR}/scripts/rotateOidcKeys \
${SRCMANAGERDIR}/scripts/lmConfigEditor \ ${SRCMANAGERDIR}/scripts/lmConfigEditor \

View File

@ -18,6 +18,16 @@
auth_request_set $headervalue9 $upstream_http_headervalue9; auth_request_set $headervalue9 $upstream_http_headervalue9;
auth_request_set $headername10 $upstream_http_headername10; auth_request_set $headername10 $upstream_http_headername10;
auth_request_set $headervalue10 $upstream_http_headervalue10; auth_request_set $headervalue10 $upstream_http_headervalue10;
auth_request_set $headername11 $upstream_http_headername11;
auth_request_set $headervalue11 $upstream_http_headervalue11;
auth_request_set $headername12 $upstream_http_headername12;
auth_request_set $headervalue12 $upstream_http_headervalue12;
auth_request_set $headername13 $upstream_http_headername13;
auth_request_set $headervalue13 $upstream_http_headervalue13;
auth_request_set $headername14 $upstream_http_headername14;
auth_request_set $headervalue14 $upstream_http_headervalue14;
auth_request_set $headername15 $upstream_http_headername15;
auth_request_set $headervalue15 $upstream_http_headervalue15;
auth_request_set $lmcookie $upstream_http_cookie; auth_request_set $lmcookie $upstream_http_cookie;
access_by_lua ' access_by_lua '
i = 1 i = 1

View File

@ -1,12 +1,14 @@
/etc/lemonldap-ng/lemonldap-ng.ini /etc/lemonldap-ng/lemonldap-ng.ini
/etc/lemonldap-ng/for_etc_hosts /etc/lemonldap-ng/for_etc_hosts
/usr/share/man/man1/convertConfig.1p /usr/share/man/man1/convertConfig.1p
/usr/share/man/man1/convertSessions.1p
/usr/share/man/man1/lemonldap-ng-cli.1p /usr/share/man/man1/lemonldap-ng-cli.1p
/usr/share/man/man3/Lemonldap::NG::Common* /usr/share/man/man3/Lemonldap::NG::Common*
/usr/share/perl5/auto/Lemonldap/NG/Common /usr/share/perl5/auto/Lemonldap/NG/Common
/usr/share/perl5/Lemonldap/NG/Common* /usr/share/perl5/Lemonldap/NG/Common*
/usr/share/lemonldap-ng/ressources /usr/share/lemonldap-ng/ressources
/usr/share/lemonldap-ng/bin/convertConfig /usr/share/lemonldap-ng/bin/convertConfig
/usr/share/lemonldap-ng/bin/convertSessions
/usr/share/lemonldap-ng/bin/importMetadata /usr/share/lemonldap-ng/bin/importMetadata
/usr/share/lemonldap-ng/bin/lmMigrateConfFiles2ini /usr/share/lemonldap-ng/bin/lmMigrateConfFiles2ini
/usr/share/lemonldap-ng/bin/rotateOidcKeys /usr/share/lemonldap-ng/bin/rotateOidcKeys

View File

@ -66,7 +66,7 @@ describe('00 Lemonldap::NG', function() {
browser.driver.findElement(by.xpath("//input[@name='password']")).sendKeys('ohwd'); browser.driver.findElement(by.xpath("//input[@name='password']")).sendKeys('ohwd');
browser.driver.findElement(by.xpath("//input[@name='checkLogins']")).click(); browser.driver.findElement(by.xpath("//input[@name='checkLogins']")).click();
browser.driver.findElement(by.xpath("//button[@type='submit']")).click(); browser.driver.findElement(by.xpath("//button[@type='submit']")).click();
expect(browser.driver.findElement(by.css('[trmsg="5"]')).getText()).toEqual('Mot de passe ou identifiant incorrect'); expect(browser.driver.findElement(by.css('[trmsg="5"]')).getText()).toEqual('Identifiant ou mot de passe incorrect');
browser.driver.findElement(by.css('[trspan="goToPortal"]')).click(); browser.driver.findElement(by.css('[trspan="goToPortal"]')).click();
// Login attempt // Login attempt

View File

@ -129,7 +129,7 @@
.\" ======================================================================== .\" ========================================================================
.\" .\"
.IX Title "llng-fastcgi-server 8" .IX Title "llng-fastcgi-server 8"
.TH llng-fastcgi-server 8 "2019-10-30" "perl v5.26.1" "User Contributed Perl Documentation" .TH llng-fastcgi-server 8 "2019-11-08" "perl v5.26.1" "User Contributed Perl Documentation"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents. .\" way too many mistakes in technical documents.
.if n .ad l .if n .ad l

View File

@ -71,6 +71,7 @@ META.json
META.yml META.yml
README README
scripts/convertConfig scripts/convertConfig
scripts/convertSessions
scripts/importMetadata scripts/importMetadata
scripts/lemonldap-ng-cli scripts/lemonldap-ng-cli
scripts/lmMigrateConfFiles2ini scripts/lmMigrateConfFiles2ini

View File

@ -90,6 +90,7 @@ WriteMakefile(
}, },
MAN1PODS => { MAN1PODS => {
'scripts/convertConfig' => 'blib/man1/convertConfig.1p', 'scripts/convertConfig' => 'blib/man1/convertConfig.1p',
'scripts/convertSessions' => 'blib/man1/convertSessions.1p',
'scripts/lemonldap-ng-cli' => 'blib/man1/lemonldap-ng-cli.1p', 'scripts/lemonldap-ng-cli' => 'blib/man1/lemonldap-ng-cli.1p',
}, },
); );

View File

@ -85,11 +85,12 @@ logLevel = warn
; CONFIGURATION CHECK ; CONFIGURATION CHECK
; ;
; By default, LLNG verify configuration at server start. If you use "reload" ; LLNG verify configuration at server start. If you use "reload" mechanism,
; mechanism local cache will be updated. configuration is checked locally every ; local cache will be updated. Configuration is checked locally every
; 10 minutes by each LLNG component. You can change this value using ; 10 minutes by each LLNG component. You can change this value using
; `checkTime` (time in seconds): ; `checkTime` (time in seconds).
;checkTime = 600 ; To increase performances, you should comment this parameter and rely on cache.
checkTime = 1
[configuration] [configuration]

View File

@ -219,18 +219,24 @@ sub _dbiGKFAS {
$sth->execute; $sth->execute;
my %res; my %res;
while ( my @row = $sth->fetchrow_array ) { while ( my @row = $sth->fetchrow_array ) {
if ( ref($data) eq 'CODE' ) { eval {
my $tmp = if ( ref($data) eq 'CODE' ) {
&$data( $args->{unserialize}->( $row[1], $next ), $row[0] ); my $tmp =
$res{ $row[0] } = $tmp if ( defined($tmp) ); &$data( $args->{unserialize}->( $row[1], $next ), $row[0] );
} $res{ $row[0] } = $tmp if ( defined($tmp) );
elsif ($data) { }
$data = [$data] unless ( ref($data) ); elsif ($data) {
my $tmp = $args->{unserialize}->( $row[1], $next ); $data = [$data] unless ( ref($data) );
$res{ $row[0] }->{$_} = $tmp->{$_} foreach (@$data); my $tmp = $args->{unserialize}->( $row[1], $next );
} $res{ $row[0] }->{$_} = $tmp->{$_} foreach (@$data);
else { }
$res{ $row[0] } = $args->{unserialize}->( $row[1], $next ); else {
$res{ $row[0] } = $args->{unserialize}->( $row[1], $next );
}
};
if ($@) {
print STDERR "Error in session $row[0]\n";
delete $res{ $row[0] };
} }
} }
return \%res; return \%res;
@ -249,26 +255,32 @@ sub _FileGKFAS {
my %res; my %res;
for my $f (@t) { for my $f (@t) {
open F, '<', "$args->{Directory}/$f"; open F, '<', "$args->{Directory}/$f";
my $row = join '', <F>; eval {
if ( ref($data) eq 'CODE' ) { my $row = join '', <F>;
eval { $res{$f} = &$data( $args->{unserialize}->($row), $f ); }; if ( ref($data) eq 'CODE' ) {
if ($@) { eval { $res{$f} = &$data( $args->{unserialize}->($row), $f ); };
$res{$f} = &$data( undef, $f ); if ($@) {
$res{$f} = &$data( undef, $f );
}
} }
} elsif ($data) {
elsif ($data) { $data = [$data] unless ( ref($data) );
$data = [$data] unless ( ref($data) ); my $tmp;
my $tmp; eval { $tmp = $args->{unserialize}->($row); };
eval { $tmp = $args->{unserialize}->($row); }; if ($@) {
if ($@) { $res{$f}->{$_} = undef foreach (@$data);
$res{$f}->{$_} = undef foreach (@$data); }
else {
$res{$f}->{$_} = $tmp->{$_} foreach (@$data);
}
} }
else { else {
$res{$f}->{$_} = $tmp->{$_} foreach (@$data); eval { $res{$f} = $args->{unserialize}->($row); };
} }
} };
else { if ($@) {
eval { $res{$f} = $args->{unserialize}->($row); }; print STDERR "Error in session $f\n";
delete $res{$f};
} }
} }
return \%res; return \%res;
@ -318,17 +330,23 @@ sub _DBFileGKFAS {
my %res; my %res;
foreach my $k ( keys %{ $class->{dbm} } ) { foreach my $k ( keys %{ $class->{dbm} } ) {
if ( ref($data) eq 'CODE' ) { eval {
$res{$k} = if ( ref($data) eq 'CODE' ) {
&$data( $args->{unserialize}->( $class->{dbm}->{$k} ), $k ); $res{$k} =
} &$data( $args->{unserialize}->( $class->{dbm}->{$k} ), $k );
elsif ($data) { }
$data = [$data] unless ( ref($data) ); elsif ($data) {
my $tmp = $args->{unserialize}->( $class->{dbm}->{$k} ); $data = [$data] unless ( ref($data) );
$res{$k}->{$_} = $tmp->{$_} foreach (@$data); my $tmp = $args->{unserialize}->( $class->{dbm}->{$k} );
} $res{$k}->{$_} = $tmp->{$_} foreach (@$data);
else { }
$res{$k} = $args->{unserialize}->( $class->{dbm}->{$k} ); else {
$res{$k} = $args->{unserialize}->( $class->{dbm}->{$k} );
}
};
if ($@) {
print STDERR "Error in session $k\n";
delete $res{$k};
} }
} }
return \%res; return \%res;

View File

@ -50,10 +50,12 @@ sub compactConf {
# Disabled for now: # Disabled for now:
# Remove unused issuerDB parameters # Remove unused issuerDB parameters except options
foreach my $k ( keys %$issuerParameters ) { foreach my $k ( keys %$issuerParameters ) {
unless ( $conf->{ $k . "Activation" } ) { unless ( $conf->{ $k . "Activation" } ) {
delete $conf->{$_} foreach ( @{ $issuerParameters->{$k} } ); foreach ( @{ $issuerParameters->{$k} } ) {
delete $conf->{$_} unless ( $_ =~ /^issuers/ );
}
} }
} }

View File

@ -23,8 +23,8 @@ use constant HANDLERSECTION => "handler";
use constant MANAGERSECTION => "manager"; 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(?:RPMetaData(?:(?:Option(?:sExtraClaim)?|ExportedVar)s|Node)|OPMetaData(?:(?:ExportedVar|Option)s|J(?:SON|WKS)|Node)|S(?:erviceMetaDataAuthnContext|torageOptions))|penIdExportedVars)|s(?:aml(?:S(?:PMetaData(?:(?:ExportedAttribute|Option)s|Node|XML)|torageOptions)|IDPMetaData(?:(?:ExportedAttribute|Option)s|Node|XML))|essionDataToRemember|laveExportedVars|fExtra)|c(?:as(?:S(?:rvMetaData(?:(?:ExportedVar|Option)s|Node)|torageOptions)|A(?:ppMetaData(?:(?:ExportedVar|Option)s|Node)|ttributes))|(?:ustomAddParam|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(?:RPMetaData(?:(?:Option(?:sExtraClaim)?|ExportedVar)s|Node)|OPMetaData(?:(?:ExportedVar|Option)s|J(?:SON|WKS)|Node)|S(?:erviceMetaDataAuthnContext|torageOptions))|penIdExportedVars)|s(?:aml(?:S(?:PMetaData(?:(?:ExportedAttribute|Option)s|Node|XML)|torageOptions)|IDPMetaData(?:(?:ExportedAttribute|Option)s|Node|XML))|essionDataToRemember|laveExportedVars|fExtra)|c(?:as(?:S(?:rvMetaData(?:(?:ExportedVar|Option)s|Node)|torageOptions)|A(?:ppMetaData(?:(?:ExportedVar|Option)s|Node)|ttributes))|(?: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(?:ServiceAllow(?:(?:AuthorizationCode|Implicit|Hybrid)Flow|DynamicRegistration)|RPMetaDataOptions(?:LogoutSessionRequired|BypassConsent|RequirePKCE|Public)|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|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)|d(?:isablePersistentStorage|biDynamicHashEnabled|ontCompactConf)|(?:mai(?:lOnPasswordChang|ntenanc)|vhostMaintenanc)e|rest(?:(?:Session|Config)Server|ExportSecretKeys)|br(?:owsersDontStorePassword|uteForceProtection)|h(?:ideOldPassword|ttpOnly)|yubikey2fUserCanRemoveKey|(?:activeTim|wsdlServ)er|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|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)|d(?:isablePersistentStorage|biDynamicHashEnabled|ontCompactConf)|(?:mai(?:lOnPasswordChang|ntenanc)|vhostMaintenanc)e|br(?:owsersDontStorePassword|uteForceProtection)|(?:(?:globalLogout|active)Tim|wsdlServ)er|h(?:ideOldPassword|ttpOnly)|yubikey2fUserCanRemoveKey|krb(?:RemoveDomain|ByJs))$/;
our @sessionTypes = ( 'remoteGlobal', 'global', 'localSession', 'persistent', 'saml', 'oidc', 'cas' ); our @sessionTypes = ( 'remoteGlobal', 'global', 'localSession', 'persistent', 'saml', 'oidc', 'cas' );

View File

@ -78,6 +78,8 @@ sub defaultValues {
'failedLoginNumber' => 5, 'failedLoginNumber' => 5,
'favAppsMaxNumber' => 3, 'favAppsMaxNumber' => 3,
'formTimeout' => 120, 'formTimeout' => 120,
'globalLogoutRule' => 0,
'globalLogoutTimer' => 1,
'globalStorage' => 'Apache::Session::File', 'globalStorage' => 'Apache::Session::File',
'globalStorageOptions' => { 'globalStorageOptions' => {
'Directory' => '/var/lib/lemonldap-ng/sessions/', 'Directory' => '/var/lib/lemonldap-ng/sessions/',
@ -167,21 +169,25 @@ sub defaultValues {
'multiValuesSeparator' => '; ', 'multiValuesSeparator' => '; ',
'mySessionAuthorizedRWKeys' => 'mySessionAuthorizedRWKeys' =>
[ '_appsListOrder', '_oidcConnectedRP', '_oidcConsents' ], [ '_appsListOrder', '_oidcConnectedRP', '_oidcConsents' ],
'notificationServerPOST' => 1, 'notificationDefaultCond' => '',
'notificationServerPOST' => 1,
'notificationServerSentAttributes' => 'notificationServerSentAttributes' =>
'uid reference date title subtitle text check', 'uid reference date title subtitle text check',
'notificationStorage' => 'File', 'notificationStorage' => 'File',
'notificationStorageOptions' => { 'notificationStorageOptions' => {
'dirName' => '/var/lib/lemonldap-ng/notifications' 'dirName' => '/var/lib/lemonldap-ng/notifications'
}, },
'notificationWildcard' => 'allusers', 'notificationWildcard' => 'allusers',
'notifyDeleted' => 1, 'notifyDeleted' => 1,
'nullAuthnLevel' => 0, 'nullAuthnLevel' => 0,
'oidcAuthnLevel' => 1, 'oidcAuthnLevel' => 1,
'oidcRPCallbackGetParam' => 'openidconnectcallback', 'oidcRPCallbackGetParam' => 'openidconnectcallback',
'oidcRPStateTimeout' => 600, 'oidcRPStateTimeout' => 600,
'oidcServiceAllowAuthorizationCodeFlow' => 1, 'oidcServiceAccessTokenExpiration' => 3600,
'oidcServiceMetaDataAuthnContext' => { 'oidcServiceAllowAuthorizationCodeFlow' => 1,
'oidcServiceAuthorizationCodeExpiration' => 60,
'oidcServiceIDTokenExpiration' => 3600,
'oidcServiceMetaDataAuthnContext' => {
'loa-1' => 1, 'loa-1' => 1,
'loa-2' => 2, 'loa-2' => 2,
'loa-3' => 3, 'loa-3' => 3,
@ -198,6 +204,7 @@ sub defaultValues {
'oidcServiceMetaDataRegistrationURI' => 'register', 'oidcServiceMetaDataRegistrationURI' => 'register',
'oidcServiceMetaDataTokenURI' => 'token', 'oidcServiceMetaDataTokenURI' => 'token',
'oidcServiceMetaDataUserInfoURI' => 'userinfo', 'oidcServiceMetaDataUserInfoURI' => 'userinfo',
'oidcServiceOfflineSessionExpiration' => 2592000,
'openIdAuthnLevel' => 1, 'openIdAuthnLevel' => 1,
'openIdExportedVars' => {}, 'openIdExportedVars' => {},
'openIdIDPList' => '0;', 'openIdIDPList' => '0;',

View File

@ -22,12 +22,12 @@ our $specialNodeHash = {
}; };
our $doubleHashKeys = 'issuerDBGetParameters'; our $doubleHashKeys = 'issuerDBGetParameters';
our $simpleHashKeys = '(?:(?:l(?:o(?:calSessionStorageOption|goutService)|dapExportedVar|wp(?:Ssl)?Opt)|re(?:moteGlobalStorageOption|st2f(?:Verify|Init)Arg|loadUrl)|c(?:as(?:StorageOption|Attribute)|ustomAddParam|ombModule)|(?:(?:d(?:emo|bi)|facebook|webID)E|e)xportedVar|g(?:r(?:antSessionRule|oup)|lobalStorageOption)|n(?:otificationStorageOption|ginxCustomHandler)|p(?:ersistentStorageOption|ortalSkinRule)|macro)s|o(?:idcS(?:erviceMetaDataAuthnContext|torageOptions)|penIdExportedVars)|s(?:(?:amlStorageOption|laveExportedVar)s|essionDataToRemember|fExtra)|a(?:ut(?:hChoiceMod|oSigninR)ules|pplicationList)|S(?:MTPTLSOpts|SLVarIf))'; our $simpleHashKeys = '(?:(?:l(?:o(?:calSessionStorageOption|goutService)|dapExportedVar|wp(?:Ssl)?Opt)|c(?:as(?:StorageOption|Attribute)|ustom(?:Plugins|Add)Param|ombModule)|re(?:moteGlobalStorageOption|st2f(?:Verify|Init)Arg|loadUrl)|(?:(?:d(?:emo|bi)|facebook|webID)E|e)xportedVar|g(?:r(?:antSessionRule|oup)|lobalStorageOption)|n(?:otificationStorageOption|ginxCustomHandler)|p(?:ersistentStorageOption|ortalSkinRule)|macro)s|o(?:idcS(?:erviceMetaDataAuthnContext|torageOptions)|penIdExportedVars)|s(?:(?:amlStorageOption|laveExportedVar)s|essionDataToRemember|fExtra)|a(?:ut(?:hChoiceMod|oSigninR)ules|pplicationList)|S(?:MTPTLSOpts|SLVarIf))';
our $specialNodeKeys = '(?:(?:(?:saml(?:ID|S)|oidc[OR])P|cas(?:App|Srv))MetaDataNode|virtualHost)s'; our $specialNodeKeys = '(?:(?:(?:saml(?:ID|S)|oidc[OR])P|cas(?:App|Srv))MetaDataNode|virtualHost)s';
our $casAppMetaDataNodeKeys = 'casAppMetaData(?:Options(?:UserAttribut|Servic|Rul)e|ExportedVars)'; our $casAppMetaDataNodeKeys = 'casAppMetaData(?:Options(?:UserAttribut|Servic|Rul)e|ExportedVars)';
our $casSrvMetaDataNodeKeys = 'casSrvMetaData(?:Options(?:ProxiedServices|DisplayName|SortNumber|Gateway|Renew|Icon|Url)|ExportedVars)'; our $casSrvMetaDataNodeKeys = 'casSrvMetaData(?:Options(?:ProxiedServices|DisplayName|SortNumber|Gateway|Renew|Icon|Url)|ExportedVars)';
our $oidcOPMetaDataNodeKeys = 'oidcOPMetaData(?:Options(?:C(?:lient(?:Secret|ID)|heckJWTSignature|onfigurationURI)|S(?:toreIDToken|ortNumber|cope)|TokenEndpointAuthMethod|(?:JWKSTimeou|Promp)t|I(?:DTokenMaxAge|con)|U(?:iLocales|seNonce)|Display(?:Name)?|AcrValues|MaxAge)|ExportedVars|J(?:SON|WKS))'; our $oidcOPMetaDataNodeKeys = 'oidcOPMetaData(?:Options(?:C(?:lient(?:Secret|ID)|heckJWTSignature|onfigurationURI)|S(?:toreIDToken|ortNumber|cope)|TokenEndpointAuthMethod|(?:JWKSTimeou|Promp)t|I(?:DTokenMaxAge|con)|U(?:iLocales|seNonce)|Display(?:Name)?|AcrValues|MaxAge)|ExportedVars|J(?:SON|WKS))';
our $oidcRPMetaDataNodeKeys = 'oidcRPMetaData(?:Options(?:I(?:DToken(?:Expiration|SignAlg)|con)|Logout(?:SessionRequired|Type|Url)|R(?:e(?:directUris|quirePKCE)|ule)|P(?:ostLogoutRedirectUris|ublic)|AccessTokenExpiration|Client(?:Secret|ID)|BypassConsent|DisplayName|ExtraClaims|UserIDAttr)|ExportedVars)'; our $oidcRPMetaDataNodeKeys = 'oidcRPMetaData(?:Options(?:A(?:(?:uthorizationCode|ccessToken)Expiration|llowOffline)|I(?:DToken(?:ForceClaims|Expiration|SignAlg)|con)|R(?:e(?:directUris|freshToken|quirePKCE)|ule)|Logout(?:SessionRequired|Type|Url)|P(?:ostLogoutRedirectUris|ublic)|OfflineSessionExpiration|Client(?:Secret|ID)|BypassConsent|DisplayName|ExtraClaims|UserIDAttr)|ExportedVars)';
our $samlIDPMetaDataNodeKeys = 'samlIDPMetaData(?:Options(?:(?:Check(?:S[LS]OMessageSignatur|Audienc|Tim)|EncryptionMod|UserAttribut|DisplayNam)e|S(?:ignS[LS]OMessage|toreSAMLToken|[LS]OBinding|ortNumber)|A(?:llow(?:LoginFromIDP|ProxiedAuthn)|daptSessionUtime)|Re(?:questedAuthnContext|solutionRule|layStateURL)|Force(?:Authn|UTF8)|I(?:sPassive|con)|NameIDFormat)|ExportedAttributes|XML)'; our $samlIDPMetaDataNodeKeys = 'samlIDPMetaData(?:Options(?:(?:Check(?:S[LS]OMessageSignatur|Audienc|Tim)|EncryptionMod|UserAttribut|DisplayNam)e|S(?:ignS[LS]OMessage|toreSAMLToken|[LS]OBinding|ortNumber)|A(?:llow(?:LoginFromIDP|ProxiedAuthn)|daptSessionUtime)|Re(?:questedAuthnContext|solutionRule|layStateURL)|Force(?:Authn|UTF8)|I(?:sPassive|con)|NameIDFormat)|ExportedAttributes|XML)';
our $samlSPMetaDataNodeKeys = 'samlSPMetaData(?:Options(?:N(?:ameID(?:SessionKey|Format)|otOnOrAfterTimeout)|S(?:essionNotOnOrAfterTimeout|ignS[LS]OMessage)|(?:CheckS[LS]OMessageSignatur|OneTimeUs|Rul)e|En(?:ableIDPInitiatedURL|cryptionMode)|ForceUTF8)|ExportedAttributes|XML)'; our $samlSPMetaDataNodeKeys = 'samlSPMetaData(?:Options(?:N(?:ameID(?:SessionKey|Format)|otOnOrAfterTimeout)|S(?:essionNotOnOrAfterTimeout|ignS[LS]OMessage)|(?:CheckS[LS]OMessageSignatur|OneTimeUs|Rul)e|En(?:ableIDPInitiatedURL|cryptionMode)|ForceUTF8)|ExportedAttributes|XML)';
our $virtualHostKeys = '(?:vhost(?:A(?:uthnLevel|liases)|(?:Maintenanc|Typ)e|ServiceTokenTTL|Https|Port)|(?:exportedHeader|locationRule)s|post)'; our $virtualHostKeys = '(?:vhost(?:A(?:uthnLevel|liases)|(?:Maintenanc|Typ)e|ServiceTokenTTL|Https|Port)|(?:exportedHeader|locationRule)s|post)';
@ -68,6 +68,6 @@ our $issuerParameters = {
issuerOptions => [qw(issuersTimeout)], issuerOptions => [qw(issuersTimeout)],
}; };
our $samlServiceParameters = [qw(samlEntityID samlServicePrivateKeySig samlServicePrivateKeySigPwd samlServicePublicKeySig samlServicePrivateKeyEnc samlServicePrivateKeyEncPwd samlServicePublicKeyEnc samlServiceUseCertificateInResponse samlServiceSignatureMethod samlNameIDFormatMapEmail samlNameIDFormatMapX509 samlNameIDFormatMapWindows samlNameIDFormatMapKerberos samlAuthnContextMapPassword samlAuthnContextMapPasswordProtectedTransport samlAuthnContextMapTLSClient samlAuthnContextMapKerberos samlOrganizationDisplayName samlOrganizationName samlOrganizationURL samlSPSSODescriptorAuthnRequestsSigned samlSPSSODescriptorWantAssertionsSigned samlSPSSODescriptorSingleLogoutServiceHTTPRedirect samlSPSSODescriptorSingleLogoutServiceHTTPPost samlSPSSODescriptorSingleLogoutServiceSOAP samlSPSSODescriptorAssertionConsumerServiceHTTPArtifact samlSPSSODescriptorAssertionConsumerServiceHTTPPost samlSPSSODescriptorArtifactResolutionServiceArtifact samlIDPSSODescriptorWantAuthnRequestsSigned samlIDPSSODescriptorSingleSignOnServiceHTTPRedirect samlIDPSSODescriptorSingleSignOnServiceHTTPPost samlIDPSSODescriptorSingleSignOnServiceHTTPArtifact samlIDPSSODescriptorSingleLogoutServiceHTTPRedirect samlIDPSSODescriptorSingleLogoutServiceHTTPPost samlIDPSSODescriptorSingleLogoutServiceSOAP samlIDPSSODescriptorArtifactResolutionServiceArtifact samlAttributeAuthorityDescriptorAttributeServiceSOAP samlMetadataForceUTF8 samlStorage samlStorageOptions samlRelayStateTimeout samlUseQueryStringSpecific samlCommonDomainCookieActivation samlCommonDomainCookieDomain samlCommonDomainCookieReader samlCommonDomainCookieWriter samlDiscoveryProtocolActivation samlDiscoveryProtocolURL samlDiscoveryProtocolPolicy samlDiscoveryProtocolIsPassive samlOverrideIDPEntityID)]; our $samlServiceParameters = [qw(samlEntityID samlServicePrivateKeySig samlServicePrivateKeySigPwd samlServicePublicKeySig samlServicePrivateKeyEnc samlServicePrivateKeyEncPwd samlServicePublicKeyEnc samlServiceUseCertificateInResponse samlServiceSignatureMethod samlNameIDFormatMapEmail samlNameIDFormatMapX509 samlNameIDFormatMapWindows samlNameIDFormatMapKerberos samlAuthnContextMapPassword samlAuthnContextMapPasswordProtectedTransport samlAuthnContextMapTLSClient samlAuthnContextMapKerberos samlOrganizationDisplayName samlOrganizationName samlOrganizationURL samlSPSSODescriptorAuthnRequestsSigned samlSPSSODescriptorWantAssertionsSigned samlSPSSODescriptorSingleLogoutServiceHTTPRedirect samlSPSSODescriptorSingleLogoutServiceHTTPPost samlSPSSODescriptorSingleLogoutServiceSOAP samlSPSSODescriptorAssertionConsumerServiceHTTPArtifact samlSPSSODescriptorAssertionConsumerServiceHTTPPost samlSPSSODescriptorArtifactResolutionServiceArtifact samlIDPSSODescriptorWantAuthnRequestsSigned samlIDPSSODescriptorSingleSignOnServiceHTTPRedirect samlIDPSSODescriptorSingleSignOnServiceHTTPPost samlIDPSSODescriptorSingleSignOnServiceHTTPArtifact samlIDPSSODescriptorSingleLogoutServiceHTTPRedirect samlIDPSSODescriptorSingleLogoutServiceHTTPPost samlIDPSSODescriptorSingleLogoutServiceSOAP samlIDPSSODescriptorArtifactResolutionServiceArtifact samlAttributeAuthorityDescriptorAttributeServiceSOAP samlMetadataForceUTF8 samlStorage samlStorageOptions samlRelayStateTimeout samlUseQueryStringSpecific samlCommonDomainCookieActivation samlCommonDomainCookieDomain samlCommonDomainCookieReader samlCommonDomainCookieWriter samlDiscoveryProtocolActivation samlDiscoveryProtocolURL samlDiscoveryProtocolPolicy samlDiscoveryProtocolIsPassive samlOverrideIDPEntityID)];
our $oidcServiceParameters = [qw(oidcServiceMetaDataAuthorizeURI oidcServiceMetaDataTokenURI oidcServiceMetaDataUserInfoURI oidcServiceMetaDataJWKSURI oidcServiceMetaDataRegistrationURI oidcServiceMetaDataIntrospectionURI oidcServiceMetaDataEndSessionURI oidcServiceMetaDataCheckSessionURI oidcServiceMetaDataFrontChannelURI oidcServiceMetaDataBackChannelURI oidcServiceMetaDataAuthnContext oidcServicePrivateKeySig oidcServicePublicKeySig oidcServiceKeyIdSig oidcServiceAllowDynamicRegistration oidcServiceAllowAuthorizationCodeFlow oidcServiceAllowImplicitFlow oidcServiceAllowHybridFlow oidcStorage oidcStorageOptions)]; our $oidcServiceParameters = [qw(oidcServiceMetaDataAuthorizeURI oidcServiceMetaDataTokenURI oidcServiceMetaDataUserInfoURI oidcServiceMetaDataJWKSURI oidcServiceMetaDataRegistrationURI oidcServiceMetaDataIntrospectionURI oidcServiceMetaDataEndSessionURI oidcServiceMetaDataCheckSessionURI oidcServiceMetaDataFrontChannelURI oidcServiceMetaDataBackChannelURI oidcServiceMetaDataAuthnContext oidcServicePrivateKeySig oidcServicePublicKeySig oidcServiceKeyIdSig oidcServiceAllowDynamicRegistration oidcServiceAllowAuthorizationCodeFlow oidcServiceAllowImplicitFlow oidcServiceAllowHybridFlow oidcServiceAuthorizationCodeExpiration oidcServiceAccessTokenExpiration oidcServiceIDTokenExpiration oidcServiceOfflineSessionExpiration oidcStorage oidcStorageOptions)];
1; 1;

View File

@ -21,6 +21,7 @@ $hash = \&Digest::SHA::sha256;
use constant HMAC_LENGTH => 32; use constant HMAC_LENGTH => 32;
use constant IV_LENGTH => 16; use constant IV_LENGTH => 16;
# Build initialization vector subroutine
BEGIN { BEGIN {
eval { require Crypt::URandom; Crypt::URandom::urandom(IV_LENGTH) }; eval { require Crypt::URandom; Crypt::URandom::urandom(IV_LENGTH) };
if ($@) { if ($@) {

View File

@ -7,8 +7,9 @@ use JSON qw(from_json to_json);
our $VERSION = '2.1.0'; our $VERSION = '2.1.0';
sub newNotification { sub newNotification {
my ( $self, $jsonString ) = @_; my ( $self, $jsonString, $defaultCond ) = @_;
my $json; my $json;
$defaultCond ||= '';
eval { $json = from_json( $jsonString, { allow_nonref => 1 } ) }; eval { $json = from_json( $jsonString, { allow_nonref => 1 } ) };
if ( my $err = $@ ) { if ( my $err = $@ ) {
eval { $self->logger->error("Unable to decode JSON file: $err") }; eval { $self->logger->error("Unable to decode JSON file: $err") };
@ -35,8 +36,16 @@ sub newNotification {
} }
push @data, $tmp; push @data, $tmp;
} }
push @data, ( $notif->{condition} // '' );
push @notifs, [ @data, $jsonString ]; unless ( exists $notif->{condition} ) {
$self->userLogger->info(
"Set defaultCondition ($defaultCond) for notification $notif->{reference}");
$notif->{condition} = $defaultCond;
}
push @data, ( $notif->{condition} );
my $body = to_json($notif);
push @notifs, [ @data, $body ];
} }
my $count; my $count;
foreach (@notifs) { foreach (@notifs) {

View File

@ -18,7 +18,8 @@ has parser => (
# @param $xml XML string containing notification # @param $xml XML string containing notification
# @return number of notifications done # @return number of notifications done
sub newNotification { sub newNotification {
my ( $self, $xml ) = @_; my ( $self, $xml, $defaultCond ) = @_;
$defaultCond ||= '';
eval { $xml = $self->parser->parse_string($xml) }; eval { $xml = $self->parser->parse_string($xml) };
if ( my $err = $@ ) { if ( my $err = $@ ) {
eval { $self->logger->error("Unable to read XML file : $err") }; eval { $self->logger->error("Unable to read XML file : $err") };
@ -53,7 +54,12 @@ sub newNotification {
if ( $tmp = $notif->getAttribute($_) ) { if ( $tmp = $notif->getAttribute($_) ) {
push @data, $tmp; push @data, $tmp;
} }
else { push @data, ""; } else {
$self->userLogger->info(
"Set defaultCondition ($defaultCond) for notification " . $notif->{reference}
);
push @data, $defaultCond;
}
} }
my $result = XML::LibXML::Document->new( $version, $encoding ); my $result = XML::LibXML::Document->new( $version, $encoding );

View File

@ -18,7 +18,7 @@ our $VERSION = '2.1.0';
# Not that only functions, not methods, can be written here # Not that only functions, not methods, can be written here
our $functions = our $functions =
[ [
qw(&checkLogonHours &date &checkDate &basic &unicode2iso &iso2unicode &groupMatch &isInNet6) qw(&checkLogonHours &date &checkDate &basic &unicode2iso &iso2unicode &groupMatch &isInNet6 &varIsInUri)
]; ];
## @function boolean checkLogonHours(string logon_hours, string syntax, string time_correction, boolean default_access) ## @function boolean checkLogonHours(string logon_hours, string syntax, string time_correction, boolean default_access)
@ -72,6 +72,36 @@ sub checkLogonHours {
return substr( $base2_logon_hours, $hourpos, 1 ); return substr( $base2_logon_hours, $hourpos, 1 );
} }
## @function integer listMatch
# Test if a value is found in a collection
# @param $list Can be a hash, array or string including the separator
# @param $value string The value to search for
# @param $ignorecase boolean Be case insensitive
# @return 1 if the value was found, 0 else
# NOTE: this function is not exported directly in this module because we don't
# want the usr to have to worry about separator. Is is wrapped in a closure in
# Jail.pm
sub listMatch {
my ( $sep, $list, $value, $ignorecase ) = @_;
my $flags = $ignorecase ? 'i' : '';
my @a;
if ( ref($list) eq "ARRAY" ) {
@a = @{$list};
}
elsif ( ref($list) eq "HASH" ) {
@a = keys %{$list};
}
else {
@a = split $sep, $list;
}
if ( grep /(?$flags)^\Q$value\E$/, @a ) {
return 1;
}
else {
return 0;
}
}
## @function integer date ## @function integer date
# Get current local date # Get current local date
# @param $gmt optional boolean To return GMT date (default is local date) # @param $gmt optional boolean To return GMT date (default is local date)
@ -187,4 +217,11 @@ sub isInNet6 {
return net6( $ip, $bits ) eq net6( $net, $bits ) ? 1 : 0; return net6( $ip, $bits ) eq net6( $net, $bits ) ? 1 : 0;
} }
sub varIsInUri {
my ( $uri, $wanteduri, $attribute, $restricted ) = @_;
return $restricted
? $uri =~ /$wanteduri$attribute$/o
: $uri =~ /$wanteduri$attribute/o;
}
1; 1;

View File

@ -24,6 +24,13 @@ sub setTypes {
( $type eq 'global' ? 'SSO' : ucfirst($type) ); ( $type eq 'global' ? 'SSO' : ucfirst($type) );
} }
} }
my $offlinebackend = $self->{sessionTypes}->{oidc} ? 'oidc' : 'global';
$self->{sessionTypes}->{offline}->{module} =
$self->{sessionTypes}->{$offlinebackend}->{module};
$self->{sessionTypes}->{offline}->{options} =
$self->{sessionTypes}->{$offlinebackend}->{options};
$self->{sessionTypes}->{offline}->{kind} = "OIDCI";
} }
sub separator { sub separator {

View File

@ -0,0 +1,250 @@
#!/usr/bin/perl
#=============================================================================
# LemonLDAP::NG session conversion tool
#
# This script lets an administrator migrate existing sessions from one backend
# to another. It is mostly useful when run on persistant sessions, but it can be
# useful in some other cases too, such as OIDC Offline sessions
#
# This is part of LemonLDAP::NG product, released under GPL
#=============================================================================
use Lemonldap::NG::Common::Apache::Session;
use Lemonldap::NG::Common::Session;
use Config::IniFiles;
use strict;
use Getopt::Std;
$Getopt::Std::STANDARD_HELP_VERSION = 1;
our $VERSION = "2.0.6";
# Options
# -d: debug mode
# -c: configuration file
# -i: ignore errors
my $opts = {};
getopts( 'dic:', $opts );
my $debug = $opts->{d};
my $config_file = $opts->{c};
my $ignore_errors = $opts->{i};
my $nb_converted = 0;
my $nb_error = 0;
sub HELP_MESSAGE {
my $OUT = shift;
print $OUT <<END_MESSAGE;
$0 [-di] -c config_file.ini
-d Debug mode
-i Ignore errors
This script converts sessions in between the two backends specified in the configuration file
The configuration file must contain the following (adjust to your environment):
[sessions_from]
storageModule = Apache::Session::File
storageModuleOptions = { \\
'Directory' => '/var/lib/lemonldap-ng/sessions', \\
'LockDirectory' => '/var/lib/lemonldap-ng/sessions/lock', \\
}
# Only convert some session types
# sessionKind = Persistent, SSO
[sessions_to]
storageModule = Apache::Session::Browseable::Postgres
storageModuleOptions = { \\
'DataSource' => 'DBI:Pg:database=lemonldapdb;host=pg.example.com', \\
'UserName' => 'lemonldaplogin', \\
'Password' => 'lemonldappw', \\
'Commit' => 1, \\
'Index' => 'ipAddr _whatToTrace user', \\
'TableName' => 'sessions', \\
}
END_MESSAGE
}
unless ($config_file) {
HELP_MESSAGE( \*STDERR );
die "You must provide the -c option";
}
my $inicfg =
Config::IniFiles->new( -file => $config_file, -allowcontinue => 1 );
my $cfg = {};
die "Could not read configuration file" unless $inicfg;
for my $section (qw/sessions_from sessions_to/) {
die "Could not find section $section in configuration file $config_file"
unless $inicfg->SectionExists($section);
# Load section parameters
my $r;
foreach ( $inicfg->Parameters($section) ) {
$r->{$_} = $inicfg->val( $section, $_ );
# Remove spaces before and after value (#1488)
$r->{$_} =~ s/^\s*(.+?)\s*/$1/;
if ( $r->{$_} =~ /^[{\[].*[}\]]$/ || $r->{$_} =~ /^sub\s*{.*}$/ ) {
eval "\$r->{$_} = $r->{$_}";
if ($@) {
print $@;
return $r;
}
}
}
$cfg->{$section} = $r;
}
my $backendFrom;
my $backendTo;
my @sessionKindOnly;
if ( $cfg->{sessions_from}->{sessionKind} ) {
@sessionKindOnly = split /\W+/, $cfg->{sessions_from}->{sessionKind};
}
if ( $cfg->{sessions_from}->{storageModule} ) {
$backendFrom = $cfg->{sessions_from}->{storageModuleOptions};
$backendFrom->{backend} = $cfg->{sessions_from}->{storageModule};
}
else {
die
"[sessions_from] configuration section does not declare a storageModule";
}
if ( $cfg->{sessions_to}->{storageModule} ) {
$backendTo = $cfg->{sessions_to}->{storageModuleOptions};
$backendTo->{backend} = $cfg->{sessions_to}->{storageModule};
}
else {
die "[sessions_to] configuration section does not declare a storageModule";
}
Lemonldap::NG::Common::Apache::Session->get_key_from_all_sessions(
$backendFrom,
sub {
my $entry = shift;
my $id = shift;
print "Processing session $id\n" if $debug;
my $s = Lemonldap::NG::Common::Session->new( {
storageModule => $backendTo->{backend},
storageModuleOptions => $backendTo,
id => $id,
info => $entry,
force => 1,
}
);
# If filtering sessionKind
if (@sessionKindOnly) {
unless ( grep { $_ eq $entry->{_session_kind} } @sessionKindOnly ) {
return undef;
}
}
if ( $s->error ) {
die "Error encountered on session $id" unless $ignore_errors;
$nb_error += 1;
print "Error converting session $id : " . $s->error . "\n";
}
else {
print "Session $id successfully converted\n" if $debug;
$nb_converted += 1;
}
}
);
print "$nb_converted sessions have been converted\n";
print "$nb_error errors encountered during conversion\n" if $nb_error;
my $exit = $nb_error ? 1 : 0;
exit $exit;
__END__
=head1 NAME
=encoding utf8
convertSessions - A tool to convert Lemonldap::NG sessions between storage backends.
=head1 SYNOPSIS
convertSession [-di] -c parameters.ini
=head1 DESCRIPTION
convertConfig is a command line tool to migrate all sessions stored
in a source backend (sessions_from), into a new backend (sessions_to).
It requires a special configuration file in which you must list the source
and destination backend modules and parameters.
Sessions will not be deleted from the source backend. Existing sessions in the
destination backend will be kept, unless they have the same session ID as a
session in the source backend. In that case, the source will overwrite the
destination.
=head1 CONFIGURATION FILE FORMAT
The configuration file needs two sections to describe the source and destination backends
Here is an example
[sessions_from]
storageModule = Apache::Session::File
storageModuleOptions = { \
'Directory' => '/var/lib/lemonldap-ng/sessions', \
'LockDirectory' => '/var/lib/lemonldap-ng/sessions/lock', \
}
# Only migrate some session types
# sessionKind = Persistent, SSO
[sessions_to]
storageModule = Apache::Session::Browseable::Postgres
storageModuleOptions = { \
'DataSource' => 'DBI:Pg:database=lemonldapdb;host=pg.example.com', \
'UserName' => 'lemonldaplogin', \
'Password' => 'lemonldappw', \
'Commit' => 1, \
'Index' => 'ipAddr _whatToTrace user', \
'TableName' => 'sessions', \
}
The C<sessionKind> parameter may be used to filter only some session types.
Thanks to this, you can use this script to migrate from one database holding
all your sessions to separate tables from each session type.
=head1 SEE ALSO
L<http://lemonldap-ng.org/>
=head1 AUTHORS
=over
=item Maxime Besson, E<lt>maxime.besson@worteks.comE<gt>
=back
=head1 BUG REPORT
Use OW2 system to report bug or ask for features:
L<https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng/issues>
=head1 DOWNLOAD
Lemonldap::NG is available at
L<http://forge.objectweb.org/project/showfiles.php?group_id=274>

View File

@ -52,6 +52,7 @@ t/61-Lemonldap-NG-Handler-PSGI-Server.t
t/62-Lemonldap-NG-Handler-Nginx.t t/62-Lemonldap-NG-Handler-Nginx.t
t/63-Lemonldap-NG-Handler-PSGI-Try.t t/63-Lemonldap-NG-Handler-PSGI-Try.t
t/64-Lemonldap-NG-Handler-PSGI-DevOps.t t/64-Lemonldap-NG-Handler-PSGI-DevOps.t
t/65-Lemonldap-NG-Handler-Nginx-ServiceToken.t
t/65-Lemonldap-NG-Handler-PSGI-ServiceToken.t t/65-Lemonldap-NG-Handler-PSGI-ServiceToken.t
t/66-Lemonldap-NG-Handler-PSGI-wildcard.t t/66-Lemonldap-NG-Handler-PSGI-wildcard.t
t/67-Lemonldap-NG-Handler-PSGI-vhostoptions.t t/67-Lemonldap-NG-Handler-PSGI-vhostoptions.t
@ -63,3 +64,4 @@ 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

@ -37,6 +37,12 @@ my %builder = (
}, },
psgi => sub { psgi => sub {
return sub { return sub {
# Fix PATH_INFO when using Nginx with default uwsgi_params
# See #2031
( $_[0]->{PATH_INFO} ) =
$_[0]->{REQUEST_URI} =~ /^(?:\Q$_[0]->{SCRIPT_NAME}\E)?([^?]*)/;
my $script = $_[0]->{SCRIPT_FILENAME}; my $script = $_[0]->{SCRIPT_FILENAME};
return $_apps{$script}->(@_) if ( $_apps{$script} ); return $_apps{$script}->(@_) if ( $_apps{$script} );
$_apps{$script} = do $script; $_apps{$script} = do $script;
@ -44,7 +50,7 @@ my %builder = (
die "Unable to load $_[0]->{SCRIPT_FILENAME}"; die "Unable to load $_[0]->{SCRIPT_FILENAME}";
} }
return $_apps{$script}->(@_); return $_apps{$script}->(@_);
} }
}, },
); );

View File

@ -4,6 +4,60 @@ use strict;
our $VERSION = '2.1.0'; our $VERSION = '2.1.0';
sub retrieveSession {
my ( $class, $req, $id ) = @_;
my ($offlineId) = $id =~ /^O-(.*)/;
# Retrieve regular session if this is not an offline access token
unless ($offlineId) {
return $class->Lemonldap::NG::Handler::Main::retrieveSession( $req,
$id );
}
# 2. Get the session from cache or backend
my $session = $req->data->{session} = (
Lemonldap::NG::Common::Session->new( {
storageModule => $class->tsv->{oidcStorageModule},
storageModuleOptions => $class->tsv->{oidcStorageOptions},
cacheModule => $class->tsv->{sessionCacheModule},
cacheModuleOptions => $class->tsv->{sessionCacheOptions},
id => $offlineId,
kind => "OIDCI",
}
)
);
unless ( $session->error ) {
$class->data( $session->data );
$class->logger->debug("Get session $offlineId from Handler::Main::Run");
# Verify that session is valid
$class->logger->error(
"_utime is not defined. This should not happen. Check if it is well transmitted to handler"
) unless $session->data->{_utime};
my $ttl = $class->tsv->{timeout} - time + $session->data->{_utime};
$class->logger->debug( "Session TTL = " . $ttl );
if ( time - $session->data->{_utime} > $class->tsv->{timeout} ) {
$class->logger->info("Session $id expired");
# Clean cached data
$class->data( {} );
return 0;
}
return $session->data;
}
else {
$class->logger->info("Session $offlineId can't be retrieved");
$class->logger->info( $session->error );
return 0;
}
}
sub fetchId { sub fetchId {
my ( $class, $req ) = @_; my ( $class, $req ) = @_;
@ -21,10 +75,16 @@ 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 ( 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 ( my $_session_id = $infos->{offline_session_id} ) {
$class->logger->debug( 'Get offline session id ' . $_session_id );
return "O-$_session_id";
}
return $class->Lemonldap::NG::Handler::Main::fetchId($req); return $class->Lemonldap::NG::Handler::Main::fetchId($req);
} }
@ -50,7 +110,8 @@ sub getOIDCInfos {
unless ( $oidcSession->error ) { unless ( $oidcSession->error ) {
$class->logger->debug("Get OIDC session $id"); $class->logger->debug("Get OIDC session $id");
$infos->{user_session_id} = $oidcSession->data->{user_session_id}; $infos->{user_session_id} = $oidcSession->data->{user_session_id};
$infos->{offline_session_id} = $oidcSession->data->{offline_session_id};
} }
else { else {
$class->logger->info("OIDC Session $id can't be retrieved"); $class->logger->info("OIDC Session $id can't be retrieved");

View File

@ -56,10 +56,10 @@ sub fetchId {
return 0; return 0;
} }
# Send service headers if exist # Send service headers to protected application if exist
if (%serviceHeaders) { if (%serviceHeaders) {
$class->logger->debug("Append service header(s)..."); $class->logger->info("Append service header(s)...");
$class->set_header_out( $req, %serviceHeaders ); $class->set_header_in( $req, %serviceHeaders );
} }
return $_session_id; return $_session_id;

View File

@ -99,9 +99,10 @@ sub portalConsts {
'93' => 'PE_IMPERSONATION_SERVICE_NOT_ALLOWED', '93' => 'PE_IMPERSONATION_SERVICE_NOT_ALLOWED',
'94' => 'PE_ISSUERMISSINGREQATTR', '94' => 'PE_ISSUERMISSINGREQATTR',
'95' => 'PE_DECRYPTVALUE_SERVICE_NOT_ALLOWED', '95' => 'PE_DECRYPTVALUE_SERVICE_NOT_ALLOWED',
'96' => 'PE_RESETCERTIFICATE_INVALID', '96' => 'PE_BADOTP',
'97' => 'PE_RESETCERTIFICATE_FORMEMPTY', '97' => 'PE_RESETCERTIFICATE_FORMEMPTY',
'98' => 'PE_RESETCERTIFICATE_FIRSTACCESS' '98' => 'PE_RESETCERTIFICATE_FIRSTACCESS',
'99' => 'PE_RESETCERTIFICATE_INVALID'
}; };
} }

View File

@ -21,10 +21,11 @@ BEGIN {
} }
} }
has customFunctions => ( is => 'rw', isa => 'Maybe[Str]' ); has customFunctions => ( is => 'rw', isa => 'Maybe[Str]' );
has useSafeJail => ( is => 'rw', isa => 'Maybe[Int]' ); has useSafeJail => ( is => 'rw', isa => 'Maybe[Int]' );
has jail => ( is => 'rw' ); has multiValuesSeparator => ( is => 'rw', isa => 'Maybe[Str]' );
has error => ( is => 'rw' ); has jail => ( is => 'rw' );
has error => ( is => 'rw' );
our $VERSION = '2.1.0'; our $VERSION = '2.1.0';
our @builtCustomFunctions; our @builtCustomFunctions;
@ -88,7 +89,18 @@ sub build_jail {
$self->jail->share_from( 'Lemonldap::NG::Common::Safelib', $self->jail->share_from( 'Lemonldap::NG::Common::Safelib',
$Lemonldap::NG::Common::Safelib::functions ); $Lemonldap::NG::Common::Safelib::functions );
$self->jail->share_from( __PACKAGE__, [ @builtCustomFunctions, '&encrypt', '&token' ] ); # Closure for listMatch
{
no warnings 'redefine';
*listMatch = sub {
return Lemonldap::NG::Common::Safelib::listMatch(
$self->multiValuesSeparator, @_ );
};
}
$self->jail->share_from( __PACKAGE__,
[ @builtCustomFunctions, '&encrypt', '&token', '&listMatch' ] );
$self->jail->share_from( 'MIME::Base64', ['&encode_base64'] ); $self->jail->share_from( 'MIME::Base64', ['&encode_base64'] );
#$self->jail->share_from( 'Lemonldap::NG::Handler::Main', ['$_v'] ); #$self->jail->share_from( 'Lemonldap::NG::Handler::Main', ['$_v'] );

View File

@ -13,6 +13,7 @@ use Scalar::Util qw(weaken);
use constant UNPROTECT => 1; use constant UNPROTECT => 1;
use constant SKIP => 2; use constant SKIP => 2;
use constant MAYSKIP => 3;
our @_onReload; our @_onReload;
@ -179,8 +180,9 @@ sub jailInit {
my ( $class, $conf ) = @_; my ( $class, $conf ) = @_;
$class->tsv->{jail} = Lemonldap::NG::Handler::Main::Jail->new( { $class->tsv->{jail} = Lemonldap::NG::Handler::Main::Jail->new( {
useSafeJail => $conf->{useSafeJail}, useSafeJail => $conf->{useSafeJail},
customFunctions => $conf->{customFunctions}, customFunctions => $conf->{customFunctions},
multiValuesSeparator => $conf->{multiValuesSeparator},
} }
); );
$class->tsv->{jail}->build_jail( $class, $conf->{require} ); $class->tsv->{jail}->build_jail( $class, $conf->{require} );
@ -545,6 +547,9 @@ sub conditionSub {
); );
} }
my $mayskip = 0;
$mayskip = MAYSKIP if $cond =~ /\bskip\b/;
# Replace some strings in condition # Replace some strings in condition
$cond = $class->substitute($cond); $cond = $class->substitute($cond);
my $sub; my $sub;
@ -554,7 +559,7 @@ sub conditionSub {
} }
# Return sub and protected flag # Return sub and protected flag
return ( $sub, 0 ); return ( $sub, $mayskip );
} }
## @method arrayref aliasInit ## @method arrayref aliasInit
@ -589,6 +594,7 @@ sub substitute {
$expr =~ s/\$ENV\{/\$r->{env}->\{/g; $expr =~ s/\$ENV\{/\$r->{env}->\{/g;
$expr =~ s/\$env->\{/\$r->{env}->\{/g; $expr =~ s/\$env->\{/\$r->{env}->\{/g;
$expr =~ s/\$_rulematch\[/\$m->\[/g; $expr =~ s/\$_rulematch\[/\$m->\[/g;
$expr =~ s/\bskip\b/q\{999_SKIP\}/g;
return $expr; return $expr;
} }

View File

@ -192,6 +192,11 @@ sub run {
$class->cleanHeaders($req); $class->cleanHeaders($req);
return $class->OK; return $class->OK;
} }
elsif ( $protection == $class->MAYSKIP
and $class->grant( $req, $session, $uri, $cond ) eq '999_SKIP' )
{
return $class->OK;
}
else { else {
@ -441,7 +446,7 @@ sub fetchId {
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

@ -6,7 +6,7 @@
# change 'tests => 1' to 'tests => last_test_to_print'; # change 'tests => 1' to 'tests => last_test_to_print';
use strict; use strict;
use Test::More tests => 9; use Test::More tests => 13;
require 't/test.pm'; require 't/test.pm';
BEGIN { use_ok('Lemonldap::NG::Handler::Main::Jail') } BEGIN { use_ok('Lemonldap::NG::Handler::Main::Jail') }
@ -18,8 +18,9 @@ my $res;
ok( ok(
my $jail = Lemonldap::NG::Handler::Main::Jail->new( my $jail = Lemonldap::NG::Handler::Main::Jail->new(
'jail' => undef, 'jail' => undef,
'useSafeJail' => 1, 'useSafeJail' => 1,
'multiValuesSeparator' => '; ',
), ),
'new jail object' 'new jail object'
); );
@ -38,6 +39,18 @@ ok(
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) ) }";
my $code = $jail->jail_reval($sub);
ok( ( defined($code) and ref($code) eq 'CODE' ),
'listMatch function is defined' );
ok( &$code eq '1', 'Get good result' );
$sub = "sub { return ( listMatch('ABC; DEF; GHI','ab',1) ) }";
my $code = $jail->jail_reval($sub);
ok( ( defined($code) and ref($code) eq 'CODE' ),
'listMatch function is defined' );
ok( &$code eq '0', 'Get good result' );
$sub = "sub { return(checkDate('20000000000000','21000000000000')) }"; $sub = "sub { return(checkDate('20000000000000','21000000000000')) }";
$code = $jail->jail_reval($sub); $code = $jail->jail_reval($sub);
ok( ok(

View File

@ -5,7 +5,7 @@
# change 'tests => 1' to 'tests => last_test_to_print'; # change 'tests => 1' to 'tests => last_test_to_print';
use Test::More tests => 5; use Test::More tests => 9;
require 't/test.pm'; require 't/test.pm';
BEGIN { use_ok('Lemonldap::NG::Handler::Main::Jail') } BEGIN { use_ok('Lemonldap::NG::Handler::Main::Jail') }
@ -16,9 +16,10 @@ BEGIN { use_ok('Lemonldap::NG::Handler::Main::Jail') }
ok( ok(
my $jail = Lemonldap::NG::Handler::Main::Jail->new( my $jail = Lemonldap::NG::Handler::Main::Jail->new(
'jail' => undef, 'jail' => undef,
'useSafeJail' => 0, 'useSafeJail' => 0,
'customFunctions' => undef 'customFunctions' => undef,
'multiValuesSeparator' => '; ',
), ),
'new fake jail object' 'new fake jail object'
); );
@ -41,3 +42,15 @@ my $sub3 = "sub { return(checkDate('20000000000000','21000000000000')) }";
my $checkDate = $jail->jail_reval($sub3); my $checkDate = $jail->jail_reval($sub3);
ok( &$checkDate == "1", ok( &$checkDate == "1",
'checkDate extended function working without Safe Jail' ); 'checkDate extended function working without Safe Jail' );
my $sub4 = "sub { return ( listMatch('ABC; DEF; GHI','abc', 1) ) }";
my $listMatch = $jail->jail_reval($sub4);
ok( ( defined($listMatch) and ref($listMatch) eq 'CODE' ),
'listMatch function is defined' );
ok( &$listMatch eq '1', 'Get good result' );
my $sub5 = "sub { return ( listMatch('ABC; DEF; GHI','ab', 1) ) }";
my $listMatch = $jail->jail_reval($sub5);
ok( ( defined($listMatch) and ref($listMatch) eq 'CODE' ),
'listMatch function is defined' );
ok( &$listMatch eq '0', 'Get good result' );

View File

@ -8,6 +8,7 @@ require 't/test-psgi-lib.pm';
init('Lemonldap::NG::Handler::PSGI'); init('Lemonldap::NG::Handler::PSGI');
my $res; my $res;
my $SKIPUSER = 0;
# Unauthentified query # Unauthentified query
# -------------------- # --------------------
@ -38,7 +39,45 @@ count(2);
ok( $res = $client->_get( '/user_dwho/', undef, undef, "lemonldap=$sessionId" ), ok( $res = $client->_get( '/user_dwho/', undef, undef, "lemonldap=$sessionId" ),
'Regexp query' ); 'Regexp query' );
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res, 200 ); ok( $res->[0] == 200, 'Code is 200' ) or explain( $res, 200 );
count(2);
# Request an URI protected by custom function -> allowed
ok(
$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 );
count(2);
# Request an URI protected by custom function -> allowed
ok(
$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 );
count(2);
# Request an URI protected by custom function -> denied
ok(
$res =
$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);
# Request an URI protected by custom function -> denied
ok(
$res =
$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
@ -124,14 +163,25 @@ ok(
); );
count(3); count(3);
ok( $res = $client->_get( '/skipif/za', undef, 'test1.example.com' ),
'Test skip() rule 1' );
ok( $res->[0] == 302, 'Code is 302' ) or explain( $res, 302 );
$SKIPUSER = 1;
ok( $res = $client->_get( '/skipif/zz', undef, 'test1.example.com' ),
'Test skip() rule 2' );
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res, 200 );
count(4);
done_testing( count() ); done_testing( count() );
clean(); clean();
sub Lemonldap::NG::Handler::PSGI::handler { sub Lemonldap::NG::Handler::PSGI::handler {
my ( $self, $req ) = @_; my ( $self, $req ) = @_;
ok( $req->env->{HTTP_AUTH_USER} eq 'dwho', 'Header is given to app' ) unless ($SKIPUSER) {
or explain( $req->env->{HTTP_AUTH_USER}, 'dwho' ); ok( $req->env->{HTTP_AUTH_USER} eq 'dwho', 'Header is given to app' )
count(1); or explain( $req->env->{HTTP_AUTH_USER}, 'dwho' );
count(1);
}
return [ 200, [ 'Content-Type', 'text/plain' ], ['Hello'] ]; return [ 200, [ 'Content-Type', 'text/plain' ], ['Hello'] ];
} }

View File

@ -40,6 +40,36 @@ ok( $h{'Auth-User'} eq 'dwho', 'Header Auth-User is set to "dwho"' )
or explain( \%h, 'Auth-User => "dwho"' ); or explain( \%h, 'Auth-User => "dwho"' );
count(1); count(1);
# Request an URI protected by custom function -> allowed
ok( $res = $client->_get( '/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);
# Request an URI protected by custom function -> denied
ok( $res = $client->_get( '/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);
# Request an URI protected by custom function -> allowed
ok( $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 );
count(2);
# Request an URI protected by custom function -> denied
ok( $res = $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);
# Request an URI protected by custom function -> denied
ok( $res = $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);
# Denied query # Denied query
ok( $res = $client->_get( '/deny', undef, undef, "lemonldap=$sessionId" ), ok( $res = $client->_get( '/deny', undef, undef, "lemonldap=$sessionId" ),
'Denied query' ); 'Denied query' );

View File

@ -44,6 +44,30 @@ ok( $h{'Headervalue1'} eq 'dwho', 'Headervalue1 is set to "dwho"' )
or explain( \%h, 'Headervalue1 => "dwho"' ); or explain( \%h, 'Headervalue1 => "dwho"' );
count(2); count(2);
# Request an URI protected by custom function -> allowed
ok( $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 );
count(2);
# Request an URI protected by custom function -> allowed
ok( $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 );
count(2);
# Request an URI protected by custom function -> denied
ok( $res = $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);
# Request an URI protected by custom function -> denied
ok( $res = $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);
# Denied query # Denied query
ok( $res = $client->_get( '/deny', undef, undef, "lemonldap=$sessionId" ), ok( $res = $client->_get( '/deny', undef, undef, "lemonldap=$sessionId" ),
'Denied query' ); 'Denied query' );

View File

@ -0,0 +1,191 @@
use Test::More;
BEGIN {
require 't/test-psgi-lib.pm';
}
init(
'Lemonldap::NG::Handler::Server::Nginx',
{
logLevel => 'error',
handlerServiceTokenTTL => 120,
vhostOptions => {
'test1.example.com' => {
vhostHttps => 0,
vhostPort => 80,
vhostMaintenance => 0,
vhostServiceTokenTTL => 180,
},
'test2.example.com' => {
vhostHttps => 0,
vhostPort => 80,
vhostMaintenance => 0,
vhostServiceTokenTTL => 300,
}
},
exportedHeaders => {
'test2.example.com' => {
'Auth-User' => '$uid',
'empty' => undef,
'zero' => "'0'",
},
}
}
);
my $res;
my $crypt = Lemonldap::NG::Common::Crypto->new('qwertyui');
my $token = $crypt->encrypt(
join ':', time,
$sessionId, 'test1.example.com',
'XFromVH=app1-auth.example.com', "serviceHeader1=$sessionId",
"serviceHeader2=$sessionId", 'test2.example.com',
'*.example.com'
);
ok(
$res = $client->_get(
'/', undef, 'test1.example.com', "lemonldap=$sessionId",
VHOSTTYPE => 'ServiceToken',
'HTTP_X_LLNG_TOKEN' => $token,
),
'Query with token 1'
);
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res->[0], 200 );
count(2);
my %headers = @{ $res->[1] };
ok( $header{cookie} eq '', 'NO cookie found' )
or print STDERR Data::Dumper::Dumper( \%headers );
my @headers = grep { /^serviceHeader\d$|^XFromVH$/ } @{ $res->[1] };
my @values = grep { /\.example\.com|^$sessionId$/ } @{ $res->[1] };
ok( @headers == 6, 'Found 6 service headers' )
or print STDERR Data::Dumper::Dumper( $res->[1] );
ok( @values == 6, 'Found 6 service header values' )
or print STDERR Data::Dumper::Dumper( $res->[1] );
count(3);
# Waiting
Time::Fake->offset("+90s");
ok(
$res = $client->_get(
'/', undef, 'test1.example.com', undef,
VHOSTTYPE => 'ServiceToken',
'HTTP_X_LLNG_TOKEN' => $token,
),
'Query with token 2'
);
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res->[0], 200 );
count(2);
@headers = grep { /^serviceHeader\d$|^XFromVH$/ } @{ $res->[1] };
@values = grep { /\.example\.com|^$sessionId$/ } @{ $res->[1] };
ok( @headers == 6, 'Found 6 service headers' )
or print STDERR Data::Dumper::Dumper($res);
ok( @values == 6, 'Found 6 service header values' )
or print STDERR Data::Dumper::Dumper( $res->[1] );
count(2);
# Waiting
Time::Fake->offset("+210s");
ok(
$res = $client->_get(
'/', undef, 'test1.example.com', undef,
VHOSTTYPE => 'ServiceToken',
'HTTP_X_LLNG_TOKEN' => $token,
),
'Query with token 3'
);
ok( $res->[0] == 401, 'Code is 401' ) or explain( $res->[0], 401 );
count(2);
@headers = grep { /^serviceHeader\d$|^XFromVH$/ } @{ $res->[1] };
ok( @headers == 0, 'NONE service header found' )
or print STDERR Data::Dumper::Dumper( $res->[1] );
count(1);
# Waiting
Time::Fake->offset("+270s");
ok(
$res = $client->_get(
'/', undef, 'test2.example.com', undef,
VHOSTTYPE => 'ServiceToken',
'HTTP_X_LLNG_TOKEN' => $token,
),
'Query with token 4'
);
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res->[0], 200 );
count(2);
my %headers = @{ $res->[1] };
ok( $headers{'zero'} eq '0', 'Found "zero" header with "0"' )
or print STDERR Data::Dumper::Dumper( $res->[1] );
ok( $headers{'empty'} eq '', 'Found "empty" header without value' )
or print STDERR Data::Dumper::Dumper( $res->[1] );
count(2);
@headers = grep { /^serviceHeader\d$|^XFromVH$/ } @{ $res->[1] };
@values = grep { /\.example\.com|^$sessionId$/ } @{ $res->[1] };
ok( @headers == 6, 'Found 6 service headers' )
or print STDERR Data::Dumper::Dumper( $res->[1] );
ok( @values == 6, 'Found 6 service header values' )
or print STDERR Data::Dumper::Dumper( $res->[1] );
count(2);
# Waiting
Time::Fake->offset("+330s");
ok(
$res = $client->_get(
'/', undef, 'test2.example.com', undef,
VHOSTTYPE => 'ServiceToken',
'HTTP_X_LLNG_TOKEN' => $token,
),
'Query with token 5'
);
ok( $res->[0] == 401, 'Code is 401' ) or explain( $res->[0], 401 );
count(2);
@headers = grep { /^serviceHeader\d$|^XFromVH$/ } @{ $res->[1] };
ok( @headers == 0, 'NONE service header found' )
or print STDERR Data::Dumper::Dumper( $res->[1] );
count(1);
ok(
$res = $client->_get(
'/', undef, 'test3.example.com', undef,
VHOSTTYPE => 'ServiceToken',
'HTTP_X_LLNG_TOKEN' => $token,
),
'Query with token 6'
);
ok( $res->[0] == 401, 'Code is 401' ) or explain( $res->[0], 401 );
count(2);
@headers = grep { /^serviceHeader\d$|^XFromVH$/ } @{ $res->[1] };
ok( @headers == 0, 'NONE service header found' )
or print STDERR Data::Dumper::Dumper( $res->[1] );
count(1);
$token = $crypt->encrypt( join ':', time, $sessionId );
ok(
$res = $client->_get(
'/', undef, 'test2.example.com', undef,
VHOSTTYPE => 'ServiceToken',
'HTTP_X_LLNG_TOKEN' => $token,
),
'Query with token 7'
);
ok( $res->[0] == 401, 'Code is 401' ) or explain( $res->[0], 401 );
count(2);
@headers = grep { /^serviceHeader\d$|^XFromVH$/ } @{ $res->[1] };
ok( @headers == 0, 'NONE service header found' )
or print STDERR Data::Dumper::Dumper( $res->[1] );
count(1);
done_testing( count() );
clean();

View File

@ -8,19 +8,19 @@ init(
'Lemonldap::NG::Handler::Server', 'Lemonldap::NG::Handler::Server',
{ {
logLevel => 'error', logLevel => 'error',
handlerServiceTokenTTL => 2, handlerServiceTokenTTL => 120,
vhostOptions => { vhostOptions => {
'test1.example.com' => { 'test1.example.com' => {
vhostHttps => 0, vhostHttps => 0,
vhostPort => 80, vhostPort => 80,
vhostMaintenance => 0, vhostMaintenance => 0,
vhostServiceTokenTTL => 3, vhostServiceTokenTTL => 180,
}, },
'test2.example.com' => { 'test2.example.com' => {
vhostHttps => 0, vhostHttps => 0,
vhostPort => 80, vhostPort => 80,
vhostMaintenance => 0, vhostMaintenance => 0,
vhostServiceTokenTTL => 5, vhostServiceTokenTTL => 300,
} }
}, },
exportedHeaders => { exportedHeaders => {
@ -38,7 +38,7 @@ 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", 'XFromVH=app1-auth.example.com', "serviceHeader1=$sessionId","serviceHeader2=$sessionId",
'test2.example.com', '*.example.com' 'test2.example.com', '*.example.com'
); );
@ -48,21 +48,21 @@ ok(
VHOSTTYPE => 'ServiceToken', VHOSTTYPE => 'ServiceToken',
'HTTP_X_LLNG_TOKEN' => $token, 'HTTP_X_LLNG_TOKEN' => $token,
), ),
'Query with token' 'Query with token 1'
); );
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 = grep { /service|^XFromVH$/ } @{ $res->[1] }; my @headers = grep { /^serviceHeader\d$|^XFromVH$/ } @{ $res->[1] };
my @values = grep { /\.example\.com|^$sessionId$/ } @{ $res->[1] }; my @values = grep { /\.example\.com|^$sessionId$/ } @{ $res->[1] };
ok( @headers == 2, 'Found 2 service headers' ) ok( @headers == 3, 'Found 3 service headers' )
or print STDERR Data::Dumper::Dumper( $res->[1] ); or print STDERR Data::Dumper::Dumper( $res->[1] );
ok( @values == 2, 'Found 2 service header values' ) ok( @values == 3, 'Found 3 service header values' )
or print STDERR Data::Dumper::Dumper( $res->[1] ); or print STDERR Data::Dumper::Dumper( $res->[1] );
count(2); count(2);
diag 'Waiting'; # Waiting
sleep 1; Time::Fake->offset("+90s");
ok( ok(
$res = $client->_get( $res = $client->_get(
@ -70,21 +70,21 @@ ok(
VHOSTTYPE => 'ServiceToken', VHOSTTYPE => 'ServiceToken',
'HTTP_X_LLNG_TOKEN' => $token, 'HTTP_X_LLNG_TOKEN' => $token,
), ),
'Query with token' 'Query with token 2'
); );
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);
@headers = grep { /service|^XFromVH$/ } @{ $res->[1] }; @headers = grep { /^serviceHeader\d$|^XFromVH$/ } @{ $res->[1] };
@values = grep { /\.example\.com|^$sessionId$/ } @{ $res->[1] }; @values = grep { /\.example\.com|^$sessionId$/ } @{ $res->[1] };
ok( @headers == 2, 'Found 2 service headers' ) ok( @headers == 3, 'Found 3 service headers' )
or print STDERR Data::Dumper::Dumper( $res->[1] ); or print STDERR Data::Dumper::Dumper( $res->[1] );
ok( @values == 2, 'Found 2 service header values' ) ok( @values == 3, 'Found 3 service header values' )
or print STDERR Data::Dumper::Dumper( $res->[1] ); or print STDERR Data::Dumper::Dumper( $res->[1] );
count(2); count(2);
diag 'Waiting'; # Waiting
sleep 2; Time::Fake->offset("+210s");
ok( ok(
$res = $client->_get( $res = $client->_get(
@ -92,18 +92,18 @@ ok(
VHOSTTYPE => 'ServiceToken', VHOSTTYPE => 'ServiceToken',
'HTTP_X_LLNG_TOKEN' => $token, 'HTTP_X_LLNG_TOKEN' => $token,
), ),
'Query with token' 'Query with token 3'
); );
ok( $res->[0] == 302, 'Code is 200' ) or explain( $res->[0], 302 ); ok( $res->[0] == 302, 'Code is 302' ) or explain( $res->[0], 302 );
count(2); count(2);
@headers = grep { /service|^XFromVH$/ } @{ $res->[1] }; @headers = grep { /^serviceHeader\d$|^XFromVH$/ } @{ $res->[1] };
ok( @headers == 0, 'NONE service header found' ) ok( @headers == 0, 'NONE service header found' )
or print STDERR Data::Dumper::Dumper( $res->[1] ); or print STDERR Data::Dumper::Dumper( $res->[1] );
count(1); count(1);
diag 'Waiting'; # Waiting
sleep 1; Time::Fake->offset("+270s");
ok( ok(
$res = $client->_get( $res = $client->_get(
@ -111,7 +111,7 @@ ok(
VHOSTTYPE => 'ServiceToken', VHOSTTYPE => 'ServiceToken',
'HTTP_X_LLNG_TOKEN' => $token, 'HTTP_X_LLNG_TOKEN' => $token,
), ),
'Query with token' 'Query with token 4'
); );
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);
@ -123,16 +123,16 @@ ok( $headers{'empty'} eq '', 'Found "empty" header without value' )
or print STDERR Data::Dumper::Dumper( $res->[1] ); or print STDERR Data::Dumper::Dumper( $res->[1] );
count(2); count(2);
@headers = grep { /service|^XFromVH$/ } @{ $res->[1] }; @headers = grep { /^serviceHeader\d$|^XFromVH$/ } @{ $res->[1] };
@values = grep { /\.example\.com|^$sessionId$/ } @{ $res->[1] }; @values = grep { /\.example\.com|^$sessionId$/ } @{ $res->[1] };
ok( @headers == 2, 'Found 2 service headers' ) ok( @headers == 3, 'Found 3 service headers' )
or print STDERR Data::Dumper::Dumper( $res->[1] ); or print STDERR Data::Dumper::Dumper( $res->[1] );
ok( @values == 2, 'Found 2 service header values' ) ok( @values == 3, 'Found 3 service header values' )
or print STDERR Data::Dumper::Dumper( $res->[1] ); or print STDERR Data::Dumper::Dumper( $res->[1] );
count(2); count(2);
diag 'Waiting'; # Waiting
sleep 1; Time::Fake->offset("+330s");
ok( ok(
$res = $client->_get( $res = $client->_get(
@ -140,12 +140,12 @@ ok(
VHOSTTYPE => 'ServiceToken', VHOSTTYPE => 'ServiceToken',
'HTTP_X_LLNG_TOKEN' => $token, 'HTTP_X_LLNG_TOKEN' => $token,
), ),
'Query with token' 'Query with token 5'
); );
ok( $res->[0] == 302, 'Code is 302' ) or explain( $res->[0], 302 ); ok( $res->[0] == 302, 'Code is 302' ) or explain( $res->[0], 302 );
count(2); count(2);
@headers = grep { /service|^XFromVH$/ } @{ $res->[1] }; @headers = grep { /^serviceHeader\d$|^XFromVH$/ } @{ $res->[1] };
ok( @headers == 0, 'NONE service header found' ) ok( @headers == 0, 'NONE service header found' )
or print STDERR Data::Dumper::Dumper( $res->[1] ); or print STDERR Data::Dumper::Dumper( $res->[1] );
count(1); count(1);
@ -156,12 +156,12 @@ ok(
VHOSTTYPE => 'ServiceToken', VHOSTTYPE => 'ServiceToken',
'HTTP_X_LLNG_TOKEN' => $token, 'HTTP_X_LLNG_TOKEN' => $token,
), ),
'Query with token' 'Query with token 6'
); );
ok( $res->[0] == 302, 'Code is 302' ) or explain( $res->[0], 302 ); ok( $res->[0] == 302, 'Code is 302' ) or explain( $res->[0], 302 );
count(2); count(2);
@headers = grep { /service|^XFromVH$/ } @{ $res->[1] }; @headers = grep { /^serviceHeader\d$|^XFromVH$/ } @{ $res->[1] };
ok( @headers == 0, 'NONE service header found' ) ok( @headers == 0, 'NONE service header found' )
or print STDERR Data::Dumper::Dumper( $res->[1] ); or print STDERR Data::Dumper::Dumper( $res->[1] );
count(1); count(1);
@ -173,12 +173,12 @@ ok(
VHOSTTYPE => 'ServiceToken', VHOSTTYPE => 'ServiceToken',
'HTTP_X_LLNG_TOKEN' => $token, 'HTTP_X_LLNG_TOKEN' => $token,
), ),
'Query with token' 'Query with token 7'
); );
ok( $res->[0] == 302, 'Code is 302' ) or explain( $res->[0], 302 ); ok( $res->[0] == 302, 'Code is 302' ) or explain( $res->[0], 302 );
count(2); count(2);
@headers = grep { /service|^XFromVH$/ } @{ $res->[1] }; @headers = grep { /^serviceHeader\d$|^XFromVH$/ } @{ $res->[1] };
ok( @headers == 0, 'NONE service header found' ) ok( @headers == 0, 'NONE service header found' )
or print STDERR Data::Dumper::Dumper( $res->[1] ); or print STDERR Data::Dumper::Dumper( $res->[1] );
count(1); count(1);

View File

@ -11,6 +11,11 @@ SKIP: {
if ($@) { if ($@) {
skip 'Cache::Memcached not found', $maintests; skip 'Cache::Memcached not found', $maintests;
} }
my $testmemd = new Cache::Memcached { 'servers' => ["127.0.0.1:11211"] };
unless ( $testmemd->stats->{hosts} ) {
skip 'Memcached not started', $maintests;
}
eval { require Apache::Session::Generate::MD5; }; eval { require Apache::Session::Generate::MD5; };
if ($@) { if ($@) {
skip 'Apache::Session::Generate::MD5 not found', $maintests; skip 'Apache::Session::Generate::MD5 not found', $maintests;

View File

@ -0,0 +1,235 @@
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

@ -43,6 +43,10 @@
"test1.example.com": { "test1.example.com": {
"^/AuthStrong(?#AuthnLevel=5)": "accept", "^/AuthStrong(?#AuthnLevel=5)": "accept",
"^/AuthWeak(?#AuthnLevel=1)": "accept", "^/AuthWeak(?#AuthnLevel=1)": "accept",
"^/test-uri1": "varIsInUri($ENV{REQUEST_URI}, '/test-uri1/', $uid, 1)",
"^/test-uri2": "varIsInUri($ENV{REQUEST_URI}, '/test-uri2/', $uid)",
"^/test-restricted_uri": "varIsInUri($ENV{REQUEST_URI}, '/test-restricted_uri/', \"$uid/\", 1)",
"^/skipif": "$ENV{REQUEST_URI} =~ /zz/ ? skip : 1",
"^/logout": "logout_sso", "^/logout": "logout_sso",
"^/deny": "deny", "^/deny": "deny",
"^/user_(\\w+)/": "$uid eq $_rulematch[1]", "^/user_(\\w+)/": "$uid eq $_rulematch[1]",

View File

@ -9,6 +9,10 @@ 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

@ -183,6 +183,7 @@ site/htdocs/static/languages/de.json
site/htdocs/static/languages/en.json site/htdocs/static/languages/en.json
site/htdocs/static/languages/fr.json site/htdocs/static/languages/fr.json
site/htdocs/static/languages/it.json site/htdocs/static/languages/it.json
site/htdocs/static/languages/tr.json
site/htdocs/static/languages/vi.json site/htdocs/static/languages/vi.json
site/htdocs/static/languages/zh.json site/htdocs/static/languages/zh.json
site/htdocs/static/logos/ar.png site/htdocs/static/logos/ar.png

View File

@ -1070,6 +1070,12 @@ qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.]
'customPassword' => { 'customPassword' => {
'type' => 'text' 'type' => 'text'
}, },
'customPlugins' => {
'type' => 'text'
},
'customPluginsParams' => {
'type' => 'keyTextContainer'
},
'customRegister' => { 'customRegister' => {
'type' => 'text' 'type' => 'text'
}, },
@ -1264,6 +1270,14 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
'default' => 120, 'default' => 120,
'type' => 'int' 'type' => 'int'
}, },
'globalLogoutRule' => {
'default' => 0,
'type' => 'boolOrExpr'
},
'globalLogoutTimer' => {
'default' => 1,
'type' => 'bool'
},
'globalStorage' => { 'globalStorage' => {
'default' => 'Apache::Session::File', 'default' => 'Apache::Session::File',
'type' => 'PerlModule' 'type' => 'PerlModule'
@ -1795,7 +1809,7 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
'managerDn' => { 'managerDn' => {
'default' => '', 'default' => '',
'msgFail' => '__badValue__', 'msgFail' => '__badValue__',
'test' => qr/^(?:\w+=.*)?$/, 'test' => qr/^.*$/,
'type' => 'text' 'type' => 'text'
}, },
'managerPassword' => { 'managerPassword' => {
@ -1835,6 +1849,10 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
'default' => 0, 'default' => 0,
'type' => 'bool' 'type' => 'bool'
}, },
'notificationDefaultCond' => {
'default' => '',
'type' => 'text'
},
'notificationServer' => { 'notificationServer' => {
'default' => 0, 'default' => 0,
'type' => 'bool' 'type' => 'bool'
@ -2021,8 +2039,14 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
'type' => 'subContainer' 'type' => 'subContainer'
}, },
'oidcRPMetaDataOptionsAccessTokenExpiration' => { 'oidcRPMetaDataOptionsAccessTokenExpiration' => {
'default' => 3600, 'type' => 'int'
'type' => 'int' },
'oidcRPMetaDataOptionsAllowOffline' => {
'default' => 0,
'type' => 'bool'
},
'oidcRPMetaDataOptionsAuthorizationCodeExpiration' => {
'type' => 'int'
}, },
'oidcRPMetaDataOptionsBypassConsent' => { 'oidcRPMetaDataOptionsBypassConsent' => {
'default' => 0, 'default' => 0,
@ -2045,8 +2069,10 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
'type' => 'text' 'type' => 'text'
}, },
'oidcRPMetaDataOptionsIDTokenExpiration' => { 'oidcRPMetaDataOptionsIDTokenExpiration' => {
'default' => 3600, 'type' => 'int'
'type' => 'int' },
'oidcRPMetaDataOptionsIDTokenForceClaims' => {
'type' => 'bool'
}, },
'oidcRPMetaDataOptionsIDTokenSignAlg' => { 'oidcRPMetaDataOptionsIDTokenSignAlg' => {
'default' => 'HS512', 'default' => 'HS512',
@ -2101,6 +2127,9 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
'oidcRPMetaDataOptionsLogoutUrl' => { 'oidcRPMetaDataOptionsLogoutUrl' => {
'type' => 'url' 'type' => 'url'
}, },
'oidcRPMetaDataOptionsOfflineSessionExpiration' => {
'type' => 'int'
},
'oidcRPMetaDataOptionsPostLogoutRedirectUris' => { 'oidcRPMetaDataOptionsPostLogoutRedirectUris' => {
'type' => 'text' 'type' => 'text'
}, },
@ -2111,6 +2140,10 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
'oidcRPMetaDataOptionsRedirectUris' => { 'oidcRPMetaDataOptionsRedirectUris' => {
'type' => 'text' 'type' => 'text'
}, },
'oidcRPMetaDataOptionsRefreshToken' => {
'default' => 0,
'type' => 'bool'
},
'oidcRPMetaDataOptionsRequirePKCE' => { 'oidcRPMetaDataOptionsRequirePKCE' => {
'default' => 0, 'default' => 0,
'type' => 'bool' 'type' => 'bool'
@ -2128,6 +2161,10 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
'default' => 600, 'default' => 600,
'type' => 'int' 'type' => 'int'
}, },
'oidcServiceAccessTokenExpiration' => {
'default' => 3600,
'type' => 'int'
},
'oidcServiceAllowAuthorizationCodeFlow' => { 'oidcServiceAllowAuthorizationCodeFlow' => {
'default' => 1, 'default' => 1,
'type' => 'bool' 'type' => 'bool'
@ -2144,6 +2181,14 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
'default' => 0, 'default' => 0,
'type' => 'bool' 'type' => 'bool'
}, },
'oidcServiceAuthorizationCodeExpiration' => {
'default' => 60,
'type' => 'int'
},
'oidcServiceIDTokenExpiration' => {
'default' => 3600,
'type' => 'int'
},
'oidcServiceKeyIdSig' => { 'oidcServiceKeyIdSig' => {
'type' => 'text' 'type' => 'text'
}, },
@ -2198,6 +2243,10 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
'default' => 'userinfo', 'default' => 'userinfo',
'type' => 'text' 'type' => 'text'
}, },
'oidcServiceOfflineSessionExpiration' => {
'default' => 2592000,
'type' => 'int'
},
'oidcServicePrivateKeySig' => { 'oidcServicePrivateKeySig' => {
'type' => 'RSAPrivateKey' 'type' => 'RSAPrivateKey'
}, },
@ -2588,6 +2637,9 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
], ],
'type' => 'select' 'type' => 'select'
}, },
'refreshSessions' => {
'type' => 'bool'
},
'registerConfirmSubject' => { 'registerConfirmSubject' => {
'type' => 'text' 'type' => 'text'
}, },

View File

@ -450,9 +450,10 @@ sub attributes {
flags => 'p', flags => 'p',
}, },
checkUserSearchAttributes => { checkUserSearchAttributes => {
type => 'text', type => 'text',
documentation => 'Attributes used for retrieving sessions in user DataBase', documentation =>
flags => 'p', 'Attributes used for retrieving sessions in user DataBase',
flags => 'p',
}, },
checkUserDisplayPersistentInfo => { checkUserDisplayPersistentInfo => {
default => 0, default => 0,
@ -466,6 +467,18 @@ sub attributes {
documentation => 'Display session empty values', documentation => 'Display session empty values',
flags => 'p', flags => 'p',
}, },
globalLogoutRule => {
type => 'boolOrExpr',
default => 0,
documentation => 'Global logout activation rule',
flags => 'p',
},
globalLogoutTimer => {
default => 1,
type => 'bool',
documentation => 'Global logout auto accept time',
flags => 'p',
},
favAppsMaxNumber => { favAppsMaxNumber => {
default => 3, default => 3,
type => 'int', type => 'int',
@ -553,10 +566,14 @@ sub attributes {
documentation => documentation =>
'Avoid asking confirmation when an Issuer asks to renew auth', 'Avoid asking confirmation when an Issuer asks to renew auth',
}, },
refreshSessions => {
type => 'bool',
documentation => 'Refresh sessions plugin',
},
forceGlobalStorageIssuerOTT => { forceGlobalStorageIssuerOTT => {
type => 'bool', type => 'bool',
documentation => documentation =>
'Force Issuer tokens be stored into Global Storage', 'Force Issuer tokens to be stored into Global Storage',
}, },
handlerInternalCache => { handlerInternalCache => {
type => 'int', type => 'int',
@ -1110,6 +1127,11 @@ sub attributes {
type => 'bool', type => 'bool',
documentation => 'Notification server activation', documentation => 'Notification server activation',
}, },
notificationDefaultCond => {
type => 'text',
default => '',
documentation => 'Notification default condition',
},
notificationServerGET => { notificationServerGET => {
default => 0, default => 0,
type => 'bool', type => 'bool',
@ -2042,15 +2064,15 @@ sub attributes {
vhostType => { vhostType => {
type => 'select', type => 'select',
select => [ select => [
{ k => 'AuthBasic', v => 'AuthBasic' }, { k => 'AuthBasic', v => 'AuthBasic' },
{ k => 'CDA', v => 'CDA' }, { k => 'CDA', v => 'CDA' },
{ k => 'DevOps', v => 'DevOps' }, { k => 'DevOps', v => 'DevOps' },
{ k => 'DevOpsST', v => 'DevOpsST' }, { k => 'DevOpsST', v => 'DevOpsST' },
{ k => 'Main', v => 'Main' }, { k => 'Main', v => 'Main' },
{ k => 'OAuth2', v => 'OAuth2' }, { k => 'OAuth2', v => 'OAuth2' },
{ k => 'SecureToken', v => 'SecureToken' }, { k => 'SecureToken', v => 'SecureToken' },
{ k => 'ServiceToken', v => 'ServiceToken' }, { k => 'ServiceToken', v => 'ServiceToken' },
{ k => 'ZimbraPreAuth',v => 'ZimbraPreAuth' }, { k => 'ZimbraPreAuth', v => 'ZimbraPreAuth' },
], ],
default => 'Main', default => 'Main',
documentation => 'Handler type', documentation => 'Handler type',
@ -2925,7 +2947,7 @@ sub attributes {
# LDAP # LDAP
managerDn => { managerDn => {
type => 'text', type => 'text',
test => qr/^(?:\w+=.*)?$/, test => qr/^.*$/,
msgFail => '__badValue__', msgFail => '__badValue__',
default => '', default => '',
documentation => 'LDAP manager DN', documentation => 'LDAP manager DN',
@ -3573,6 +3595,16 @@ m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
documentation => 'Custom additional parameters', documentation => 'Custom additional parameters',
}, },
# Custom plugins
customPlugins => {
type => 'text',
documentation => 'Custom plugins',
},
customPluginsParams => {
type => 'keyTextContainer',
documentation => 'Custom plugins parameters',
},
# OpenID Connect auth params # OpenID Connect auth params
oidcAuthnLevel => { oidcAuthnLevel => {
type => 'int', type => 'int',
@ -3679,6 +3711,26 @@ m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
default => 0, default => 0,
documentation => 'OpenID Connect allow hybrid flow', documentation => 'OpenID Connect allow hybrid flow',
}, },
oidcServiceAuthorizationCodeExpiration => {
type => 'int',
default => 60,
documentation => 'OpenID Connect global code TTL',
},
oidcServiceAccessTokenExpiration => {
type => 'int',
default => 3600,
documentation => 'OpenID Connect global access token TTL',
},
oidcServiceIDTokenExpiration => {
type => 'int',
default => 3600,
documentation => 'OpenID Connect global ID token TTL',
},
oidcServiceOfflineSessionExpiration => {
type => 'int',
default => 2592000,
documentation => 'OpenID Connect global offline session TTL',
},
oidcStorage => { oidcStorage => {
type => 'PerlModule', type => 'PerlModule',
documentation => 'Apache::Session module to store OIDC user data', documentation => 'Apache::Session module to store OIDC user data',
@ -3779,11 +3831,12 @@ m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
], ],
default => 'HS512', default => 'HS512',
}, },
oidcRPMetaDataOptionsIDTokenExpiration => oidcRPMetaDataOptionsIDTokenExpiration => { type => 'int' },
{ type => 'int', default => 3600 }, oidcRPMetaDataOptionsIDTokenForceClaims => { type => 'bool' },
oidcRPMetaDataOptionsAccessTokenExpiration => oidcRPMetaDataOptionsAccessTokenExpiration => { type => 'int' },
{ type => 'int', default => 3600 }, oidcRPMetaDataOptionsAuthorizationCodeExpiration => { type => 'int' },
oidcRPMetaDataOptionsRedirectUris => { type => 'text', }, oidcRPMetaDataOptionsOfflineSessionExpiration => { type => 'int' },
oidcRPMetaDataOptionsRedirectUris => { type => 'text', },
oidcRPMetaDataOptionsExtraClaims => oidcRPMetaDataOptionsExtraClaims =>
{ type => 'keyTextContainer', default => {} }, { type => 'keyTextContainer', default => {} },
oidcRPMetaDataOptionsBypassConsent => { oidcRPMetaDataOptionsBypassConsent => {
@ -3820,6 +3873,16 @@ m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
default => 0, default => 0,
documentation => 'Require PKCE', documentation => 'Require PKCE',
}, },
oidcRPMetaDataOptionsAllowOffline => {
type => 'bool',
default => 0,
documentation => 'Allow offline access',
},
oidcRPMetaDataOptionsRefreshToken => {
type => 'bool',
default => 0,
documentation => 'Issue refresh tokens',
},
oidcRPMetaDataOptionsRule => { oidcRPMetaDataOptionsRule => {
type => 'text', type => 'text',
test => sub { return perlExpr(@_) }, test => sub { return perlExpr(@_) },

View File

@ -203,7 +203,12 @@ sub cTrees {
'oidcRPMetaDataOptionsUserIDAttr', 'oidcRPMetaDataOptionsUserIDAttr',
'oidcRPMetaDataOptionsIDTokenSignAlg', 'oidcRPMetaDataOptionsIDTokenSignAlg',
'oidcRPMetaDataOptionsIDTokenExpiration', 'oidcRPMetaDataOptionsIDTokenExpiration',
'oidcRPMetaDataOptionsIDTokenForceClaims',
'oidcRPMetaDataOptionsAccessTokenExpiration', 'oidcRPMetaDataOptionsAccessTokenExpiration',
'oidcRPMetaDataOptionsAuthorizationCodeExpiration',
'oidcRPMetaDataOptionsAllowOffline',
'oidcRPMetaDataOptionsRefreshToken',
'oidcRPMetaDataOptionsOfflineSessionExpiration',
'oidcRPMetaDataOptionsRedirectUris', 'oidcRPMetaDataOptionsRedirectUris',
'oidcRPMetaDataOptionsBypassConsent', 'oidcRPMetaDataOptionsBypassConsent',
{ {

View File

@ -103,9 +103,10 @@ sub portalConstants {
PE_IMPERSONATION_SERVICE_NOT_ALLOWED => 93, PE_IMPERSONATION_SERVICE_NOT_ALLOWED => 93,
PE_ISSUERMISSINGREQATTR => 94, PE_ISSUERMISSINGREQATTR => 94,
PE_DECRYPTVALUE_SERVICE_NOT_ALLOWED => 95, PE_DECRYPTVALUE_SERVICE_NOT_ALLOWED => 95,
PE_RESETCERTIFICATE_INVALID => 96, PE_BADOTP => 96,
PE_RESETCERTIFICATE_FORMEMPTY => 97, PE_RESETCERTIFICATE_FORMEMPTY => 97,
PE_RESETCERTIFICATE_FIRSTACCESS => 98, PE_RESETCERTIFICATE_FIRSTACCESS => 98,
PE_RESETCERTIFICATE_INVALID => 99,
}; };
} }

View File

@ -628,6 +628,7 @@ sub tree {
help => 'notifications.html#server', help => 'notifications.html#server',
nodes => [ nodes => [
'notificationServer', 'notificationServer',
'notificationDefaultCond',
'notificationServerSentAttributes', 'notificationServerSentAttributes',
{ {
title => title =>
@ -712,6 +713,13 @@ sub tree {
help => 'autosignin.html', help => 'autosignin.html',
nodes => ['autoSigninRules'], nodes => ['autoSigninRules'],
}, },
{
title => 'globalLogout',
help => 'globallogout.html',
form => 'simpleInputContainer',
nodes =>
[ 'globalLogoutRule', 'globalLogoutTimer', ],
},
{ {
title => 'stateCheck', title => 'stateCheck',
help => 'checkstate.html', help => 'checkstate.html',
@ -757,11 +765,15 @@ sub tree {
title => 'decryptValue', title => 'decryptValue',
help => 'decryptvalue.html', help => 'decryptvalue.html',
form => 'simpleInputContainer', form => 'simpleInputContainer',
nodes => [ nodes =>
'decryptValueRule', [ 'decryptValueRule', 'decryptValueFunctions', ]
'decryptValueFunctions',
]
}, },
{
title => 'customPluginsNode',
help => 'plugincustom.html',
nodes => [ 'customPlugins', 'customPluginsParams' ]
},
'refreshSessions',
] ]
}, },
{ {
@ -1206,6 +1218,10 @@ sub tree {
'oidcServiceAllowAuthorizationCodeFlow', 'oidcServiceAllowAuthorizationCodeFlow',
'oidcServiceAllowImplicitFlow', 'oidcServiceAllowImplicitFlow',
'oidcServiceAllowHybridFlow', 'oidcServiceAllowHybridFlow',
'oidcServiceAuthorizationCodeExpiration',
'oidcServiceAccessTokenExpiration',
'oidcServiceIDTokenExpiration',
'oidcServiceOfflineSessionExpiration',
], ],
}, },
{ {

View File

@ -453,7 +453,6 @@ sub tests {
# Warn if 2F dependencies seem missing # Warn if 2F dependencies seem missing
sfaDependencies => sub { sfaDependencies => sub {
my $ok = 0; my $ok = 0;
foreach (qw(u totp utotp yubikey)) { foreach (qw(u totp utotp yubikey)) {
$ok ||= $conf->{ $_ . '2fActivation' }; $ok ||= $conf->{ $_ . '2fActivation' };
@ -684,8 +683,24 @@ sub tests {
"Notifications enabled WITHOUT persistent session storage" ) "Notifications enabled WITHOUT persistent session storage" )
if ( $conf->{notification} ); if ( $conf->{notification} );
return ( 1, return ( 1,
"BruteForceProtection plugin enabled WITHOUT persistent session storage" ) "BruteForceProtection plugin enabled WITHOUT persistent session storage"
if ( $conf->{bruteForceProtection} ); ) if ( $conf->{bruteForceProtection} );
# Return
return 1;
},
# Warn if XML dependencies seem missing
xmlDependencies => sub {
return 1 unless ( $conf->{oldNotifFormat} );
eval "use XML::LibXML";
return ( 1,
"XML::LibXML module is required to enable old format notifications"
) if ($@);
eval "use XML::LibXSLT";
return ( 1,
"XML::LibXSLT module is required to enable old format notifications"
) if ($@);
# Return # Return
return 1; return 1;

View File

@ -18,7 +18,6 @@ extends 'Lemonldap::NG::Common::Conf::AccessLib';
our $VERSION = '2.1.0'; our $VERSION = '2.1.0';
has notifAccess => ( is => 'rw' ); has notifAccess => ( is => 'rw' );
has notifFormat => ( is => 'rw' ); has notifFormat => ( is => 'rw' );
############################# #############################
@ -75,7 +74,6 @@ sub addRoutes {
{ done => { ':notificationId' => 'deleteDoneNotification' } }, { done => { ':notificationId' => 'deleteDoneNotification' } },
['DELETE'] ['DELETE']
); );
} }
sub setNotifAccess { sub setNotifAccess {
@ -158,6 +156,7 @@ sub notifications {
my ( $notifs, $res ); my ( $notifs, $res );
$notifs = $self->notifAccess->$sub(); $notifs = $self->notifAccess->$sub();
my $total = ( keys %$notifs );
# Restrict to wanted values # Restrict to wanted values
if ( if (
@ -201,6 +200,7 @@ sub notifications {
result => 1, result => 1,
count => $count, count => $count,
values => $res, values => $res,
total => $total
} }
); );
} }
@ -221,7 +221,7 @@ sub notifications {
} }
} }
return $self->sendJSONresponse( $req, return $self->sendJSONresponse( $req,
{ result => 1, count => scalar(@r), values => \@r } ); { result => 1, count => scalar(@r), values => \@r, total => $total } );
} }
} }
@ -289,6 +289,7 @@ sub newNotification {
$self->logger->debug("Notification Date = $json->{date}"); $self->logger->debug("Notification Date = $json->{date}");
unless ( $json->{date} =~ /^\d{4}-\d{2}-\d{2}$/ ) { unless ( $json->{date} =~ /^\d{4}-\d{2}-\d{2}$/ ) {
$self->logger->error("Malformed date");
return $self->sendError( $req, "Malformed date", 200 ); return $self->sendError( $req, "Malformed date", 200 );
} }
@ -312,8 +313,8 @@ sub newNotification {
delete $json->{xml}; delete $json->{xml};
}; };
if ($@) { if ($@) {
$self->logger->error("Notification malformed $@"); $self->logger->error("Malformed notification $@");
return $self->sendError( $req, "Notification malformed: $@", 200 ); return $self->sendError( $req, "Malformed notification: $@", 200 );
} }
$newNotif = to_json($json); $newNotif = to_json($json);
} }

View File

@ -99,6 +99,7 @@ sub sessions {
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);
my $res; my $res;

View File

@ -2,6 +2,9 @@
# LemonLDAP::NG Notifications Explorer client # LemonLDAP::NG Notifications Explorer client
### ###
# Max number of notifications to display (see overScheme)
max = 25
scheme = [ scheme = [
(v) -> (v) ->
"groupBy=substr(uid,1)" "groupBy=substr(uid,1)"
@ -11,6 +14,19 @@ scheme = [
"uid=#{v}" "uid=#{v}"
] ]
# When number of children nodes exceeds "max" value
# and does not return "null", a level is added. See
# "$scope.updateTree" method
overScheme =
(v,level,over) ->
# "v.length > over" avoids a loop if one user opened more than "max"
# notifications
console.log 'overScheme => level', level, 'over', over
if level == 1 and v.length > over
"uid=#{v}*&groupBy=substr(uid,#{(level+over+1)})"
else
null
# Session menu # Session menu
menu = menu =
actives: [ actives: [
@ -109,11 +125,12 @@ llapp.controller 'NotificationsExplorerCtrl', [ '$scope', '$translator', '$locat
$scope.waiting = false $scope.waiting = false
$scope.init() $scope.init()
# Open node
$scope.stoggle = (scope) -> $scope.stoggle = (scope) ->
node = scope.$modelValue node = scope.$modelValue
if node.nodes.length == 0 if node.nodes.length == 0
$scope.updateTree node.value, node.nodes, node.level, node.query $scope.updateTree node.value, node.nodes, node.level, node.over, node.query, node.count
scope.toggle() scope.toggle()
$scope.notifDate = (s) -> $scope.notifDate = (s) ->
if s? if s?
@ -139,20 +156,35 @@ llapp.controller 'NotificationsExplorerCtrl', [ '$scope', '$translator', '$locat
$scope.init() $scope.init()
autoId = 0 autoId = 0
$scope.updateTree = (value, node, level, currentQuery) -> $scope.updateTree = (value, node, level, over, currentQuery, count) ->
$scope.waiting = true $scope.waiting = true
query = scheme[level] value, currentQuery query = scheme[level] value, currentQuery
# If number of notifications exceeds "max", call it
if count > max
if tmp = overScheme value, level, over
over++
query = tmp
level = level - 1
else
over = 0
else
over = 0
# Launch HTTP query
$http.get("#{scriptname}notifications/#{$scope.type}?#{query}").then (response) -> $http.get("#{scriptname}notifications/#{$scope.type}?#{query}").then (response) ->
data = response.data data = response.data
if data.result if data.result
for n in data.values for n in data.values
autoId++ autoId++
n.id = "node#{autoId}" n.id = "node#{autoId}"
if level <scheme.length - 1 if level < scheme.length - 1
n.nodes = [] n.nodes = []
n.level = level + 1 n.level = level + 1
n.query = query n.query = query
n.over = over
node.push n node.push n
$scope.total = data.total if value == ''
$scope.waiting = false $scope.waiting = false
, (resp) -> , (resp) ->
$scope.waiting = false $scope.waiting = false
@ -246,7 +278,7 @@ llapp.controller 'NotificationsExplorerCtrl', [ '$scope', '$translator', '$locat
$scope.currentNotification = null $scope.currentNotification = null
$q.all [ $q.all [
$translator.init $scope.lang $translator.init $scope.lang
$scope.updateTree '', $scope.data, 0 $scope.updateTree '', $scope.data, 0, 0
] ]
.then -> .then ->
$scope.waiting = false $scope.waiting = false

View File

@ -78,26 +78,26 @@ overScheme =
_whatToTrace: (t,v,level,over) -> _whatToTrace: (t,v,level,over) ->
# "v.length > over" avoids a loop if one user opened more than "max" # "v.length > over" avoids a loop if one user opened more than "max"
# sessions # sessions
console.log 'overSchema => level', level, 'over', over console.log 'overScheme => level', level, 'over', over
if level == 1 and v.length > over if level == 1 and v.length > over
"#{t}=#{v}*&groupBy=substr(#{t},#{(level+over+1)})" "#{t}=#{v}*&groupBy=substr(#{t},#{(level+over+1)})"
else else
null null
# Note: IPv4 only # Note: IPv4 only
ipAddr: (t,v,level,over) -> ipAddr: (t,v,level,over) ->
console.log 'overSchema => level', level, 'over', over console.log 'overScheme => level', level, 'over', over
if level > 0 and level < 4 and !v.match(/^\d+\.\d/) and over < 2 if level > 0 and level < 4 and !v.match(/^\d+\.\d/) and over < 2
"#{t}=#{v}*&groupBy=net(#{t},#{16*level+4*(over+1)},#{1+level+over})" "#{t}=#{v}*&groupBy=net(#{t},#{16*level+4*(over+1)},#{1+level+over})"
else else
null null
_startTime: (t,v,level,over) -> _startTime: (t,v,level,over) ->
console.log 'overSchema => level', level, 'over', over console.log 'overScheme => level', level, 'over', over
if level > 3 if level > 3
"#{t}=#{v}*&groupBy=substr(#{t},#{(10+level+over)})" "#{t}=#{v}*&groupBy=substr(#{t},#{(10+level+over)})"
else else
null null
_session_uid: (t,v,level,over) -> _session_uid: (t,v,level,over) ->
console.log 'overSchema => level', level, 'over', over console.log 'overScheme => level', level, 'over', over
if level == 1 and v.length > over if level == 1 and v.length > over
"#{t}=#{v}*&groupBy=substr(#{t},#{(level+over+1)})" "#{t}=#{v}*&groupBy=substr(#{t},#{(level+over+1)})"
else else
@ -420,7 +420,7 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location',
sessionType = 'global' sessionType = 'global'
if n == null if n == null
$scope.type = '_whatToTrace' $scope.type = '_whatToTrace'
else if n[1].match /^(persistent)$/ else if n[1].match /^(persistent|offline)$/
sessionType = RegExp.$1 sessionType = RegExp.$1
$scope.type = '_session_uid' $scope.type = '_session_uid'
else else

View File

@ -44,8 +44,12 @@
/* Tables */ /* Tables */
.panel .table th { .panel .table th {
width: 10%;
vertical-align: middle; vertical-align: middle;
width: 10%;
}
.th_rulesAuthnLevel {
width: 2% !important;
} }
/* Category tree */ /* Category tree */

View File

@ -1 +1 @@
.tree-node{border-width:1px;border-style:solid;border-radius:4px;margin-bottom:8px}.tree-node-default{background:#fff}.angular-ui-tree-handle{cursor:pointer}#pleaseWait{background-color:#fff;width:100%;min-height:9.9%;height:auto;z-index:5000;position:absolute;left:0;top:0;margin-left:auto;margin-right:auto;padding:30% 20%;text-align:center;opacity:.8;filter:alpha(opacity=80)}.panel .table th{width:10%;vertical-align:middle}ul.cat-tree{list-style-type:square}p.cat-tree-category{font-weight:bold}html,body{margin:0;padding:0;height:100vh;background:radial-gradient(circle at 50% 0,#fff 0,#ddd 100%) no-repeat scroll 0 0 #ddd}header#navbar{height:9vh;margin-bottom:1vh;padding-bottom:0vh}#content{max-height:86vh;height:86vh;margin:0;padding:0;position:relative}header#navbar .navbar-brand{padding:10px 15px}header#navbar .navbar-brand img{background:#fff}textarea{width:100%}textarea#privateKey,textarea#publicKey,textarea#filetext{font-size:8pt}dl,.panel{margin-bottom:8px}.panel-body{padding:5px}.input-group-solid{border:1px solid #CCC;border-radius:4px;padding:6px 12px}.input-group>.input-group-solid:last-child{border-bottom-left-radius:0;border-top-left-radius:0}#cfgnum{position:relative}#cfgnum.label-warning:hover:after{content:attr(comment);padding:4px 8px;color:rgba(0,0,0,0.5);position:absolute;left:100%;bottom:100%;white-space:nowrap;z-index:2;border-radius:5px;background:#f0ad4e}.link{cursor:pointer}#bottom .panel{height:100%;margin-top:8px}#bottom .panel-body{height:100%}#right{display:flex;flex-direction:column}#bottom{flex:1}@media(min-width:768px){.scrollable-sm{max-height:86vh !important;overflow:auto}#right,#left{max-height:86vh !important;height:86vh}#top{overflow-x:hidden}#bottom{height:43vh;overflow:hidden}}#top,#bottom,#right,#left{position:relative}@media(max-width:767px){#right{position:absolute;top:0;z-index:1000}.lmmenu{position:static;float:left;width:100%;min-height:200px;margin-top:0;border:1px solid #ccc;box-shadow:none}#content{position:relative}}.scrollable{overflow:auto}.container{padding-bottom:15px}.hresizer{position:absolute;width:6px;top:0;bottom:0;right:0;background-color:#EEE;cursor:e-resize}.vresizer{position:absolute;height:6px;left:0;right:0;top:0;background-color:#EEE;cursor:n-resize}.angular-ui-tree-empty{min-height:auto;border:0}.old{color:#A00}.new{color:#0A0}.maxw{width:100%}.center{text-align:center}.backgrounddiv{margin-top:10px}.llcontainer{position:relative;margin-bottom:10px}.navbar-nav li{line-height:50px} .tree-node{border-width:1px;border-style:solid;border-radius:4px;margin-bottom:8px}.tree-node-default{background:#fff}.angular-ui-tree-handle{cursor:pointer}#pleaseWait{background-color:#fff;width:100%;min-height:9.9%;height:auto;z-index:5000;position:absolute;left:0;top:0;margin-left:auto;margin-right:auto;padding:30% 20%;text-align:center;opacity:.8;filter:alpha(opacity=80)}.panel .table th{vertical-align:middle;width:10%}.th_rulesAuthnLevel{width:2%!important}ul.cat-tree{list-style-type:square}p.cat-tree-category{font-weight:bold}html,body{margin:0;padding:0;height:100vh;background:radial-gradient(circle at 50% 0,#fff 0,#ddd 100%) no-repeat scroll 0 0 #ddd}header#navbar{height:9vh;margin-bottom:1vh;padding-bottom:0vh}#content{max-height:86vh;height:86vh;margin:0;padding:0;position:relative}header#navbar .navbar-brand{padding:10px 15px}header#navbar .navbar-brand img{background:#fff}textarea{width:100%}textarea#privateKey,textarea#publicKey,textarea#filetext{font-size:8pt}dl,.panel{margin-bottom:8px}.panel-body{padding:5px}.input-group-solid{border:1px solid #CCC;border-radius:4px;padding:6px 12px}.input-group>.input-group-solid:last-child{border-bottom-left-radius:0;border-top-left-radius:0}#cfgnum{position:relative}#cfgnum.label-warning:hover:after{content:attr(comment);padding:4px 8px;color:rgba(0,0,0,0.5);position:absolute;left:100%;bottom:100%;white-space:nowrap;z-index:2;border-radius:5px;background:#f0ad4e}.link{cursor:pointer}#bottom .panel{height:100%;margin-top:8px}#bottom .panel-body{height:100%}#right{display:flex;flex-direction:column}#bottom{flex:1}@media(min-width:768px){.scrollable-sm{max-height:86vh!important;overflow:auto}#right,#left{max-height:86vh!important;height:86vh}#top{overflow-x:hidden}#bottom{height:43vh;overflow:hidden}}#top,#bottom,#right,#left{position:relative}@media(max-width:767px){#right{position:absolute;top:0;z-index:1000}.lmmenu{position:static;float:left;width:100%;min-height:200px;margin-top:0;border:1px solid #ccc;box-shadow:none}#content{position:relative}}.scrollable{overflow:auto}.container{padding-bottom:15px}.hresizer{position:absolute;width:6px;top:0;bottom:0;right:0;background-color:#EEE;cursor:e-resize}.vresizer{position:absolute;height:6px;left:0;right:0;top:0;background-color:#EEE;cursor:n-resize}.angular-ui-tree-empty{min-height:auto;border:0}.old{color:#A00}.new{color:#0A0}.maxw{width:100%}.center{text-align:center}.backgrounddiv{margin-top:10px}.llcontainer{position:relative;margin-bottom:10px}.navbar-nav li{line-height:50px}

View File

@ -5,11 +5,11 @@
<table class="table table-striped"> <table class="table table-striped">
<thead> <thead>
<tr> <tr>
<th width="20%" trspan="comments"></th> <th trspan="comments"></th>
<th width="30%" trspan="regexps"></th> <th trspan="regexps"></th>
<th width="40%" trspan="rules"></th> <th trspan="rules"></th>
<th width="7%" trspan="rulesAuthnLevel"></th> <th class="th_rulesAuthnLevel" trspan="rulesAuthnLevel"></th>
<th /> <th class="th_rulesAuthnLevel" />
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -33,7 +33,7 @@
<input type="number" class="form-control" ng-model="s.level"/> <input type="number" class="form-control" ng-model="s.level"/>
</td> </td>
<td ng-if="s.re=='default'"> <td ng-if="s.re=='default'">
<input class="form-control" placeholder="defaultLevel" readonly/> <input class="form-control" placeholder="default" readonly/>
</td> </td>
<td> <td>
<span ng-if="s.re!='default'" class="link text-danger glyphicon glyphicon-minus-sign" ng-click="del(currentNode.nodes,$index)"/> <span ng-if="s.re!='default'" class="link text-danger glyphicon glyphicon-minus-sign" ng-click="del(currentNode.nodes,$index)"/>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -473,19 +473,49 @@ function templates(tpl,key) {
"type" : "select" "type" : "select"
}, },
{ {
"default" : 3600,
"get" : tpl+"s/"+key+"/"+"oidcRPMetaDataOptionsIDTokenExpiration", "get" : tpl+"s/"+key+"/"+"oidcRPMetaDataOptionsIDTokenExpiration",
"id" : tpl+"s/"+key+"/"+"oidcRPMetaDataOptionsIDTokenExpiration", "id" : tpl+"s/"+key+"/"+"oidcRPMetaDataOptionsIDTokenExpiration",
"title" : "oidcRPMetaDataOptionsIDTokenExpiration", "title" : "oidcRPMetaDataOptionsIDTokenExpiration",
"type" : "int" "type" : "int"
}, },
{ {
"default" : 3600, "get" : tpl+"s/"+key+"/"+"oidcRPMetaDataOptionsIDTokenForceClaims",
"id" : tpl+"s/"+key+"/"+"oidcRPMetaDataOptionsIDTokenForceClaims",
"title" : "oidcRPMetaDataOptionsIDTokenForceClaims",
"type" : "bool"
},
{
"get" : tpl+"s/"+key+"/"+"oidcRPMetaDataOptionsAccessTokenExpiration", "get" : tpl+"s/"+key+"/"+"oidcRPMetaDataOptionsAccessTokenExpiration",
"id" : tpl+"s/"+key+"/"+"oidcRPMetaDataOptionsAccessTokenExpiration", "id" : tpl+"s/"+key+"/"+"oidcRPMetaDataOptionsAccessTokenExpiration",
"title" : "oidcRPMetaDataOptionsAccessTokenExpiration", "title" : "oidcRPMetaDataOptionsAccessTokenExpiration",
"type" : "int" "type" : "int"
}, },
{
"get" : tpl+"s/"+key+"/"+"oidcRPMetaDataOptionsAuthorizationCodeExpiration",
"id" : tpl+"s/"+key+"/"+"oidcRPMetaDataOptionsAuthorizationCodeExpiration",
"title" : "oidcRPMetaDataOptionsAuthorizationCodeExpiration",
"type" : "int"
},
{
"default" : 0,
"get" : tpl+"s/"+key+"/"+"oidcRPMetaDataOptionsAllowOffline",
"id" : tpl+"s/"+key+"/"+"oidcRPMetaDataOptionsAllowOffline",
"title" : "oidcRPMetaDataOptionsAllowOffline",
"type" : "bool"
},
{
"default" : 0,
"get" : tpl+"s/"+key+"/"+"oidcRPMetaDataOptionsRefreshToken",
"id" : tpl+"s/"+key+"/"+"oidcRPMetaDataOptionsRefreshToken",
"title" : "oidcRPMetaDataOptionsRefreshToken",
"type" : "bool"
},
{
"get" : tpl+"s/"+key+"/"+"oidcRPMetaDataOptionsOfflineSessionExpiration",
"id" : tpl+"s/"+key+"/"+"oidcRPMetaDataOptionsOfflineSessionExpiration",
"title" : "oidcRPMetaDataOptionsOfflineSessionExpiration",
"type" : "int"
},
{ {
"get" : tpl+"s/"+key+"/"+"oidcRPMetaDataOptionsRedirectUris", "get" : tpl+"s/"+key+"/"+"oidcRPMetaDataOptionsRedirectUris",
"id" : tpl+"s/"+key+"/"+"oidcRPMetaDataOptionsRedirectUris", "id" : tpl+"s/"+key+"/"+"oidcRPMetaDataOptionsRedirectUris",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,2 +1 @@
(function(){var n;n={authParams:function(o,e,s){var t,a,d,i,r;for(r=[],i=s.nodes,t=0,a=i.length;t<a;t++)d=i[t],r.push(o.getKey(d));return e.all(r).then(function(){var t,a,i,r,l,c,f,h,u,g,m,_,p,w,P,v,C,b,y,x,F,K,L;for(t=!1,_=[],P=function(n){var o;if(o=n.toLowerCase(),"openidconnect"===o&&(o="oidc"),_.push(o+"Params"),"ad"===o)return _.push("ldapParams")},b=s.nodes,a=0,l=b.length;a<l;a++)d=b[a],P(d.data);for(y=s.nodes_cond,i=0,c=y.length;i<c;i++){if(d=y[i],K=0,p=d._nodes?d._nodes:d.nodes,"Choice"===s.nodes[0].data&&"choiceParams"===d.id)if(console.log("Choice is selected"),p[1].cnodes)K++;else for(p=p[1]._nodes?p[1]._nodes:p[1].nodes,r=0,f=p.length;r<f;r++)for(m=p[r],x=m.data,w=0,h=x.length;w<h;w++)"string"==typeof(L=x[w])&&P(L);else if("Combination"===s.nodes[0].data&&"combinationParams"===d.id)if(console.log("Combination is selected"),p[1].cnodes)K++;else for(p=p[1]._nodes?p[1]._nodes:p[1].nodes,v=0,u=p.length;v<u;v++)m=p[v],P(m.data.type);if(K)return o.waiting=!0,void o.download({$modelValue:p[1]}).then(function(){return n.authParams(o,e,s)})}for(F=s.nodes_cond,C=0,g=F.length;C<g;C++)d=F[C],t||-1!==_.indexOf(d.id)?d.show=!0:d.show=!1})}},window.filterFunctions=n}).call(this); (function(){var F;F={authParams:function(C,b,y){var n,o,x,e,s;for(s=[],n=0,o=(e=y.nodes).length;n<o;n++)x=e[n],s.push(C.getKey(x));return b.all(s).then(function(){var n,o,e,s,t,a,d,i,r,l,c,f,h,u,g,m,_,p,w,P,v;for(!1,l=[],h=function(n){var o;if("openidconnect"===(o=n.toLowerCase())&&(o="oidc"),l.push(o+"Params"),"ad"===o)return l.push("ldapParams")},n=0,s=(m=y.nodes).length;n<s;n++)h((x=m[n]).data);for(o=0,t=(_=y.nodes_cond).length;o<t;o++){if(P=0,c=(x=_[o])._nodes?x._nodes:x.nodes,"Choice"===y.nodes[0].data&&"choiceParams"===x.id)if(console.log("Choice is selected"),c[1].cnodes)P++;else for(e=0,a=(c=c[1]._nodes?c[1]._nodes:c[1].nodes).length;e<a;e++)for(f=0,d=(p=c[e].data).length;f<d;f++)"string"==typeof(v=p[f])&&h(v);else if("Combination"===y.nodes[0].data&&"combinationParams"===x.id)if(console.log("Combination is selected"),c[1].cnodes)P++;else for(u=0,i=(c=c[1]._nodes?c[1]._nodes:c[1].nodes).length;u<i;u++)h(c[u].data.type);if(P)return C.waiting=!0,void C.download({$modelValue:c[1]}).then(function(){return F.authParams(C,b,y)})}for(g=0,r=(w=y.nodes_cond).length;g<r;g++)x=w[g],-1===l.indexOf(x.id)?x.show=!1:x.show=!0})}},window.filterFunctions=F}).call(this);
//# sourceMappingURL=lemonldap-ng-manager/site/htdocs/static/js/filterFunctions.min.js.map

View File

@ -1 +1 @@
{"version":3,"sources":["lemonldap-ng-manager/site/htdocs/static/js/filterFunctions.js"],"names":["filterFunctions","authParams","scope","$q","node","i","len","n","ref","wait","nodes","length","push","getKey","all","then","j","k","l","len1","len2","len3","len4","len5","len6","m","nToShow","nd","o","p","q","r","ref1","ref2","ref3","ref4","restart","s","tmp","toLowerCase","data","nodes_cond","_nodes","id","console","log","cnodes","type","waiting","download","$modelValue","indexOf","show","window","call","this"],"mappings":"CACA,WACE,GAAIA,EAEJA,IACEC,WAAY,SAASC,EAAOC,EAAIC,GAC9B,GAAIC,GAAGC,EAAKC,EAAGC,EAAKC,CAGpB,KAFAA,KACAD,EAAMJ,EAAKM,MACNL,EAAI,EAAGC,EAAME,EAAIG,OAAQN,EAAIC,EAAKD,IACrCE,EAAIC,EAAIH,GACRI,EAAKG,KAAKV,EAAMW,OAAON,GAEzB,OAAOJ,GAAGW,IAAIL,GAAMM,KAAK,WACvB,GAAID,GAAKE,EAAGC,EAAGC,EAAGC,EAAMC,EAAMC,EAAMC,EAAMC,EAAMC,EAAMC,EAAGC,EAASC,EAAIC,EAAGC,EAAGC,EAAGC,EAAGC,EAAMC,EAAMC,EAAMC,EAAMC,EAASC,CAenH,KAdAvB,GAAM,EACNY,KACAG,EAAI,SAASQ,GACX,GAAIC,EAMJ,IALAA,EAAMD,EAAEE,cACI,kBAARD,IACFA,EAAM,QAERZ,EAAQd,KAAK0B,EAAM,UACP,OAARA,EACF,MAAOZ,GAAQd,KAAK,eAGxBoB,EAAO5B,EAAKM,MACPM,EAAI,EAAGG,EAAOa,EAAKrB,OAAQK,EAAIG,EAAMH,IACxCT,EAAIyB,EAAKhB,GACTa,EAAEtB,EAAEiC,KAGN,KADAP,EAAO7B,EAAKqC,WACPxB,EAAI,EAAGG,EAAOa,EAAKtB,OAAQM,EAAIG,EAAMH,IAAK,CAI7C,GAHAV,EAAI0B,EAAKhB,GACTmB,EAAU,EACVT,EAAKpB,EAAEmC,OAASnC,EAAEmC,OAASnC,EAAEG,MACF,WAAvBN,EAAKM,MAAM,GAAG8B,MAA8B,iBAATjC,EAAEoC,GAEvC,GADAC,QAAQC,IAAI,sBACRlB,EAAG,GAAGmB,OACRV,QAGA,KADAT,EAAKA,EAAG,GAAGe,OAASf,EAAG,GAAGe,OAASf,EAAG,GAAGjB,MACpCQ,EAAI,EAAGG,EAAOM,EAAGhB,OAAQO,EAAIG,EAAMH,IAGtC,IAFAO,EAAIE,EAAGT,GACPgB,EAAOT,EAAEe,KACJZ,EAAI,EAAGN,EAAOY,EAAKvB,OAAQiB,EAAIN,EAAMM,IAEvB,iBADjBS,EAAIH,EAAKN,KAEPC,EAAEQ,OAKL,IAA2B,gBAAvBjC,EAAKM,MAAM,GAAG8B,MAAmC,sBAATjC,EAAEoC,GAEnD,GADAC,QAAQC,IAAI,2BACRlB,EAAG,GAAGmB,OACRV,QAGA,KADAT,EAAKA,EAAG,GAAGe,OAASf,EAAG,GAAGe,OAASf,EAAG,GAAGjB,MACpCoB,EAAI,EAAGP,EAAOI,EAAGhB,OAAQmB,EAAIP,EAAMO,IACtCL,EAAIE,EAAGG,GACPD,EAAEJ,EAAEe,KAAKO,KAIf,IAAIX,EAOF,MANAlC,GAAM8C,SAAU,MAChB9C,GAAM+C,UACJC,YAAevB,EAAG,KACjBZ,KAAK,WACN,MAAOf,GAAgBC,WAAWC,EAAOC,EAAIC,KAMnD,IADA+B,EAAO/B,EAAKqC,WACPV,EAAI,EAAGP,EAAOW,EAAKxB,OAAQoB,EAAIP,EAAMO,IACxCxB,EAAI4B,EAAKJ,GACJjB,IAAkC,IAA3BY,EAAQyB,QAAQ5C,EAAEoC,IAG5BpC,EAAE6C,MAAO,EAFT7C,EAAE6C,MAAO,MASnBC,OAAOrD,gBAAkBA,IAExBsD,KAAKC","file":"lemonldap-ng-manager/site/htdocs/static/js/filterFunctions.min.js"} {"version":3,"sources":["lemonldap-ng-manager/site/htdocs/static/js/filterFunctions.js"],"names":["filterFunctions","authParams","scope","$q","node","i","len","n","ref","wait","nodes","length","push","getKey","all","then","j","k","l","len1","len2","len3","len4","len5","len6","nToShow","nd","o","p","q","r","ref1","ref2","ref3","ref4","restart","s","tmp","toLowerCase","data","nodes_cond","_nodes","id","console","log","cnodes","type","waiting","download","$modelValue","indexOf","show","window","call","this"],"mappings":"CACA,WACE,IAAIA,EAEJA,EAAkB,CAChBC,WAAY,SAASC,EAAOC,EAAIC,GAC9B,IAAIC,EAAGC,EAAKC,EAAGC,EAAKC,EAGpB,IAFAA,EAAO,GAEFJ,EAAI,EAAGC,GADZE,EAAMJ,EAAKM,OACWC,OAAQN,EAAIC,EAAKD,IACrCE,EAAIC,EAAIH,GACRI,EAAKG,KAAKV,EAAMW,OAAON,IAEzB,OAAOJ,EAAGW,IAAIL,GAAMM,KAAK,WACvB,IAASC,EAAGC,EAAGC,EAAGC,EAAMC,EAAMC,EAAMC,EAAMC,EAAMC,EAASC,EAASC,EAAIC,EAAGC,EAAGC,EAAGC,EAAGC,EAAMC,EAAMC,EAAMC,EAAMC,EAASC,EAenH,KAdM,EACNX,EAAU,GACVG,EAAI,SAASQ,GACX,IAAIC,EAMJ,GAJY,mBADZA,EAAMD,EAAEE,iBAEND,EAAM,QAERZ,EAAQb,KAAKyB,EAAM,UACP,OAARA,EACF,OAAOZ,EAAQb,KAAK,eAInBI,EAAI,EAAGG,GADZY,EAAO3B,EAAKM,OACYC,OAAQK,EAAIG,EAAMH,IAExCY,GADArB,EAAIwB,EAAKf,IACLuB,MAGN,IAAKtB,EAAI,EAAGG,GADZY,EAAO5B,EAAKoC,YACY7B,OAAQM,EAAIG,EAAMH,IAAK,CAI7C,GAFAkB,EAAU,EACVT,GAFAnB,EAAIyB,EAAKf,IAEFwB,OAASlC,EAAEkC,OAASlC,EAAEG,MACF,WAAvBN,EAAKM,MAAM,GAAG6B,MAA8B,iBAAThC,EAAEmC,GAEvC,GADAC,QAAQC,IAAI,sBACRlB,EAAG,GAAGmB,OACRV,SAGA,IAAKjB,EAAI,EAAGG,GADZK,EAAKA,EAAG,GAAGe,OAASf,EAAG,GAAGe,OAASf,EAAG,GAAGhB,OACnBC,OAAQO,EAAIG,EAAMH,IAGtC,IAAKS,EAAI,EAAGL,GADZW,EADIP,EAAGR,GACEqB,MACe5B,OAAQgB,EAAIL,EAAMK,IAEvB,iBADjBS,EAAIH,EAAKN,KAEPC,EAAEQ,QAKL,GAA2B,gBAAvBhC,EAAKM,MAAM,GAAG6B,MAAmC,sBAAThC,EAAEmC,GAEnD,GADAC,QAAQC,IAAI,2BACRlB,EAAG,GAAGmB,OACRV,SAGA,IAAKN,EAAI,EAAGN,GADZG,EAAKA,EAAG,GAAGe,OAASf,EAAG,GAAGe,OAASf,EAAG,GAAGhB,OACnBC,OAAQkB,EAAIN,EAAMM,IAEtCD,EADIF,EAAGG,GACHU,KAAKO,MAIf,GAAIX,EAOF,OANAjC,EAAM6C,SAAU,OAChB7C,EAAM8C,SAAS,CACbC,YAAevB,EAAG,KACjBX,KAAK,WACN,OAAOf,EAAgBC,WAAWC,EAAOC,EAAIC,KAMnD,IAAK0B,EAAI,EAAGN,GADZU,EAAO9B,EAAKoC,YACY7B,OAAQmB,EAAIN,EAAMM,IACxCvB,EAAI2B,EAAKJ,IAC8B,IAA3BL,EAAQyB,QAAQ3C,EAAEmC,IAC5BnC,EAAE4C,MAAO,EAET5C,EAAE4C,MAAO,MAOnBC,OAAOpD,gBAAkBA,IAExBqD,KAAKC"}

View File

@ -1 +1 @@
(function(){var e;(e=angular.module("llApp",["ngAria"])).provider("$translator",function(){var e,t,n,r,a,i,o,s,l,u,c,g;if(g={},decodeURIComponent(document.cookie).match(/llnglanguage=(\w+)/))g.lang=RegExp.$1;else if(navigator){for(r=[],a=[],l=[navigator.language],navigator.languages&&(l=navigator.languages),t=0,i=l.length;t<i;t++)for(s=l[t],console.log("Navigator lang",s),n=0,o=(c=window.availableLanguages).length;n<o;n++)e=c[n],console.log(" Available lang",e),u=new RegExp("^"+e+"-?"),s.match(u)?(console.log(" Matching lang =",e),r.push(e)):e.substring(0,1)===s.substring(0,1)&&a.push(e);g.lang=r[0]?r[0]:a[0]?a[0]:"en"}else g.lang="en";return console.log("Selected lang ->",g.lang),g.deferredTr=[],g.translationFields={},g.translate=function(e){return g.translationFields[e]&&(e=g.translationFields[e]),e},g.translateField=function(e,t){return g.translate(e[t])},g.translateP=function(e){return e&&g.translationFields.portal&&(e=e.replace(/__(\w+)__/g,function(e,t){return g.translate(t)})),e},this.$get=["$q","$http",function(t,n){return g.last="",g.init=function(e){var i;return e||(e=g.lang),(i=new Date).setTime(i.getTime()+2592e6),document.cookie="llnglanguage="+e+"; expires="+i.toUTCString()+"; path=/",i=t.defer(),g.last!==e?(g.last=e,n.get(window.staticPrefix+"languages/"+e+".json").then(function(e){var t,n,r,a;for(g.translationFields=e.data,n=0,r=(a=g.deferredTr).length;n<r;n++)(t=a[n]).e[t.f](g.translationFields[t.m]);return g.deferredTr=[],i.resolve("Translation files loaded")},function(e){return i.reject("")})):i.resolve("No change"),i.promise},g}],this}),e.directive("trspan",["$translator",function(r){return{restrict:"A",replace:!1,transclude:!0,scope:{trspan:"@"},link:function(e,t,n){return r.translationFields.portal?n.trspan=r.translate(n.trspan):r.deferredTr.push({e:t,f:"text",m:n.trspan}),t.text(n.trspan)},template:""}}]),e.provider("$htmlParams",function(){return this.$get=function(){var n;return n={},{set:function(e,t){return n[e]=t},menu:function(){return n.menu},params:function(){return n.params}}},this}),e.directive("script",["$htmlParams",function(a){return{restrict:"E",terminal:!0,compile:function(e,t){var n,r;if(t.type&&(r=t.type.match(/text\/(menu|parameters)/)))try{return a.set(r[1],JSON.parse(e[0].text))}catch(e){n=e,console.log("Parsing error:",n)}}}}]),e.controller("ModalInstanceCtrl",["$scope","$uibModalInstance","elem","set","init",function(a,e,t,n,r){var i,o;return null,a.elem=t,a.set=n,a.result=r,a.staticPrefix=window.staticPrefix,i=t("currentNode"),a.translateP=t("translateP"),i&&(o=i.data,a.currentNode=i),a.ok=function(){return n("result",a.result),e.close(!0)},a.cancel=function(){return i&&(a.currentNode.data=o),e.dismiss("cancel")},a.inSelect=function(e){var t,n,r;for(t=0,n=(r=a.currentNode.select).length;t<n;t++)if(r[t].k===e)return!0;return!1}}]),e.directive("onReadFile",["$parse",function(a){return{restrict:"A",scope:!1,link:function(n,e,t){var r;return r=a(t.onReadFile),e.on("change",function(e){var t;return(t=new FileReader).onload=function(e){return n.$apply(function(){return r(n,{$fileContent:e.target.result})})},t.readAsText((e.srcElement||e.target).files[0])})}}}]),e.directive("resizer",["$document",function(i){var o,s;return s=o=null,function(e,t,r){var n,a;return t.on("mousedown",function(e){return"vertical"===r.resizer?s=$(r.resizerRight).width()+$(r.resizerLeft).width():o=$(r.resizerTop).height()+$(r.resizerBottom).height(),e.preventDefault(),i.on("mousemove",n),i.on("mouseup",a)}),n=function(e){var t,n;return"vertical"===r.resizer?(t=e.pageX,r.resizerMax&&t>r.resizerMax&&(t=parseInt(r.resizerMax)),$(r.resizerLeft).css({width:t+"px"}),$(r.resizerRight).css({width:s-t+"px"})):(n=e.pageY-$("#navbar").height(),$(r.resizerTop).css({height:n+"px"}),$(r.resizerBottom).css({height:o-n+"px"}))},a=function(){return i.unbind("mousemove",n),i.unbind("mouseup",a)}}}]),e.factory("$lmhttp",["$q","$location",function(t,e){return{responseError:function(e){return 401===e.status&&window.portal?window.location=window.portal+"?url="+window.btoa(window.location).replace(/\//,"_"):t.reject(e)}}}]),e.config(["$httpProvider",function(e){return e.interceptors.push("$lmhttp")}])}).call(this); (function(){var e;(e=angular.module("llApp",["ngAria"])).provider("$translator",function(){var e,t,n,r,a,i,o,s,l,u,c,g;if(g={},decodeURIComponent(document.cookie).match(/llnglanguage=(\w+)/))g.lang=RegExp.$1;else if(navigator){for(r=[],a=[],l=[navigator.language],navigator.languages&&(l=navigator.languages),t=0,i=l.length;t<i;t++)for(s=l[t],console.log("Navigator lang",s),n=0,o=(c=window.availableLanguages).length;n<o;n++)e=c[n],console.log(" Available lang",e),u=new RegExp("^"+e+"-?"),s.match(u)?(console.log(" Matching lang =",e),r.push(e)):e.substring(0,1)===s.substring(0,1)&&a.push(e);g.lang=r[0]?r[0]:a[0]?a[0]:"en"}else g.lang="en";return console.log("Selected lang ->",g.lang),g.deferredTr=[],g.translationFields={},g.translate=function(e){return g.translationFields[e]&&(e=g.translationFields[e]),e},g.translateField=function(e,t){return g.translate(e[t])},g.translateP=function(e){return e&&g.translationFields.portal&&(e=e.replace(/__(\w+)__/g,function(e,t){return g.translate(t)})),e},this.$get=["$q","$http",function(t,n){return g.last="",g.init=function(e){var i;return e=e||g.lang,(i=new Date).setTime(i.getTime()+2592e6),document.cookie="llnglanguage="+e+"; expires="+i.toUTCString()+"; path=/",i=t.defer(),g.last!==e?(g.last=e,n.get(window.staticPrefix+"languages/"+e+".json").then(function(e){var t,n,r,a;for(g.translationFields=e.data,n=0,r=(a=g.deferredTr).length;n<r;n++)(t=a[n]).e[t.f](g.translationFields[t.m]);return g.deferredTr=[],i.resolve("Translation files loaded")},function(e){return i.reject("")})):i.resolve("No change"),i.promise},g}],this}),e.directive("trspan",["$translator",function(r){return{restrict:"A",replace:!1,transclude:!0,scope:{trspan:"@"},link:function(e,t,n){return r.translationFields.portal?n.trspan=r.translate(n.trspan):r.deferredTr.push({e:t,f:"text",m:n.trspan}),t.text(n.trspan)},template:""}}]),e.provider("$htmlParams",function(){return this.$get=function(){var n;return n={},{set:function(e,t){return n[e]=t},menu:function(){return n.menu},params:function(){return n.params}}},this}),e.directive("script",["$htmlParams",function(a){return{restrict:"E",terminal:!0,compile:function(e,t){var n,r;if(t.type&&(r=t.type.match(/text\/(menu|parameters)/)))try{return a.set(r[1],JSON.parse(e[0].text))}catch(e){n=e,console.log("Parsing error:",n)}}}}]),e.controller("ModalInstanceCtrl",["$scope","$uibModalInstance","elem","set","init",function(a,e,t,n,r){var i,o;return null,a.elem=t,a.set=n,a.result=r,a.staticPrefix=window.staticPrefix,i=t("currentNode"),a.translateP=t("translateP"),i&&(o=i.data,a.currentNode=i),a.ok=function(){return n("result",a.result),e.close(!0)},a.cancel=function(){return i&&(a.currentNode.data=o),e.dismiss("cancel")},a.inSelect=function(e){var t,n,r;for(t=0,n=(r=a.currentNode.select).length;t<n;t++)if(r[t].k===e)return!0;return!1}}]),e.directive("onReadFile",["$parse",function(a){return{restrict:"A",scope:!1,link:function(n,e,t){var r;return r=a(t.onReadFile),e.on("change",function(e){var t;return(t=new FileReader).onload=function(e){return n.$apply(function(){return r(n,{$fileContent:e.target.result})})},t.readAsText((e.srcElement||e.target).files[0])})}}}]),e.directive("resizer",["$document",function(i){var o,s;return s=o=null,function(e,t,r){var n,a;return t.on("mousedown",function(e){return"vertical"===r.resizer?s=$(r.resizerRight).width()+$(r.resizerLeft).width():o=$(r.resizerTop).height()+$(r.resizerBottom).height(),e.preventDefault(),i.on("mousemove",n),i.on("mouseup",a)}),n=function(e){var t,n;return"vertical"===r.resizer?(t=e.pageX,r.resizerMax&&t>r.resizerMax&&(t=parseInt(r.resizerMax)),$(r.resizerLeft).css({width:t+"px"}),$(r.resizerRight).css({width:s-t+"px"})):(n=e.pageY-$("#navbar").height(),$(r.resizerTop).css({height:n+"px"}),$(r.resizerBottom).css({height:o-n+"px"}))},a=function(){return i.unbind("mousemove",n),i.unbind("mouseup",a)}}}]),e.factory("$lmhttp",["$q","$location",function(t,e){return{responseError:function(e){return 401===e.status&&window.portal?window.location=window.portal+"?url="+window.btoa(window.location).replace(/\//,"_"):t.reject(e)}}}]),e.config(["$httpProvider",function(e){return e.interceptors.push("$lmhttp")}])}).call(this);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -5,7 +5,9 @@
*/ */
(function() { (function() {
var llapp, menu, scheme; var llapp, max, menu, overScheme, scheme;
max = 25;
scheme = [ scheme = [
function(v) { function(v) {
@ -17,6 +19,15 @@
} }
]; ];
overScheme = function(v, level, over) {
console.log('overScheme => level', level, 'over', over);
if (level === 1 && v.length > over) {
return "uid=" + v + "*&groupBy=substr(uid," + (level + over + 1) + ")";
} else {
return null;
}
};
menu = { menu = {
actives: [ actives: [
{ {
@ -131,9 +142,9 @@
var node; var node;
node = scope.$modelValue; node = scope.$modelValue;
if (node.nodes.length === 0) { if (node.nodes.length === 0) {
$scope.updateTree(node.value, node.nodes, node.level, node.query); $scope.updateTree(node.value, node.nodes, node.level, node.over, node.query, node.count);
return scope.toggle();
} }
return scope.toggle();
}; };
$scope.notifDate = function(s) { $scope.notifDate = function(s) {
var d; var d;
@ -164,10 +175,21 @@
} }
}); });
autoId = 0; autoId = 0;
$scope.updateTree = function(value, node, level, currentQuery) { $scope.updateTree = function(value, node, level, over, currentQuery, count) {
var query; var query, tmp;
$scope.waiting = true; $scope.waiting = true;
query = scheme[level](value, currentQuery); query = scheme[level](value, currentQuery);
if (count > max) {
if (tmp = overScheme(value, level, over)) {
over++;
query = tmp;
level = level - 1;
} else {
over = 0;
}
} else {
over = 0;
}
return $http.get(scriptname + "notifications/" + $scope.type + "?" + query).then(function(response) { return $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;
@ -181,9 +203,13 @@
n.nodes = []; n.nodes = [];
n.level = level + 1; n.level = level + 1;
n.query = query; n.query = query;
n.over = over;
} }
node.push(n); node.push(n);
} }
if (value === '') {
$scope.total = data.total;
}
} }
return $scope.waiting = false; return $scope.waiting = false;
}, function(resp) { }, function(resp) {
@ -301,7 +327,7 @@
$scope.data = []; $scope.data = [];
$scope.currentScope = null; $scope.currentScope = null;
$scope.currentNotification = null; $scope.currentNotification = null;
$q.all([$translator.init($scope.lang), $scope.updateTree('', $scope.data, 0)]).then(function() { $q.all([$translator.init($scope.lang), $scope.updateTree('', $scope.data, 0, 0)]).then(function() {
return $scope.waiting = false; return $scope.waiting = false;
}, function(resp) { }, function(resp) {
return $scope.waiting = false; return $scope.waiting = false;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -83,7 +83,7 @@
overScheme = { overScheme = {
_whatToTrace: function(t, v, level, over) { _whatToTrace: function(t, v, level, over) {
console.log('overSchema => level', level, 'over', over); console.log('overScheme => level', level, 'over', over);
if (level === 1 && v.length > over) { if (level === 1 && v.length > over) {
return t + "=" + v + "*&groupBy=substr(" + t + "," + (level + over + 1) + ")"; return t + "=" + v + "*&groupBy=substr(" + t + "," + (level + over + 1) + ")";
} else { } else {
@ -91,7 +91,7 @@
} }
}, },
ipAddr: function(t, v, level, over) { ipAddr: function(t, v, level, over) {
console.log('overSchema => level', level, 'over', over); console.log('overScheme => level', level, 'over', over);
if (level > 0 && level < 4 && !v.match(/^\d+\.\d/) && over < 2) { if (level > 0 && level < 4 && !v.match(/^\d+\.\d/) && over < 2) {
return t + "=" + v + "*&groupBy=net(" + t + "," + (16 * level + 4 * (over + 1)) + "," + (1 + level + over) + ")"; return t + "=" + v + "*&groupBy=net(" + t + "," + (16 * level + 4 * (over + 1)) + "," + (1 + level + over) + ")";
} else { } else {
@ -99,7 +99,7 @@
} }
}, },
_startTime: function(t, v, level, over) { _startTime: function(t, v, level, over) {
console.log('overSchema => level', level, 'over', over); console.log('overScheme => level', level, 'over', over);
if (level > 3) { if (level > 3) {
return t + "=" + v + "*&groupBy=substr(" + t + "," + (10 + level + over) + ")"; return t + "=" + v + "*&groupBy=substr(" + t + "," + (10 + level + over) + ")";
} else { } else {
@ -107,7 +107,7 @@
} }
}, },
_session_uid: function(t, v, level, over) { _session_uid: function(t, v, level, over) {
console.log('overSchema => level', level, 'over', over); console.log('overScheme => level', level, 'over', over);
if (level === 1 && v.length > over) { if (level === 1 && v.length > over) {
return t + "=" + v + "*&groupBy=substr(" + t + "," + (level + over + 1) + ")"; return t + "=" + v + "*&groupBy=substr(" + t + "," + (level + over + 1) + ")";
} else { } else {
@ -511,7 +511,7 @@
sessionType = 'global'; sessionType = 'global';
if (n === null) { if (n === null) {
$scope.type = '_whatToTrace'; $scope.type = '_whatToTrace';
} else if (n[1].match(/^(persistent)$/)) { } else if (n[1].match(/^(persistent|offline)$/)) {
sessionType = RegExp.$1; sessionType = RegExp.$1;
$scope.type = '_session_uid'; $scope.type = '_session_uid';
} else { } else {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -219,6 +219,9 @@
"customModule":"وحدة مخصصة", "customModule":"وحدة مخصصة",
"customParams":"أسماء الوحدات المخصصة", "customParams":"أسماء الوحدات المخصصة",
"customPassword":"وحدة كلمة المرورالمخصصة", "customPassword":"وحدة كلمة المرورالمخصصة",
"customPlugins":"Modules list",
"customPluginsNode":"Custom plugins",
"customPluginsParams":"Additional parameters",
"customPortalSkin":"غلاف البوابة مخصص", "customPortalSkin":"غلاف البوابة مخصص",
"customRegister":"وحدة تسجيل مخصص", "customRegister":"وحدة تسجيل مخصص",
"customToTrace":"REMOTE_CUSTOM", "customToTrace":"REMOTE_CUSTOM",
@ -247,7 +250,7 @@
"dbiUserTable":"جدول المستخدم", "dbiUserTable":"جدول المستخدم",
"decryptValue":"Decrypt value", "decryptValue":"Decrypt value",
"decryptValueFunctions":"Decrypt functions", "decryptValueFunctions":"Decrypt functions",
"decryptValueRule":"Use rule", "decryptValueRule":"استخدام القاعدة",
"default":"الاعْتيادي", "default":"الاعْتيادي",
"defaultRule":"القاعدة الاعتيادية ", "defaultRule":"القاعدة الاعتيادية ",
"demoModeOn":"هذا المدير يعمل في طريقة العرض", "demoModeOn":"هذا المدير يعمل في طريقة العرض",
@ -313,6 +316,9 @@
"forms":"إستمارات", "forms":"إستمارات",
"friendlyName":"اسم مألوف", "friendlyName":"اسم مألوف",
"generalParameters":"المعاييرالعامة", "generalParameters":"المعاييرالعامة",
"globalLogout":"Global logout",
"globalLogoutRule":"Activation",
"globalLogoutTimer":"قبول تلقائي للوقت",
"globalStorage":"أباتشي :: وحدة الجلسة", "globalStorage":"أباتشي :: وحدة الجلسة",
"globalStorageOptions":"أباتشي :: معايير وحدة الجلسة", "globalStorageOptions":"أباتشي :: معايير وحدة الجلسة",
"gpgAuthnLevel":"مستوى إثبات الهوية", "gpgAuthnLevel":"مستوى إثبات الهوية",
@ -430,7 +436,7 @@
"loadFromUrl":"تحميل من أل يو أر ل", "loadFromUrl":"تحميل من أل يو أر ل",
"localSessionStorage":"وحدة ذاكرة التخزين المؤقت", "localSessionStorage":"وحدة ذاكرة التخزين المؤقت",
"localSessionStorageOptions":"خيارات وحدة التخزين المؤقت", "localSessionStorageOptions":"خيارات وحدة التخزين المؤقت",
"locationRules":"قاعدة الدخول", "locationRules":"Access rules",
"loginHistory":"سجل تسجيل الدخول", "loginHistory":"سجل تسجيل الدخول",
"loginHistoryEnabled":"تفعيل", "loginHistoryEnabled":"تفعيل",
"logo":"شعار", "logo":"شعار",
@ -507,6 +513,8 @@
"notAValidPerlExpression":"عبارة بيرل ليست صحيحة", "notAValidPerlExpression":"عبارة بيرل ليست صحيحة",
"notification":"تفعيل", "notification":"تفعيل",
"notifications":"إشعار", "notifications":"إشعار",
"notification_s":"notification(s)",
"notificationDefaultCond":"Default condition",
"notificationServer":"إشعارالخادم", "notificationServer":"إشعارالخادم",
"notificationServerDELETE":"DELETE method", "notificationServerDELETE":"DELETE method",
"notificationServerGET":"GET method", "notificationServerGET":"GET method",
@ -533,6 +541,7 @@
"nullParams":"لا شيء في المعايير", "nullParams":"لا شيء في المعايير",
"number":"رقم", "number":"رقم",
"off":"إيقاف", "off":"إيقاف",
"offlineSessions":"Offline sessions",
"oldValue":"قيمة قديمة", "oldValue":"قيمة قديمة",
"on":"تنشيط", "on":"تنشيط",
"oidcAuthnLevel":"مستوى إثبات الهوية", "oidcAuthnLevel":"مستوى إثبات الهوية",
@ -544,6 +553,7 @@
"oidcOPMetaDataNode":" أوبين أيدي كونيكت بروفيدر", "oidcOPMetaDataNode":" أوبين أيدي كونيكت بروفيدر",
"oidcOPMetaDataOptions":"الخيارات", "oidcOPMetaDataOptions":"الخيارات",
"oidcRPMetaDataOptionsAuthentication":"إثبات الهوية", "oidcRPMetaDataOptionsAuthentication":"إثبات الهوية",
"oidcRPMetaDataOptionsAllowOffline":"Allow offline access",
"oidcOPMetaDataOptionsCheckJWTSignature":"توقيع", "oidcOPMetaDataOptionsCheckJWTSignature":"توقيع",
"oidcOPMetaDataOptionsClientID":"معرف العميل", "oidcOPMetaDataOptionsClientID":"معرف العميل",
"oidcOPMetaDataOptionsClientSecret":"سرالعميل", "oidcOPMetaDataOptionsClientSecret":"سرالعميل",
@ -573,6 +583,7 @@
"oidcRPMetaDataNode":"الأطراف المعتمد لي أوبين أيدي كونيكت", "oidcRPMetaDataNode":"الأطراف المعتمد لي أوبين أيدي كونيكت",
"oidcRPMetaDataOptions":"الخيارات", "oidcRPMetaDataOptions":"الخيارات",
"oidcRPMetaDataOptionsAccessTokenExpiration":"انتهاء صلاحية التوكن", "oidcRPMetaDataOptionsAccessTokenExpiration":"انتهاء صلاحية التوكن",
"oidcRPMetaDataOptionsAuthorizationCodeExpiration":"Authorization Code expiration",
"oidcRPMetaDataOptionsBypassConsent":"تخطى الموافقة ", "oidcRPMetaDataOptionsBypassConsent":"تخطى الموافقة ",
"oidcRPMetaDataOptionsClientID":"معرف العميل", "oidcRPMetaDataOptionsClientID":"معرف العميل",
"oidcRPMetaDataOptionsClientSecret":"سرالعميل", "oidcRPMetaDataOptionsClientSecret":"سرالعميل",
@ -581,6 +592,9 @@
"oidcRPMetaDataOptionsIcon":"شعار", "oidcRPMetaDataOptionsIcon":"شعار",
"oidcRPMetaDataOptionsIDTokenExpiration":" انتهاء صلاحية تعريف التوكن", "oidcRPMetaDataOptionsIDTokenExpiration":" انتهاء صلاحية تعريف التوكن",
"oidcRPMetaDataOptionsIDTokenSignAlg":"خوارزمية توقيع آي دي التوكن", "oidcRPMetaDataOptionsIDTokenSignAlg":"خوارزمية توقيع آي دي التوكن",
"oidcRPMetaDataOptionsIDTokenForceClaims":"Force claims to be returned in ID Token",
"oidcRPMetaDataOptionsOfflineSessionExpiration":"Offline session expiration",
"oidcRPMetaDataOptionsRefreshToken":"Use refresh tokens",
"oidcRPMetaDataOptionsUserIDAttr":"خاصّيّة المستخدم", "oidcRPMetaDataOptionsUserIDAttr":"خاصّيّة المستخدم",
"oidcRPName":"اسم أوبين أيدي كونيكت RP", "oidcRPName":"اسم أوبين أيدي كونيكت RP",
"oidcRPStateTimeout":"حالة مهلة الجلسة", "oidcRPStateTimeout":"حالة مهلة الجلسة",
@ -600,6 +614,10 @@
"oidcServicePrivateKeySig":"توقيع على المفتاح الخاص", "oidcServicePrivateKeySig":"توقيع على المفتاح الخاص",
"oidcServicePublicKeySig":"توقيع على المفتاح العمومي", "oidcServicePublicKeySig":"توقيع على المفتاح العمومي",
"oidcServiceKeyIdSig":"توقيع على هوية المفتاح ", "oidcServiceKeyIdSig":"توقيع على هوية المفتاح ",
"oidcServiceAuthorizationCodeExpiration":"Authorization Code expiration",
"oidcServiceAccessTokenExpiration":"انتهاء صلاحية التوكن",
"oidcServiceIDTokenExpiration":" انتهاء صلاحية تعريف التوكن",
"oidcServiceOfflineSessionExpiration":"Offline session expiration",
"oidcStorage":"اسم وحدة الجلسات", "oidcStorage":"اسم وحدة الجلسات",
"oidcStorageOptions":"خيارات وحدة الجلسات", "oidcStorageOptions":"خيارات وحدة الجلسات",
"oidcOPMetaDataNodes":" أوبين أيدي كونيكت بروفيدر", "oidcOPMetaDataNodes":" أوبين أيدي كونيكت بروفيدر",
@ -620,6 +638,7 @@
"oidcServiceAllowAuthorizationCodeFlow":"ترخيص كود التدفق", "oidcServiceAllowAuthorizationCodeFlow":"ترخيص كود التدفق",
"oidcServiceAllowImplicitFlow":"التدفق الضمني", "oidcServiceAllowImplicitFlow":"التدفق الضمني",
"oidcServiceAllowHybridFlow":"تدفق هجين", "oidcServiceAllowHybridFlow":"تدفق هجين",
"oidcServiceAllowOffline":"Allow offline access",
"ok":"حسنا", "ok":"حسنا",
"oldNotifFormat":"استخدام صيغة xml القديمة", "oldNotifFormat":"استخدام صيغة xml القديمة",
"openIdAttr":"تسجيل الدخول في أوبين أيدي", "openIdAttr":"تسجيل الدخول في أوبين أيدي",
@ -726,6 +745,7 @@
"redirectFormMethod":"طريقة إعادة توجيه الإستمارة", "redirectFormMethod":"طريقة إعادة توجيه الإستمارة",
"redirection":"معالج إعادة التوجيه", "redirection":"معالج إعادة التوجيه",
"reference":"مرجع", "reference":"مرجع",
"refreshSessions":"Refresh sessions API",
"regexp":"التعبير النمطي", "regexp":"التعبير النمطي",
"regexps":"التعبير النمطي", "regexps":"التعبير النمطي",
"register":"تسجيل حساب جديد", "register":"تسجيل حساب جديد",
@ -768,9 +788,9 @@
"returnUrl":"إرجاع اليو آر إل", "returnUrl":"إرجاع اليو آر إل",
"rp":"Relying Party", "rp":"Relying Party",
"rule":"القاعدة", "rule":"القاعدة",
"ruleAuthnLevel":"Required authentication level", "ruleAuthnLevel":"مستوى إثبات الهوية واجب",
"rules":"القواعد", "rules":"القواعد",
"rulesAuthnLevel":"Required authentication levels", "rulesAuthnLevel":"Required auth levels",
"Same":"نفسه", "Same":"نفسه",
"save":"حفظ", "save":"حفظ",
"saveReport":"احفظ التقرير", "saveReport":"احفظ التقرير",

View File

@ -219,6 +219,9 @@
"customModule":"Custom module", "customModule":"Custom module",
"customParams":"Custom module names", "customParams":"Custom module names",
"customPassword":"Custom password module", "customPassword":"Custom password module",
"customPlugins":"Modules list",
"customPluginsNode":"Custom plugins",
"customPluginsParams":"Additional parameters",
"customPortalSkin":"Custom portal skin", "customPortalSkin":"Custom portal skin",
"customRegister":"Custom register module", "customRegister":"Custom register module",
"customToTrace":"REMOTE_CUSTOM", "customToTrace":"REMOTE_CUSTOM",
@ -312,6 +315,9 @@
"forms":"Forms", "forms":"Forms",
"friendlyName":"Friendly name", "friendlyName":"Friendly name",
"generalParameters":"General Parameters", "generalParameters":"General Parameters",
"globalLogout":"Global logout",
"globalLogoutRule":"Activation",
"globalLogoutTimer":"Auto accept time",
"globalStorage":"Apache::Session module", "globalStorage":"Apache::Session module",
"globalStorageOptions":"Apache::Session module parameters", "globalStorageOptions":"Apache::Session module parameters",
"gpgAuthnLevel":"Authentication level", "gpgAuthnLevel":"Authentication level",
@ -506,6 +512,8 @@
"notAValidPerlExpression":"Not a valid Perl expression", "notAValidPerlExpression":"Not a valid Perl expression",
"notification":"Activation", "notification":"Activation",
"notifications":"Notifications", "notifications":"Notifications",
"notification_s":"notification(s)",
"notificationDefaultCond":"Default condition",
"notificationServer":"Notification server", "notificationServer":"Notification server",
"notificationServerDELETE":"DELETE method", "notificationServerDELETE":"DELETE method",
"notificationServerGET":"GET method", "notificationServerGET":"GET method",
@ -515,8 +523,8 @@
"serverNotification":"Server", "serverNotification":"Server",
"notificationCreated":"Notification has been created", "notificationCreated":"Notification has been created",
"notificationDeleted":"Notification deleted", "notificationDeleted":"Notification deleted",
"notificationDone":"notification done", "notificationDone":"Notification done",
"notificationsDone":"notifications done", "notificationsDone":"Notifications done",
"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",
@ -532,6 +540,7 @@
"nullParams":"Null parameters", "nullParams":"Null parameters",
"number":"Number", "number":"Number",
"off":"Off", "off":"Off",
"offlineSessions":"Offline sessions",
"oldValue":"Old value", "oldValue":"Old value",
"on":"On", "on":"On",
"oidcAuthnLevel":"Authentication level", "oidcAuthnLevel":"Authentication level",
@ -543,6 +552,7 @@
"oidcOPMetaDataNode":"OpenID Connect Providers", "oidcOPMetaDataNode":"OpenID Connect Providers",
"oidcOPMetaDataOptions":"Optionen", "oidcOPMetaDataOptions":"Optionen",
"oidcRPMetaDataOptionsAuthentication":"Authentication", "oidcRPMetaDataOptionsAuthentication":"Authentication",
"oidcRPMetaDataOptionsAllowOffline":"Allow offline access",
"oidcOPMetaDataOptionsCheckJWTSignature":"Check JWT signature", "oidcOPMetaDataOptionsCheckJWTSignature":"Check JWT signature",
"oidcOPMetaDataOptionsClientID":"Client ID", "oidcOPMetaDataOptionsClientID":"Client ID",
"oidcOPMetaDataOptionsClientSecret":"Client secret", "oidcOPMetaDataOptionsClientSecret":"Client secret",
@ -571,7 +581,8 @@
"oidcRPMetaDataExportedVars":"Exported attributes", "oidcRPMetaDataExportedVars":"Exported attributes",
"oidcRPMetaDataNode":"OpenID Connect Relying Parties", "oidcRPMetaDataNode":"OpenID Connect Relying Parties",
"oidcRPMetaDataOptions":"Options", "oidcRPMetaDataOptions":"Options",
"oidcRPMetaDataOptionsAccessTokenExpiration":"Access token expiration", "oidcRPMetaDataOptionsAccessTokenExpiration":"Access Token expiration",
"oidcRPMetaDataOptionsAuthorizationCodeExpiration":"Authorization Code expiration",
"oidcRPMetaDataOptionsBypassConsent":"Bypass consent", "oidcRPMetaDataOptionsBypassConsent":"Bypass consent",
"oidcRPMetaDataOptionsClientID":"Client ID", "oidcRPMetaDataOptionsClientID":"Client ID",
"oidcRPMetaDataOptionsClientSecret":"Client secret", "oidcRPMetaDataOptionsClientSecret":"Client secret",
@ -580,6 +591,9 @@
"oidcRPMetaDataOptionsIcon":"Logo", "oidcRPMetaDataOptionsIcon":"Logo",
"oidcRPMetaDataOptionsIDTokenExpiration":"ID Token expiration", "oidcRPMetaDataOptionsIDTokenExpiration":"ID Token expiration",
"oidcRPMetaDataOptionsIDTokenSignAlg":"ID Token signature algorithm", "oidcRPMetaDataOptionsIDTokenSignAlg":"ID Token signature algorithm",
"oidcRPMetaDataOptionsIDTokenForceClaims":"Force claims to be returned in ID Token",
"oidcRPMetaDataOptionsOfflineSessionExpiration":"Offline session expiration",
"oidcRPMetaDataOptionsRefreshToken":"Use refresh tokens",
"oidcRPMetaDataOptionsUserIDAttr":"User attribute", "oidcRPMetaDataOptionsUserIDAttr":"User attribute",
"oidcRPName":"OpenID Connect RP Name", "oidcRPName":"OpenID Connect RP Name",
"oidcRPStateTimeout":"State session timeout", "oidcRPStateTimeout":"State session timeout",
@ -599,6 +613,10 @@
"oidcServicePrivateKeySig":"Signing private key", "oidcServicePrivateKeySig":"Signing private key",
"oidcServicePublicKeySig":"Signing public key", "oidcServicePublicKeySig":"Signing public key",
"oidcServiceKeyIdSig":"Signing key ID", "oidcServiceKeyIdSig":"Signing key ID",
"oidcServiceAuthorizationCodeExpiration":"Authorization Code expiration",
"oidcServiceAccessTokenExpiration":"Access Token expiration",
"oidcServiceIDTokenExpiration":"ID Token expiration",
"oidcServiceOfflineSessionExpiration":"Offline session expiration",
"oidcStorage":"Sessions module name", "oidcStorage":"Sessions module name",
"oidcStorageOptions":"Sessions module options", "oidcStorageOptions":"Sessions module options",
"oidcOPMetaDataNodes":"OpenID Connect Providers", "oidcOPMetaDataNodes":"OpenID Connect Providers",
@ -619,6 +637,7 @@
"oidcServiceAllowAuthorizationCodeFlow":"Authorization Code Flow", "oidcServiceAllowAuthorizationCodeFlow":"Authorization Code Flow",
"oidcServiceAllowImplicitFlow":"Implicit Flow", "oidcServiceAllowImplicitFlow":"Implicit Flow",
"oidcServiceAllowHybridFlow":"Hybrid Flow", "oidcServiceAllowHybridFlow":"Hybrid Flow",
"oidcServiceAllowOffline":"Allow offline access",
"ok":"OK", "ok":"OK",
"oldNotifFormat":"Use old XML format", "oldNotifFormat":"Use old XML format",
"openIdAttr":"OpenID login", "openIdAttr":"OpenID login",
@ -725,6 +744,7 @@
"redirectFormMethod":"Method for redirect form", "redirectFormMethod":"Method for redirect form",
"redirection":"Handler redirections", "redirection":"Handler redirections",
"reference":"Reference", "reference":"Reference",
"refreshSessions":"Refresh sessions API",
"regexp":"Regular expression", "regexp":"Regular expression",
"regexps":"Regular expressions", "regexps":"Regular expressions",
"register":"Register new account", "register":"Register new account",
@ -769,7 +789,7 @@
"rule":"Rule", "rule":"Rule",
"ruleAuthnLevel":"Required authentication level", "ruleAuthnLevel":"Required authentication level",
"rules":"Regeln", "rules":"Regeln",
"rulesAuthnLevel":"Required authentication levels", "rulesAuthnLevel":"Required auth levels",
"Same":"Same", "Same":"Same",
"save":"Save", "save":"Save",
"saveReport":"Save report", "saveReport":"Save report",

View File

@ -219,6 +219,9 @@
"customModule":"Custom module", "customModule":"Custom module",
"customParams":"Custom module names", "customParams":"Custom module names",
"customPassword":"Custom password module", "customPassword":"Custom password module",
"customPlugins":"Modules list",
"customPluginsNode":"Custom plugins",
"customPluginsParams":"Additional parameters",
"customPortalSkin":"Custom portal skin", "customPortalSkin":"Custom portal skin",
"customRegister":"Custom register module", "customRegister":"Custom register module",
"customToTrace":"REMOTE_CUSTOM", "customToTrace":"REMOTE_CUSTOM",
@ -312,6 +315,9 @@
"forms":"Forms", "forms":"Forms",
"friendlyName":"Friendly name", "friendlyName":"Friendly name",
"generalParameters":"General Parameters", "generalParameters":"General Parameters",
"globalLogout":"Global logout",
"globalLogoutRule":"Activation",
"globalLogoutTimer":"Auto accept time",
"globalStorage":"Apache::Session module", "globalStorage":"Apache::Session module",
"globalStorageOptions":"Apache::Session module parameters", "globalStorageOptions":"Apache::Session module parameters",
"gpgAuthnLevel":"Authentication level", "gpgAuthnLevel":"Authentication level",
@ -506,6 +512,8 @@
"notAValidPerlExpression":"Not a valid Perl expression", "notAValidPerlExpression":"Not a valid Perl expression",
"notification":"Activation", "notification":"Activation",
"notifications":"Notifications", "notifications":"Notifications",
"notification_s":"notification(s)",
"notificationDefaultCond":"Default condition",
"notificationServer":"Notification server", "notificationServer":"Notification server",
"notificationServerDELETE":"DELETE method", "notificationServerDELETE":"DELETE method",
"notificationServerGET":"GET method", "notificationServerGET":"GET method",
@ -515,8 +523,8 @@
"serverNotification":"Server", "serverNotification":"Server",
"notificationCreated":"Notification has been created", "notificationCreated":"Notification has been created",
"notificationDeleted":"Notification deleted", "notificationDeleted":"Notification deleted",
"notificationDone":"notification done", "notificationDone":"Notification done",
"notificationsDone":"notifications done", "notificationsDone":"Notifications done",
"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",
@ -532,6 +540,7 @@
"nullParams":"Null parameters", "nullParams":"Null parameters",
"number":"Number", "number":"Number",
"off":"Off", "off":"Off",
"offlineSessions":"Offline sessions",
"oldValue":"Old value", "oldValue":"Old value",
"on":"On", "on":"On",
"oidcAuthnLevel":"Authentication level", "oidcAuthnLevel":"Authentication level",
@ -543,6 +552,7 @@
"oidcOPMetaDataNode":"OpenID Connect Providers", "oidcOPMetaDataNode":"OpenID Connect Providers",
"oidcOPMetaDataOptions":"Options", "oidcOPMetaDataOptions":"Options",
"oidcRPMetaDataOptionsAuthentication":"Authentication", "oidcRPMetaDataOptionsAuthentication":"Authentication",
"oidcRPMetaDataOptionsAllowOffline":"Allow offline access",
"oidcOPMetaDataOptionsCheckJWTSignature":"Check JWT signature", "oidcOPMetaDataOptionsCheckJWTSignature":"Check JWT signature",
"oidcOPMetaDataOptionsClientID":"Client ID", "oidcOPMetaDataOptionsClientID":"Client ID",
"oidcOPMetaDataOptionsClientSecret":"Client secret", "oidcOPMetaDataOptionsClientSecret":"Client secret",
@ -571,7 +581,8 @@
"oidcRPMetaDataExportedVars":"Exported attributes", "oidcRPMetaDataExportedVars":"Exported attributes",
"oidcRPMetaDataNode":"OpenID Connect Relying Parties", "oidcRPMetaDataNode":"OpenID Connect Relying Parties",
"oidcRPMetaDataOptions":"Options", "oidcRPMetaDataOptions":"Options",
"oidcRPMetaDataOptionsAccessTokenExpiration":"Access token expiration", "oidcRPMetaDataOptionsAccessTokenExpiration":"Access Token expiration",
"oidcRPMetaDataOptionsAuthorizationCodeExpiration":"Authorization Code expiration",
"oidcRPMetaDataOptionsBypassConsent":"Bypass consent", "oidcRPMetaDataOptionsBypassConsent":"Bypass consent",
"oidcRPMetaDataOptionsClientID":"Client ID", "oidcRPMetaDataOptionsClientID":"Client ID",
"oidcRPMetaDataOptionsClientSecret":"Client secret", "oidcRPMetaDataOptionsClientSecret":"Client secret",
@ -580,6 +591,9 @@
"oidcRPMetaDataOptionsIcon":"Logo", "oidcRPMetaDataOptionsIcon":"Logo",
"oidcRPMetaDataOptionsIDTokenExpiration":"ID Token expiration", "oidcRPMetaDataOptionsIDTokenExpiration":"ID Token expiration",
"oidcRPMetaDataOptionsIDTokenSignAlg":"ID Token signature algorithm", "oidcRPMetaDataOptionsIDTokenSignAlg":"ID Token signature algorithm",
"oidcRPMetaDataOptionsIDTokenForceClaims":"Force claims to be returned in ID Token",
"oidcRPMetaDataOptionsOfflineSessionExpiration":"Offline session expiration",
"oidcRPMetaDataOptionsRefreshToken":"Use refresh tokens",
"oidcRPMetaDataOptionsUserIDAttr":"User attribute", "oidcRPMetaDataOptionsUserIDAttr":"User attribute",
"oidcRPName":"OpenID Connect RP Name", "oidcRPName":"OpenID Connect RP Name",
"oidcRPStateTimeout":"State session timeout", "oidcRPStateTimeout":"State session timeout",
@ -599,6 +613,10 @@
"oidcServicePrivateKeySig":"Signing private key", "oidcServicePrivateKeySig":"Signing private key",
"oidcServicePublicKeySig":"Signing public key", "oidcServicePublicKeySig":"Signing public key",
"oidcServiceKeyIdSig":"Signing key ID", "oidcServiceKeyIdSig":"Signing key ID",
"oidcServiceAuthorizationCodeExpiration":"Authorization Code expiration",
"oidcServiceAccessTokenExpiration":"Access Token expiration",
"oidcServiceIDTokenExpiration":"ID Token expiration",
"oidcServiceOfflineSessionExpiration":"Offline session expiration",
"oidcStorage":"Sessions module name", "oidcStorage":"Sessions module name",
"oidcStorageOptions":"Sessions module options", "oidcStorageOptions":"Sessions module options",
"oidcOPMetaDataNodes":"OpenID Connect Providers", "oidcOPMetaDataNodes":"OpenID Connect Providers",
@ -619,6 +637,7 @@
"oidcServiceAllowAuthorizationCodeFlow":"Authorization Code Flow", "oidcServiceAllowAuthorizationCodeFlow":"Authorization Code Flow",
"oidcServiceAllowImplicitFlow":"Implicit Flow", "oidcServiceAllowImplicitFlow":"Implicit Flow",
"oidcServiceAllowHybridFlow":"Hybrid Flow", "oidcServiceAllowHybridFlow":"Hybrid Flow",
"oidcServiceAllowOffline":"Allow offline access",
"ok":"OK", "ok":"OK",
"oldNotifFormat":"Use old XML format", "oldNotifFormat":"Use old XML format",
"openIdAttr":"OpenID login", "openIdAttr":"OpenID login",
@ -725,6 +744,7 @@
"redirectFormMethod":"Method for redirect form", "redirectFormMethod":"Method for redirect form",
"redirection":"Handler redirections", "redirection":"Handler redirections",
"reference":"Reference", "reference":"Reference",
"refreshSessions":"Refresh sessions API",
"regexp":"Regular expression", "regexp":"Regular expression",
"regexps":"Regular expressions", "regexps":"Regular expressions",
"register":"Register new account", "register":"Register new account",
@ -769,7 +789,7 @@
"rule":"Rule", "rule":"Rule",
"ruleAuthnLevel":"Required authentication level", "ruleAuthnLevel":"Required authentication level",
"rules":"Rules", "rules":"Rules",
"rulesAuthnLevel":"Required authentication levels", "rulesAuthnLevel":"Required auth levels",
"Same":"Same", "Same":"Same",
"save":"Save", "save":"Save",
"saveReport":"Save report", "saveReport":"Save report",

View File

@ -219,6 +219,9 @@
"customModule":"Module personnalisé", "customModule":"Module personnalisé",
"customParams":"Nom des modules personnalisés", "customParams":"Nom des modules personnalisés",
"customPassword":"Module de mots-de-passe personnalisé", "customPassword":"Module de mots-de-passe personnalisé",
"customPlugins":"Liste des modules",
"customPluginsNode":"Extensions personnalisées",
"customPluginsParams":"Paramètres supplémentaires",
"customPortalSkin":"Style personnalisé du portail", "customPortalSkin":"Style personnalisé du portail",
"customRegister":"Module d'enregistrement personnalisé", "customRegister":"Module d'enregistrement personnalisé",
"customToTrace":"REMOTE_CUSTOM", "customToTrace":"REMOTE_CUSTOM",
@ -312,6 +315,9 @@
"forms":"Formulaires", "forms":"Formulaires",
"friendlyName":"Nom alternatif", "friendlyName":"Nom alternatif",
"generalParameters":"Paramètres généraux", "generalParameters":"Paramètres généraux",
"globalLogout":"Déconnexion globale",
"globalLogoutRule":"Activation",
"globalLogoutTimer":"Délai d'acceptation automatique",
"globalStorage":"Module Apache::Session", "globalStorage":"Module Apache::Session",
"globalStorageOptions":"Paramètres du module Apache::Session", "globalStorageOptions":"Paramètres du module Apache::Session",
"gpgAuthnLevel":"Niveau d'authentification", "gpgAuthnLevel":"Niveau d'authentification",
@ -506,6 +512,8 @@
"notAValidPerlExpression":"Pas une expression Perl valide", "notAValidPerlExpression":"Pas une expression Perl valide",
"notification":"Activation", "notification":"Activation",
"notifications":"Notifications", "notifications":"Notifications",
"notification_s":"notification(s)",
"notificationDefaultCond":"Condition par défaut",
"notificationServer":"Serveur de notifications", "notificationServer":"Serveur de notifications",
"notificationServerDELETE":"Méthode DELETE", "notificationServerDELETE":"Méthode DELETE",
"notificationServerGET":"Méthode GET", "notificationServerGET":"Méthode GET",
@ -515,8 +523,8 @@
"serverNotification":"Serveur", "serverNotification":"Serveur",
"notificationCreated":"La notification a été créée", "notificationCreated":"La notification a été créée",
"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",
"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",
@ -532,6 +540,7 @@
"nullParams":"Paramètres Null", "nullParams":"Paramètres Null",
"number":"Numéro", "number":"Numéro",
"off":"Désactivé", "off":"Désactivé",
"offlineSessions":"Sessions hors ligne",
"oldValue":"Ancienne valeur", "oldValue":"Ancienne valeur",
"on":"Activé", "on":"Activé",
"oidcAuthnLevel":"Niveau d'authentification", "oidcAuthnLevel":"Niveau d'authentification",
@ -543,6 +552,7 @@
"oidcOPMetaDataNode":"Fournisseurs OpenID Connect", "oidcOPMetaDataNode":"Fournisseurs OpenID Connect",
"oidcOPMetaDataOptions":"Options", "oidcOPMetaDataOptions":"Options",
"oidcRPMetaDataOptionsAuthentication":"Authentification", "oidcRPMetaDataOptionsAuthentication":"Authentification",
"oidcRPMetaDataOptionsAllowOffline":"Autoriser l'accès hors ligne",
"oidcOPMetaDataOptionsCheckJWTSignature":"Vérifier la signature des jetons", "oidcOPMetaDataOptionsCheckJWTSignature":"Vérifier la signature des jetons",
"oidcOPMetaDataOptionsClientID":"Identifiant", "oidcOPMetaDataOptionsClientID":"Identifiant",
"oidcOPMetaDataOptionsClientSecret":"Mot de passe", "oidcOPMetaDataOptionsClientSecret":"Mot de passe",
@ -572,6 +582,7 @@
"oidcRPMetaDataNode":"Clients OpenID Connect", "oidcRPMetaDataNode":"Clients OpenID Connect",
"oidcRPMetaDataOptions":"Options", "oidcRPMetaDataOptions":"Options",
"oidcRPMetaDataOptionsAccessTokenExpiration":"Expiration des jetons d'accès", "oidcRPMetaDataOptionsAccessTokenExpiration":"Expiration des jetons d'accès",
"oidcRPMetaDataOptionsAuthorizationCodeExpiration":"Expiration des codes d'autorisation",
"oidcRPMetaDataOptionsBypassConsent":"Contourner le consentement", "oidcRPMetaDataOptionsBypassConsent":"Contourner le consentement",
"oidcRPMetaDataOptionsClientID":"Identifiant", "oidcRPMetaDataOptionsClientID":"Identifiant",
"oidcRPMetaDataOptionsClientSecret":"Mot de passe", "oidcRPMetaDataOptionsClientSecret":"Mot de passe",
@ -580,6 +591,9 @@
"oidcRPMetaDataOptionsIcon":"Logo", "oidcRPMetaDataOptionsIcon":"Logo",
"oidcRPMetaDataOptionsIDTokenExpiration":"Expiration des jetons d'identité", "oidcRPMetaDataOptionsIDTokenExpiration":"Expiration des jetons d'identité",
"oidcRPMetaDataOptionsIDTokenSignAlg":"Algorithme de signature des jetons d'identité", "oidcRPMetaDataOptionsIDTokenSignAlg":"Algorithme de signature des jetons d'identité",
"oidcRPMetaDataOptionsIDTokenForceClaims":"Forcer la publication des attributs dans l'ID Token",
"oidcRPMetaDataOptionsOfflineSessionExpiration":"Expiration des sessions hors-ligne",
"oidcRPMetaDataOptionsRefreshToken":"Utiliser les refresh tokens",
"oidcRPMetaDataOptionsUserIDAttr":"Attribut de l'utilisateur", "oidcRPMetaDataOptionsUserIDAttr":"Attribut de l'utilisateur",
"oidcRPName":"Nom du client OpenID Connect", "oidcRPName":"Nom du client OpenID Connect",
"oidcRPStateTimeout":"Durée d'une session state", "oidcRPStateTimeout":"Durée d'une session state",
@ -599,6 +613,10 @@
"oidcServicePrivateKeySig":"Clef privée de signature", "oidcServicePrivateKeySig":"Clef privée de signature",
"oidcServicePublicKeySig":"Clef publique de signature", "oidcServicePublicKeySig":"Clef publique de signature",
"oidcServiceKeyIdSig":"Identifiant de clef de signature", "oidcServiceKeyIdSig":"Identifiant de clef de signature",
"oidcServiceAuthorizationCodeExpiration":"Expiration des codes d'autorisation",
"oidcServiceAccessTokenExpiration":"Expiration des jetons d'accès",
"oidcServiceIDTokenExpiration":"Expiration des jetons d'identité",
"oidcServiceOfflineSessionExpiration":"Expiration des sessions hors-ligne",
"oidcStorage":"Nom du module des sessions", "oidcStorage":"Nom du module des sessions",
"oidcStorageOptions":"Options du module des sessions", "oidcStorageOptions":"Options du module des sessions",
"oidcOPMetaDataNodes":"Fournisseurs OpenID Connect", "oidcOPMetaDataNodes":"Fournisseurs OpenID Connect",
@ -619,6 +637,7 @@
"oidcServiceAllowAuthorizationCodeFlow":"Authorization Code Flow", "oidcServiceAllowAuthorizationCodeFlow":"Authorization Code Flow",
"oidcServiceAllowImplicitFlow":"Implicit Flow", "oidcServiceAllowImplicitFlow":"Implicit Flow",
"oidcServiceAllowHybridFlow":"Hybrid Flow", "oidcServiceAllowHybridFlow":"Hybrid Flow",
"oidcServiceAllowOffline":"Autoriser l'accès hors ligne",
"ok":"OK", "ok":"OK",
"oldNotifFormat":"Utiliser l'ancien format XML", "oldNotifFormat":"Utiliser l'ancien format XML",
"openIdAttr":"Identifiant OpenID", "openIdAttr":"Identifiant OpenID",
@ -725,6 +744,7 @@
"redirectFormMethod":"Méthode du formulaire de redirection", "redirectFormMethod":"Méthode du formulaire de redirection",
"redirection":"Redirections du Handler", "redirection":"Redirections du Handler",
"reference":"Référence", "reference":"Référence",
"refreshSessions":"API de rafraîchissement des sessions",
"regexp":"Expression régulière", "regexp":"Expression régulière",
"regexps":"Expressions régulières", "regexps":"Expressions régulières",
"register":"Créer un nouveau compte", "register":"Créer un nouveau compte",
@ -769,7 +789,7 @@
"rule":"Règle", "rule":"Règle",
"ruleAuthnLevel":"Niveau d'authentication requis", "ruleAuthnLevel":"Niveau d'authentication requis",
"rules":"Règles", "rules":"Règles",
"rulesAuthnLevel":"Niveaux d'authentification requis", "rulesAuthnLevel":"Niveaux auth requis",
"Same":"Identique", "Same":"Identique",
"save":"Sauver", "save":"Sauver",
"saveReport":"Rapport de sauvegarde", "saveReport":"Rapport de sauvegarde",

View File

@ -219,6 +219,9 @@
"customModule":"Personalizza modulo", "customModule":"Personalizza modulo",
"customParams":"Personalizza i nomi dei moduli", "customParams":"Personalizza i nomi dei moduli",
"customPassword":"Personalizza il modulo password", "customPassword":"Personalizza il modulo password",
"customPlugins":"Modules list",
"customPluginsNode":"Custom plugins",
"customPluginsParams":"Additional parameters",
"customPortalSkin":"Personalizza faccia del portale ", "customPortalSkin":"Personalizza faccia del portale ",
"customRegister":"Personalizza modulo di registro", "customRegister":"Personalizza modulo di registro",
"customToTrace":"REMOTE_CUSTOM", "customToTrace":"REMOTE_CUSTOM",
@ -246,7 +249,7 @@
"dbiUserTable":"Tabella utente", "dbiUserTable":"Tabella utente",
"decryptValue":"Decrypt value", "decryptValue":"Decrypt value",
"decryptValueFunctions":"Decrypt functions", "decryptValueFunctions":"Decrypt functions",
"decryptValueRule":"Use rule", "decryptValueRule":"Utilizza la regola",
"default":"Predefinito", "default":"Predefinito",
"defaultRule":"Regola predefinita", "defaultRule":"Regola predefinita",
"demoModeOn":"Questo gestore viene eseguito in modalità demo", "demoModeOn":"Questo gestore viene eseguito in modalità demo",
@ -312,6 +315,9 @@
"forms":"Moduli", "forms":"Moduli",
"friendlyName":"Nome amichevole", "friendlyName":"Nome amichevole",
"generalParameters":"Parametri generali", "generalParameters":"Parametri generali",
"globalLogout":"Global logout",
"globalLogoutRule":"Activation",
"globalLogoutTimer":"Auto accettazione tempo",
"globalStorage":"Modulo Apache::Session", "globalStorage":"Modulo Apache::Session",
"globalStorageOptions":"Parametri di modulo Apache::Session", "globalStorageOptions":"Parametri di modulo Apache::Session",
"gpgAuthnLevel":"Livello di autenticazione", "gpgAuthnLevel":"Livello di autenticazione",
@ -429,7 +435,7 @@
"loadFromUrl":"Carica a partire dall'URL", "loadFromUrl":"Carica a partire dall'URL",
"localSessionStorage":"Modulo cache", "localSessionStorage":"Modulo cache",
"localSessionStorageOptions":"Opzioni modulo cache", "localSessionStorageOptions":"Opzioni modulo cache",
"locationRules":"Regola di accesso", "locationRules":"Regole di accesso",
"loginHistory":"Cronologia dei login", "loginHistory":"Cronologia dei login",
"loginHistoryEnabled":"Attivazione", "loginHistoryEnabled":"Attivazione",
"logo":"Logo", "logo":"Logo",
@ -506,6 +512,8 @@
"notAValidPerlExpression":"Non una valida espressione Perl", "notAValidPerlExpression":"Non una valida espressione Perl",
"notification":"Attivazione", "notification":"Attivazione",
"notifications":"Notifiche", "notifications":"Notifiche",
"notification_s":"notification(s)",
"notificationDefaultCond":"Default condition",
"notificationServer":"Server di notifica", "notificationServer":"Server di notifica",
"notificationServerDELETE":"DELETE method", "notificationServerDELETE":"DELETE method",
"notificationServerGET":"GET method", "notificationServerGET":"GET method",
@ -532,6 +540,7 @@
"nullParams":"Parametri Null", "nullParams":"Parametri Null",
"number":"Numero", "number":"Numero",
"off":"Off", "off":"Off",
"offlineSessions":"Offline sessions",
"oldValue":"Vecchio valore", "oldValue":"Vecchio valore",
"on":"On", "on":"On",
"oidcAuthnLevel":"Livello di autenticazione", "oidcAuthnLevel":"Livello di autenticazione",
@ -543,6 +552,7 @@
"oidcOPMetaDataNode":"Provider di OpenID Connect", "oidcOPMetaDataNode":"Provider di OpenID Connect",
"oidcOPMetaDataOptions":"Opzioni", "oidcOPMetaDataOptions":"Opzioni",
"oidcRPMetaDataOptionsAuthentication":"Autenticazione", "oidcRPMetaDataOptionsAuthentication":"Autenticazione",
"oidcRPMetaDataOptionsAllowOffline":"Allow offline access",
"oidcOPMetaDataOptionsCheckJWTSignature":"Controllare la firma JWT", "oidcOPMetaDataOptionsCheckJWTSignature":"Controllare la firma JWT",
"oidcOPMetaDataOptionsClientID":"ID Client", "oidcOPMetaDataOptionsClientID":"ID Client",
"oidcOPMetaDataOptionsClientSecret":"Segreto Client", "oidcOPMetaDataOptionsClientSecret":"Segreto Client",
@ -572,6 +582,7 @@
"oidcRPMetaDataNode":"Parti basate su OpenID Connect", "oidcRPMetaDataNode":"Parti basate su OpenID Connect",
"oidcRPMetaDataOptions":"Opzioni", "oidcRPMetaDataOptions":"Opzioni",
"oidcRPMetaDataOptionsAccessTokenExpiration":"Scadenza accesso token", "oidcRPMetaDataOptionsAccessTokenExpiration":"Scadenza accesso token",
"oidcRPMetaDataOptionsAuthorizationCodeExpiration":"Authorization Code expiration",
"oidcRPMetaDataOptionsBypassConsent":"Consenso di bypass", "oidcRPMetaDataOptionsBypassConsent":"Consenso di bypass",
"oidcRPMetaDataOptionsClientID":"ID Client", "oidcRPMetaDataOptionsClientID":"ID Client",
"oidcRPMetaDataOptionsClientSecret":"Segreto Client", "oidcRPMetaDataOptionsClientSecret":"Segreto Client",
@ -580,6 +591,9 @@
"oidcRPMetaDataOptionsIcon":"Logo", "oidcRPMetaDataOptionsIcon":"Logo",
"oidcRPMetaDataOptionsIDTokenExpiration":"Scadenza ID Token", "oidcRPMetaDataOptionsIDTokenExpiration":"Scadenza ID Token",
"oidcRPMetaDataOptionsIDTokenSignAlg":"Algoritmo di firma di identificazione di Token", "oidcRPMetaDataOptionsIDTokenSignAlg":"Algoritmo di firma di identificazione di Token",
"oidcRPMetaDataOptionsIDTokenForceClaims":"Force claims to be returned in ID Token",
"oidcRPMetaDataOptionsOfflineSessionExpiration":"Offline session expiration",
"oidcRPMetaDataOptionsRefreshToken":"Use refresh tokens",
"oidcRPMetaDataOptionsUserIDAttr":"Attributo utente", "oidcRPMetaDataOptionsUserIDAttr":"Attributo utente",
"oidcRPName":"Nome di OpenID Connect RP", "oidcRPName":"Nome di OpenID Connect RP",
"oidcRPStateTimeout":"Durata della sessione stato", "oidcRPStateTimeout":"Durata della sessione stato",
@ -599,6 +613,10 @@
"oidcServicePrivateKeySig":"Firma della chiave privata", "oidcServicePrivateKeySig":"Firma della chiave privata",
"oidcServicePublicKeySig":"Firma della chiave pubblica", "oidcServicePublicKeySig":"Firma della chiave pubblica",
"oidcServiceKeyIdSig":"ID del codice di accesso", "oidcServiceKeyIdSig":"ID del codice di accesso",
"oidcServiceAuthorizationCodeExpiration":"Authorization Code expiration",
"oidcServiceAccessTokenExpiration":"Scadenza accesso token",
"oidcServiceIDTokenExpiration":"Scadenza ID Token",
"oidcServiceOfflineSessionExpiration":"Offline session expiration",
"oidcStorage":"Nome del modulo Sessioni", "oidcStorage":"Nome del modulo Sessioni",
"oidcStorageOptions":"Opzioni del modulo Sessioni", "oidcStorageOptions":"Opzioni del modulo Sessioni",
"oidcOPMetaDataNodes":"Provider di OpenID Connect", "oidcOPMetaDataNodes":"Provider di OpenID Connect",
@ -619,6 +637,7 @@
"oidcServiceAllowAuthorizationCodeFlow":"Flusso del codice di autorizzazione", "oidcServiceAllowAuthorizationCodeFlow":"Flusso del codice di autorizzazione",
"oidcServiceAllowImplicitFlow":"Flusso implicito", "oidcServiceAllowImplicitFlow":"Flusso implicito",
"oidcServiceAllowHybridFlow":"Flusso ibrido", "oidcServiceAllowHybridFlow":"Flusso ibrido",
"oidcServiceAllowOffline":"Allow offline access",
"ok":"OK", "ok":"OK",
"oldNotifFormat":"Utilizza il vecchio formato XML", "oldNotifFormat":"Utilizza il vecchio formato XML",
"openIdAttr":"Login OpenID", "openIdAttr":"Login OpenID",
@ -707,12 +726,12 @@
"proxyUseSoap":"Usa SOAP invece di REST", "proxyUseSoap":"Usa SOAP invece di REST",
"publicKey":"Chiave pubblica", "publicKey":"Chiave pubblica",
"purgeNotification":"Elimina definitivamente la notifica", "purgeNotification":"Elimina definitivamente la notifica",
"radius2f":"Radius second factor", "radius2f":"Radius secondo fattore",
"radius2fActivation":"Attivazione", "radius2fActivation":"Attivazione",
"radius2fServer":"Nome host del server", "radius2fServer":"Nome host del server",
"radius2fSecret":"Segreto condiviso", "radius2fSecret":"Segreto condiviso",
"radius2fUsernameSessionKey":"Session key containing login", "radius2fUsernameSessionKey":"Session key containing login",
"radius2fTimeout":"Authentication timeout", "radius2fTimeout":"Timeout di autenticazione",
"radius2fAuthnLevel":"Livello di autenticazione", "radius2fAuthnLevel":"Livello di autenticazione",
"radius2fLogo":"Logo", "radius2fLogo":"Logo",
"radius2fLabel":"Label", "radius2fLabel":"Label",
@ -725,6 +744,7 @@
"redirectFormMethod":"Metodo per il modulo di reindirizzamento", "redirectFormMethod":"Metodo per il modulo di reindirizzamento",
"redirection":"Redirezioni del gestore", "redirection":"Redirezioni del gestore",
"reference":"Riferimento", "reference":"Riferimento",
"refreshSessions":"Refresh sessions API",
"regexp":"Espressione regolare", "regexp":"Espressione regolare",
"regexps":"Espressioni regolari", "regexps":"Espressioni regolari",
"register":"Registra nuovo account", "register":"Registra nuovo account",
@ -767,9 +787,9 @@
"returnUrl":"URL di ritorno", "returnUrl":"URL di ritorno",
"rp":"Parte facente affidamento", "rp":"Parte facente affidamento",
"rule":"Regola", "rule":"Regola",
"ruleAuthnLevel":"Required authentication level", "ruleAuthnLevel":"Livello di autenticazione richiesto",
"rules":"Regole", "rules":"Regole",
"rulesAuthnLevel":"Required authentication levels", "rulesAuthnLevel":"Required auth levels",
"Same":"Stesso", "Same":"Stesso",
"save":"Salva", "save":"Salva",
"saveReport":"Salva report", "saveReport":"Salva report",

File diff suppressed because it is too large Load Diff

View File

@ -219,6 +219,9 @@
"customModule":"Mô đun tùy chỉnh", "customModule":"Mô đun tùy chỉnh",
"customParams":"Tên mô-đun tùy chỉnh", "customParams":"Tên mô-đun tùy chỉnh",
"customPassword":"Mô đun mật khẩu tùy chỉnh", "customPassword":"Mô đun mật khẩu tùy chỉnh",
"customPlugins":"Modules list",
"customPluginsNode":"Custom plugins",
"customPluginsParams":"Additional parameters",
"customPortalSkin":"Tùy chỉnh giao diện cổng thông tin", "customPortalSkin":"Tùy chỉnh giao diện cổng thông tin",
"customRegister":"Module đăng ký tùy chỉnh", "customRegister":"Module đăng ký tùy chỉnh",
"customToTrace":"REMOTE_CUSTOM", "customToTrace":"REMOTE_CUSTOM",
@ -312,6 +315,9 @@
"forms":"Biểu mẫu", "forms":"Biểu mẫu",
"friendlyName":"Tên thân thiện", "friendlyName":"Tên thân thiện",
"generalParameters":"Thông số chung", "generalParameters":"Thông số chung",
"globalLogout":"Global logout",
"globalLogoutRule":"Activation",
"globalLogoutTimer":"Tự động chấp nhận thời gian",
"globalStorage":"Mô đun Apache :: Session", "globalStorage":"Mô đun Apache :: Session",
"globalStorageOptions":"Tham số mô đun Apache :: Session ", "globalStorageOptions":"Tham số mô đun Apache :: Session ",
"gpgAuthnLevel":"Mức xác thực", "gpgAuthnLevel":"Mức xác thực",
@ -429,7 +435,7 @@
"loadFromUrl":"Nạp từ URL", "loadFromUrl":"Nạp từ URL",
"localSessionStorage":"Mô-đun bộ nhớ cache", "localSessionStorage":"Mô-đun bộ nhớ cache",
"localSessionStorageOptions":"Tùy chọn mô-đun bộ nhớ cache", "localSessionStorageOptions":"Tùy chọn mô-đun bộ nhớ cache",
"locationRules":"Quy tắc truy cập", "locationRules":"Access rules",
"loginHistory":"Lịch sử đăng nhập", "loginHistory":"Lịch sử đăng nhập",
"loginHistoryEnabled":"Kích hoạt", "loginHistoryEnabled":"Kích hoạt",
"logo":"Logo", "logo":"Logo",
@ -506,6 +512,8 @@
"notAValidPerlExpression":"Không phải là một biểu thức Perl hợp lệ", "notAValidPerlExpression":"Không phải là một biểu thức Perl hợp lệ",
"notification":"Kích hoạt", "notification":"Kích hoạt",
"notifications":"Thông báo", "notifications":"Thông báo",
"notification_s":"notification(s)",
"notificationDefaultCond":"Default condition",
"notificationServer":"Máy chủ Thông báo", "notificationServer":"Máy chủ Thông báo",
"notificationServerDELETE":"DELETE method", "notificationServerDELETE":"DELETE method",
"notificationServerGET":"GET method", "notificationServerGET":"GET method",
@ -515,8 +523,8 @@
"serverNotification":"Server", "serverNotification":"Server",
"notificationCreated":"Thông báo đã được tạo ra", "notificationCreated":"Thông báo đã được tạo ra",
"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",
"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",
@ -532,6 +540,7 @@
"nullParams":"Tham số Null", "nullParams":"Tham số Null",
"number":"Số", "number":"Số",
"off":"Tắt", "off":"Tắt",
"offlineSessions":"Offline sessions",
"oldValue":"Giá trị cũ", "oldValue":"Giá trị cũ",
"on":"Vào", "on":"Vào",
"oidcAuthnLevel":"Mức xác thực", "oidcAuthnLevel":"Mức xác thực",
@ -543,6 +552,7 @@
"oidcOPMetaDataNode":"Nhà cung cấp Kết nối OpenID", "oidcOPMetaDataNode":"Nhà cung cấp Kết nối OpenID",
"oidcOPMetaDataOptions":"Tùy chọn", "oidcOPMetaDataOptions":"Tùy chọn",
"oidcRPMetaDataOptionsAuthentication":"Xác thực", "oidcRPMetaDataOptionsAuthentication":"Xác thực",
"oidcRPMetaDataOptionsAllowOffline":"Allow offline access",
"oidcOPMetaDataOptionsCheckJWTSignature":"Kiểm tra chữ ký JWT", "oidcOPMetaDataOptionsCheckJWTSignature":"Kiểm tra chữ ký JWT",
"oidcOPMetaDataOptionsClientID":"Client ID", "oidcOPMetaDataOptionsClientID":"Client ID",
"oidcOPMetaDataOptionsClientSecret":"Trình khách bí mật", "oidcOPMetaDataOptionsClientSecret":"Trình khách bí mật",
@ -571,7 +581,8 @@
"oidcRPMetaDataExportedVars":"Biến đã được xuất", "oidcRPMetaDataExportedVars":"Biến đã được xuất",
"oidcRPMetaDataNode":"OpenID Connect Relying Parties", "oidcRPMetaDataNode":"OpenID Connect Relying Parties",
"oidcRPMetaDataOptions":"Tùy chọn", "oidcRPMetaDataOptions":"Tùy chọn",
"oidcRPMetaDataOptionsAccessTokenExpiration":"Hết hạn truy cập Token", "oidcRPMetaDataOptionsAccessTokenExpiration":"Access Token expiration",
"oidcRPMetaDataOptionsAuthorizationCodeExpiration":"Authorization Code expiration",
"oidcRPMetaDataOptionsBypassConsent":"Bỏ qua sự đồng ý", "oidcRPMetaDataOptionsBypassConsent":"Bỏ qua sự đồng ý",
"oidcRPMetaDataOptionsClientID":"Client ID", "oidcRPMetaDataOptionsClientID":"Client ID",
"oidcRPMetaDataOptionsClientSecret":"Trình khách bí mật", "oidcRPMetaDataOptionsClientSecret":"Trình khách bí mật",
@ -580,6 +591,9 @@
"oidcRPMetaDataOptionsIcon":"Logo", "oidcRPMetaDataOptionsIcon":"Logo",
"oidcRPMetaDataOptionsIDTokenExpiration":"ID Token hết hạn", "oidcRPMetaDataOptionsIDTokenExpiration":"ID Token hết hạn",
"oidcRPMetaDataOptionsIDTokenSignAlg":"Thuật toán chữ ký ID Token", "oidcRPMetaDataOptionsIDTokenSignAlg":"Thuật toán chữ ký ID Token",
"oidcRPMetaDataOptionsIDTokenForceClaims":"Force claims to be returned in ID Token",
"oidcRPMetaDataOptionsOfflineSessionExpiration":"Offline session expiration",
"oidcRPMetaDataOptionsRefreshToken":"Use refresh tokens",
"oidcRPMetaDataOptionsUserIDAttr":"thuộc tính người dùng", "oidcRPMetaDataOptionsUserIDAttr":"thuộc tính người dùng",
"oidcRPName":"OpenID Connect RP Name", "oidcRPName":"OpenID Connect RP Name",
"oidcRPStateTimeout":"Thời gian chờ của trạng thái phiên làm việc", "oidcRPStateTimeout":"Thời gian chờ của trạng thái phiên làm việc",
@ -599,6 +613,10 @@
"oidcServicePrivateKeySig":"Ký khóa cá nhân", "oidcServicePrivateKeySig":"Ký khóa cá nhân",
"oidcServicePublicKeySig":"Ký khóa công khai", "oidcServicePublicKeySig":"Ký khóa công khai",
"oidcServiceKeyIdSig":"Khóa ID chính", "oidcServiceKeyIdSig":"Khóa ID chính",
"oidcServiceAuthorizationCodeExpiration":"Authorization Code expiration",
"oidcServiceAccessTokenExpiration":"Access Token expiration",
"oidcServiceIDTokenExpiration":"ID Token expiration",
"oidcServiceOfflineSessionExpiration":"Offline session expiration",
"oidcStorage":"Tên mô-đun phiên", "oidcStorage":"Tên mô-đun phiên",
"oidcStorageOptions":"Tùy chọn mô-đun phiên", "oidcStorageOptions":"Tùy chọn mô-đun phiên",
"oidcOPMetaDataNodes":"Nhà cung cấp Kết nối OpenID", "oidcOPMetaDataNodes":"Nhà cung cấp Kết nối OpenID",
@ -619,6 +637,7 @@
"oidcServiceAllowAuthorizationCodeFlow":"Dòng mã ủy quyền", "oidcServiceAllowAuthorizationCodeFlow":"Dòng mã ủy quyền",
"oidcServiceAllowImplicitFlow":"Dòng chảy ngầm", "oidcServiceAllowImplicitFlow":"Dòng chảy ngầm",
"oidcServiceAllowHybridFlow":"Dòng chảy hỗn hợp", "oidcServiceAllowHybridFlow":"Dòng chảy hỗn hợp",
"oidcServiceAllowOffline":"Allow offline access",
"ok":"OK", "ok":"OK",
"oldNotifFormat":"Sử dụng định dạng XML cũ", "oldNotifFormat":"Sử dụng định dạng XML cũ",
"openIdAttr":"Đăng nhập OpenID", "openIdAttr":"Đăng nhập OpenID",
@ -725,6 +744,7 @@
"redirectFormMethod":"Phương pháp chuyển hướng mẫu", "redirectFormMethod":"Phương pháp chuyển hướng mẫu",
"redirection":"chuyển hướng trình điều khiển", "redirection":"chuyển hướng trình điều khiển",
"reference":"Tham khảo", "reference":"Tham khảo",
"refreshSessions":"Refresh sessions API",
"regexp":"Biểu thức chính quy", "regexp":"Biểu thức chính quy",
"regexps":"Biểu thức thông thường", "regexps":"Biểu thức thông thường",
"register":"Đăng ký tài khoản mới", "register":"Đăng ký tài khoản mới",
@ -769,7 +789,7 @@
"rule":"Quy tắc", "rule":"Quy tắc",
"ruleAuthnLevel":"Required authentication level", "ruleAuthnLevel":"Required authentication level",
"rules":"Quy tắc", "rules":"Quy tắc",
"rulesAuthnLevel":"Required authentication levels", "rulesAuthnLevel":"Required auth levels",
"Same":"Tương tự", "Same":"Tương tự",
"save":"Lưu", "save":"Lưu",
"saveReport":"Lưu báo cáo", "saveReport":"Lưu báo cáo",

View File

@ -219,6 +219,9 @@
"customModule":"定制模块", "customModule":"定制模块",
"customParams":"定制模块名称", "customParams":"定制模块名称",
"customPassword":"Custom password module", "customPassword":"Custom password module",
"customPlugins":"Modules list",
"customPluginsNode":"Custom plugins",
"customPluginsParams":"Additional parameters",
"customPortalSkin":"Custom portal skin", "customPortalSkin":"Custom portal skin",
"customRegister":"Custom register module", "customRegister":"Custom register module",
"customToTrace":"REMOTE_CUSTOM", "customToTrace":"REMOTE_CUSTOM",
@ -312,6 +315,9 @@
"forms":"Forms", "forms":"Forms",
"friendlyName":"Friendly name", "friendlyName":"Friendly name",
"generalParameters":"通用参数", "generalParameters":"通用参数",
"globalLogout":"Global logout",
"globalLogoutRule":"Activation",
"globalLogoutTimer":"自动接收时间",
"globalStorage":"Apache::Session 模块", "globalStorage":"Apache::Session 模块",
"globalStorageOptions":"Apache::Session module parameters", "globalStorageOptions":"Apache::Session module parameters",
"gpgAuthnLevel":"认证等级", "gpgAuthnLevel":"认证等级",
@ -506,6 +512,8 @@
"notAValidPerlExpression":"Not a valid Perl expression", "notAValidPerlExpression":"Not a valid Perl expression",
"notification":"激活", "notification":"激活",
"notifications":"Notifications", "notifications":"Notifications",
"notification_s":"notification(s)",
"notificationDefaultCond":"Default condition",
"notificationServer":"Notification server", "notificationServer":"Notification server",
"notificationServerDELETE":"DELETE method", "notificationServerDELETE":"DELETE method",
"notificationServerGET":"GET method", "notificationServerGET":"GET method",
@ -515,8 +523,8 @@
"serverNotification":"Server", "serverNotification":"Server",
"notificationCreated":"Notification has been created", "notificationCreated":"Notification has been created",
"notificationDeleted":"Notification deleted", "notificationDeleted":"Notification deleted",
"notificationDone":"notification done", "notificationDone":"Notification done",
"notificationsDone":"notifications done", "notificationsDone":"Notifications done",
"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",
@ -532,6 +540,7 @@
"nullParams":"Null parameters", "nullParams":"Null parameters",
"number":"Number", "number":"Number",
"off":"Off", "off":"Off",
"offlineSessions":"Offline sessions",
"oldValue":"Old value", "oldValue":"Old value",
"on":"On", "on":"On",
"oidcAuthnLevel":"认证等级", "oidcAuthnLevel":"认证等级",
@ -543,6 +552,7 @@
"oidcOPMetaDataNode":"OpenID Connect Providers", "oidcOPMetaDataNode":"OpenID Connect Providers",
"oidcOPMetaDataOptions":"Options", "oidcOPMetaDataOptions":"Options",
"oidcRPMetaDataOptionsAuthentication":"Authentication", "oidcRPMetaDataOptionsAuthentication":"Authentication",
"oidcRPMetaDataOptionsAllowOffline":"Allow offline access",
"oidcOPMetaDataOptionsCheckJWTSignature":"Check JWT signature", "oidcOPMetaDataOptionsCheckJWTSignature":"Check JWT signature",
"oidcOPMetaDataOptionsClientID":"Client ID", "oidcOPMetaDataOptionsClientID":"Client ID",
"oidcOPMetaDataOptionsClientSecret":"Client secret", "oidcOPMetaDataOptionsClientSecret":"Client secret",
@ -571,7 +581,8 @@
"oidcRPMetaDataExportedVars":"Exported attributes", "oidcRPMetaDataExportedVars":"Exported attributes",
"oidcRPMetaDataNode":"OpenID Connect Relying Parties", "oidcRPMetaDataNode":"OpenID Connect Relying Parties",
"oidcRPMetaDataOptions":"Options", "oidcRPMetaDataOptions":"Options",
"oidcRPMetaDataOptionsAccessTokenExpiration":"Access token expiration", "oidcRPMetaDataOptionsAccessTokenExpiration":"Access Token expiration",
"oidcRPMetaDataOptionsAuthorizationCodeExpiration":"Authorization Code expiration",
"oidcRPMetaDataOptionsBypassConsent":"Bypass consent", "oidcRPMetaDataOptionsBypassConsent":"Bypass consent",
"oidcRPMetaDataOptionsClientID":"Client ID", "oidcRPMetaDataOptionsClientID":"Client ID",
"oidcRPMetaDataOptionsClientSecret":"Client secret", "oidcRPMetaDataOptionsClientSecret":"Client secret",
@ -580,6 +591,9 @@
"oidcRPMetaDataOptionsIcon":"Logo", "oidcRPMetaDataOptionsIcon":"Logo",
"oidcRPMetaDataOptionsIDTokenExpiration":"ID Token expiration", "oidcRPMetaDataOptionsIDTokenExpiration":"ID Token expiration",
"oidcRPMetaDataOptionsIDTokenSignAlg":"ID Token signature algorithm", "oidcRPMetaDataOptionsIDTokenSignAlg":"ID Token signature algorithm",
"oidcRPMetaDataOptionsIDTokenForceClaims":"Force claims to be returned in ID Token",
"oidcRPMetaDataOptionsOfflineSessionExpiration":"Offline session expiration",
"oidcRPMetaDataOptionsRefreshToken":"Use refresh tokens",
"oidcRPMetaDataOptionsUserIDAttr":"User attribute", "oidcRPMetaDataOptionsUserIDAttr":"User attribute",
"oidcRPName":"OpenID Connect RP Name", "oidcRPName":"OpenID Connect RP Name",
"oidcRPStateTimeout":"State session timeout", "oidcRPStateTimeout":"State session timeout",
@ -599,6 +613,10 @@
"oidcServicePrivateKeySig":"Signing private key", "oidcServicePrivateKeySig":"Signing private key",
"oidcServicePublicKeySig":"Signing public key", "oidcServicePublicKeySig":"Signing public key",
"oidcServiceKeyIdSig":"Signing key ID", "oidcServiceKeyIdSig":"Signing key ID",
"oidcServiceAuthorizationCodeExpiration":"Authorization Code expiration",
"oidcServiceAccessTokenExpiration":"Access Token expiration",
"oidcServiceIDTokenExpiration":"ID Token expiration",
"oidcServiceOfflineSessionExpiration":"Offline session expiration",
"oidcStorage":"Sessions module name", "oidcStorage":"Sessions module name",
"oidcStorageOptions":"Sessions module options", "oidcStorageOptions":"Sessions module options",
"oidcOPMetaDataNodes":"OpenID Connect Providers", "oidcOPMetaDataNodes":"OpenID Connect Providers",
@ -619,6 +637,7 @@
"oidcServiceAllowAuthorizationCodeFlow":"Authorization Code Flow", "oidcServiceAllowAuthorizationCodeFlow":"Authorization Code Flow",
"oidcServiceAllowImplicitFlow":"Implicit Flow", "oidcServiceAllowImplicitFlow":"Implicit Flow",
"oidcServiceAllowHybridFlow":"Hybrid Flow", "oidcServiceAllowHybridFlow":"Hybrid Flow",
"oidcServiceAllowOffline":"Allow offline access",
"ok":"OK", "ok":"OK",
"oldNotifFormat":"Use old XML format", "oldNotifFormat":"Use old XML format",
"openIdAttr":"OpenID login", "openIdAttr":"OpenID login",
@ -725,6 +744,7 @@
"redirectFormMethod":"Method for redirect form", "redirectFormMethod":"Method for redirect form",
"redirection":"Handler redirections", "redirection":"Handler redirections",
"reference":"Reference", "reference":"Reference",
"refreshSessions":"Refresh sessions API",
"regexp":"Regular expression", "regexp":"Regular expression",
"regexps":"Regular expressions", "regexps":"Regular expressions",
"register":"Register new account", "register":"Register new account",
@ -769,7 +789,7 @@
"rule":"Rule", "rule":"Rule",
"ruleAuthnLevel":"Required authentication level", "ruleAuthnLevel":"Required authentication level",
"rules":"Rules", "rules":"Rules",
"rulesAuthnLevel":"Required authentication levels", "rulesAuthnLevel":"Required auth levels",
"Same":"Same", "Same":"Same",
"save":"Save", "save":"Save",
"saveReport":"Save report", "saveReport":"Save report",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -21,6 +21,7 @@
</ul> </ul>
</div> </div>
</div> </div>
<div ng-show="data.length!=0" class="text-center"><p class="badge">{{total}} <span trspan="notification_s"></span></p></div>
<div class="region region-sidebar-first"> <div class="region region-sidebar-first">
<section id="block-superfish-1" class="block block-superfish clearfix"> <section id="block-superfish-1" class="block block-superfish clearfix">
<div ui-tree data-drag-enabled="false" id="tree-root"> <div ui-tree data-drag-enabled="false" id="tree-root">

View File

@ -26,6 +26,7 @@
</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"><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>
</ul> </ul>
</div> </div>
</div> </div>

View File

@ -35,7 +35,7 @@ foreach my $lang (@langs) {
my @l1 = sort keys %$keys; my @l1 = sort keys %$keys;
my @l2 = sort keys %$l; my @l2 = sort keys %$l;
ok( $#l1 == $#l2, ok( $#l1 == $#l2,
"'$lang' and 'en' have the same count (" . @l1 . '/' . @l2 . ")" ); "'$lang' and 'en' have the same count (" . @l2 . '/' . @l1 . ")" );
my @unTr; my @unTr;
while (@l1) { while (@l1) {

View File

@ -109,12 +109,14 @@ lib/Lemonldap/NG/Portal/Plugins/ContextSwitching.pm
lib/Lemonldap/NG/Portal/Plugins/DecryptValue.pm lib/Lemonldap/NG/Portal/Plugins/DecryptValue.pm
lib/Lemonldap/NG/Portal/Plugins/FavApps.pm lib/Lemonldap/NG/Portal/Plugins/FavApps.pm
lib/Lemonldap/NG/Portal/Plugins/ForceAuthn.pm lib/Lemonldap/NG/Portal/Plugins/ForceAuthn.pm
lib/Lemonldap/NG/Portal/Plugins/GlobalLogout.pm
lib/Lemonldap/NG/Portal/Plugins/GrantSession.pm lib/Lemonldap/NG/Portal/Plugins/GrantSession.pm
lib/Lemonldap/NG/Portal/Plugins/History.pm lib/Lemonldap/NG/Portal/Plugins/History.pm
lib/Lemonldap/NG/Portal/Plugins/Impersonation.pm lib/Lemonldap/NG/Portal/Plugins/Impersonation.pm
lib/Lemonldap/NG/Portal/Plugins/MailPasswordReset.pm lib/Lemonldap/NG/Portal/Plugins/MailPasswordReset.pm
lib/Lemonldap/NG/Portal/Plugins/Notifications.pm lib/Lemonldap/NG/Portal/Plugins/Notifications.pm
lib/Lemonldap/NG/Portal/Plugins/PublicPages.pm lib/Lemonldap/NG/Portal/Plugins/PublicPages.pm
lib/Lemonldap/NG/Portal/Plugins/Refresh.pm
lib/Lemonldap/NG/Portal/Plugins/Register.pm lib/Lemonldap/NG/Portal/Plugins/Register.pm
lib/Lemonldap/NG/Portal/Plugins/RESTServer.pm lib/Lemonldap/NG/Portal/Plugins/RESTServer.pm
lib/Lemonldap/NG/Portal/Plugins/SingleSession.pm lib/Lemonldap/NG/Portal/Plugins/SingleSession.pm
@ -154,8 +156,10 @@ README
scripts/llngDeleteSession scripts/llngDeleteSession
site/coffee/2fregistration.coffee site/coffee/2fregistration.coffee
site/coffee/autoRenew.coffee site/coffee/autoRenew.coffee
site/coffee/captcha.coffee
site/coffee/confirm.coffee site/coffee/confirm.coffee
site/coffee/favapps.coffee site/coffee/favapps.coffee
site/coffee/globalLogout.coffee
site/coffee/idpchoice.coffee site/coffee/idpchoice.coffee
site/coffee/info.coffee site/coffee/info.coffee
site/coffee/kerberos.coffee site/coffee/kerberos.coffee
@ -285,12 +289,18 @@ site/htdocs/static/common/js/2fregistration.min.js.map
site/htdocs/static/common/js/autoRenew.js site/htdocs/static/common/js/autoRenew.js
site/htdocs/static/common/js/autoRenew.min.js site/htdocs/static/common/js/autoRenew.min.js
site/htdocs/static/common/js/autoRenew.min.js.map site/htdocs/static/common/js/autoRenew.min.js.map
site/htdocs/static/common/js/captcha.js
site/htdocs/static/common/js/captcha.min.js
site/htdocs/static/common/js/captcha.min.js.map
site/htdocs/static/common/js/confirm.js site/htdocs/static/common/js/confirm.js
site/htdocs/static/common/js/confirm.min.js site/htdocs/static/common/js/confirm.min.js
site/htdocs/static/common/js/confirm.min.js.map site/htdocs/static/common/js/confirm.min.js.map
site/htdocs/static/common/js/favapps.js site/htdocs/static/common/js/favapps.js
site/htdocs/static/common/js/favapps.min.js site/htdocs/static/common/js/favapps.min.js
site/htdocs/static/common/js/favapps.min.js.map site/htdocs/static/common/js/favapps.min.js.map
site/htdocs/static/common/js/globalLogout.js
site/htdocs/static/common/js/globalLogout.min.js
site/htdocs/static/common/js/globalLogout.min.js.map
site/htdocs/static/common/js/idpchoice.js site/htdocs/static/common/js/idpchoice.js
site/htdocs/static/common/js/idpchoice.min.js site/htdocs/static/common/js/idpchoice.min.js
site/htdocs/static/common/js/idpchoice.min.js.map site/htdocs/static/common/js/idpchoice.min.js.map
@ -363,10 +373,12 @@ site/htdocs/static/languages/it.json
site/htdocs/static/languages/nl.json site/htdocs/static/languages/nl.json
site/htdocs/static/languages/pt.json site/htdocs/static/languages/pt.json
site/htdocs/static/languages/ro.json site/htdocs/static/languages/ro.json
site/htdocs/static/languages/tr.json
site/htdocs/static/languages/vi.json site/htdocs/static/languages/vi.json
site/htdocs/static/languages/zh.json site/htdocs/static/languages/zh.json
site/templates/bootstrap/2fchoice.tpl site/templates/bootstrap/2fchoice.tpl
site/templates/bootstrap/2fregisters.tpl site/templates/bootstrap/2fregisters.tpl
site/templates/bootstrap/captcha.tpl
site/templates/bootstrap/casBack2Url.tpl site/templates/bootstrap/casBack2Url.tpl
site/templates/bootstrap/certificateReset.tpl site/templates/bootstrap/certificateReset.tpl
site/templates/bootstrap/checklogins.tpl site/templates/bootstrap/checklogins.tpl
@ -383,6 +395,7 @@ site/templates/bootstrap/decryptvalue.tpl
site/templates/bootstrap/error.tpl site/templates/bootstrap/error.tpl
site/templates/bootstrap/ext2fcheck.tpl site/templates/bootstrap/ext2fcheck.tpl
site/templates/bootstrap/footer.tpl site/templates/bootstrap/footer.tpl
site/templates/bootstrap/globallogout.tpl
site/templates/bootstrap/gpgform.tpl site/templates/bootstrap/gpgform.tpl
site/templates/bootstrap/header.tpl site/templates/bootstrap/header.tpl
site/templates/bootstrap/idpchoice.tpl site/templates/bootstrap/idpchoice.tpl
@ -435,6 +448,7 @@ site/templates/common/mail/fi.json
site/templates/common/mail/fr.json site/templates/common/mail/fr.json
site/templates/common/mail/it.json site/templates/common/mail/it.json
site/templates/common/mail/ms.json site/templates/common/mail/ms.json
site/templates/common/mail/tr.json
site/templates/common/mail/vi.json site/templates/common/mail/vi.json
site/templates/common/mail/zh_CN.json site/templates/common/mail/zh_CN.json
site/templates/common/mail_2fcode.tpl site/templates/common/mail_2fcode.tpl
@ -521,6 +535,8 @@ t/32-Auth-and-issuer-OIDC-implicit-no-token.t
t/32-Auth-and-issuer-OIDC-implicit.t t/32-Auth-and-issuer-OIDC-implicit.t
t/32-Auth-and-issuer-OIDC-sorted.t t/32-Auth-and-issuer-OIDC-sorted.t
t/32-CAS-10.t t/32-CAS-10.t
t/32-OIDC-Offline-Session.t
t/32-OIDC-Refresh-Token.t
t/32-OIDC-RP-rule.t t/32-OIDC-RP-rule.t
t/32-OIDC-Token-Introspection.t t/32-OIDC-Token-Introspection.t
t/32-OIDC-Token-Security.t t/32-OIDC-Token-Security.t
@ -575,6 +591,8 @@ t/43-MailPasswordReset-with-token.t
t/43-MailPasswordReset.t t/43-MailPasswordReset.t
t/44-CertificateResetByMail-LDAP.t t/44-CertificateResetByMail-LDAP.t
t/50-IssuerGet.t t/50-IssuerGet.t
t/57-GlobalLogout-without-Timer.t
t/57-GlobalLogout.t
t/58-DecryptValue-with-custom-function.t t/58-DecryptValue-with-custom-function.t
t/58-DecryptValue-with-internal-function.t t/58-DecryptValue-with-internal-function.t
t/59-Double-cookies-for-a-Single-session.t t/59-Double-cookies-for-a-Single-session.t
@ -587,6 +605,7 @@ t/61-ForceAuthn.t
t/61-GrantSession.t t/61-GrantSession.t
t/61-Session-ActivityTimeout.t t/61-Session-ActivityTimeout.t
t/61-Session-Timeout.t t/61-Session-Timeout.t
t/62-Refresh-plugin.t
t/62-SingleSession.t t/62-SingleSession.t
t/62-UpgradeSession.t t/62-UpgradeSession.t
t/63-History.t t/63-History.t
@ -667,4 +686,5 @@ t/test-psgi.pm
t/testslapd/confs-sessions.ldif t/testslapd/confs-sessions.ldif
t/testslapd/slapd.ldif t/testslapd/slapd.ldif
t/testslapd/users.ldif t/testslapd/users.ldif
t/Time-Fake.pm
t/vrfyOTP.pl t/vrfyOTP.pl

View File

@ -3,7 +3,7 @@ package Lemonldap::NG::Portal::2F::Ext2F;
use strict; use strict;
use Mouse; use Mouse;
use Lemonldap::NG::Portal::Main::Constants qw( use Lemonldap::NG::Portal::Main::Constants qw(
PE_BADCREDENTIALS PE_BADOTP
PE_ERROR PE_ERROR
PE_FORMEMPTY PE_FORMEMPTY
PE_OK PE_OK
@ -119,7 +119,7 @@ sub verify {
$self->userLogger->warn( 'Second factor failed for ' $self->userLogger->warn( 'Second factor failed for '
. $session->{ $self->conf->{whatToTrace} } ); . $session->{ $self->conf->{whatToTrace} } );
$self->logger->error("External verify command failed (code $c)"); $self->logger->error("External verify command failed (code $c)");
return PE_BADCREDENTIALS; return PE_BADOTP;
} }
return PE_OK; return PE_OK;
} }
@ -136,7 +136,7 @@ sub verify {
$self->userLogger->warn( 'Second factor failed for ' $self->userLogger->warn( 'Second factor failed for '
. $session->{ $self->conf->{whatToTrace} } ); . $session->{ $self->conf->{whatToTrace} } );
return PE_BADCREDENTIALS; return PE_BADOTP;
} }
# system() is used with an array to avoid shell injection # system() is used with an array to avoid shell injection

View File

@ -3,7 +3,7 @@ package Lemonldap::NG::Portal::2F::Mail2F;
use strict; use strict;
use Mouse; use Mouse;
use Lemonldap::NG::Portal::Main::Constants qw( use Lemonldap::NG::Portal::Main::Constants qw(
PE_BADCREDENTIALS PE_BADOTP
PE_ERROR PE_ERROR
PE_FORMEMPTY PE_FORMEMPTY
PE_OK PE_OK
@ -147,7 +147,7 @@ sub verify {
$self->userLogger->warn( 'Second factor failed for ' $self->userLogger->warn( 'Second factor failed for '
. $session->{ $self->conf->{whatToTrace} } ); . $session->{ $self->conf->{whatToTrace} } );
return PE_BADCREDENTIALS; return PE_BADOTP;
} }
1; 1;

View File

@ -3,7 +3,7 @@ package Lemonldap::NG::Portal::2F::REST;
use strict; use strict;
use Mouse; use Mouse;
use Lemonldap::NG::Portal::Main::Constants qw( use Lemonldap::NG::Portal::Main::Constants qw(
PE_BADCREDENTIALS PE_BADOTP
PE_ERROR PE_ERROR
PE_FORMEMPTY PE_FORMEMPTY
PE_OK PE_OK
@ -139,7 +139,7 @@ sub verify {
unless ( $res->{result} ) { unless ( $res->{result} ) {
$self->userLogger->warn( 'REST Second factor failed for ' $self->userLogger->warn( 'REST Second factor failed for '
. $session->{ $self->conf->{whatToTrace} } ); . $session->{ $self->conf->{whatToTrace} } );
return PE_BADCREDENTIALS; return PE_BADOTP;
} }
PE_OK; PE_OK;
} }

View File

@ -3,7 +3,7 @@ package Lemonldap::NG::Portal::2F::Radius;
use strict; use strict;
use Mouse; use Mouse;
use Lemonldap::NG::Portal::Main::Constants qw( use Lemonldap::NG::Portal::Main::Constants qw(
PE_BADCREDENTIALS PE_BADOTP
PE_ERROR PE_ERROR
PE_MALFORMEDUSER PE_MALFORMEDUSER
PE_OK PE_OK
@ -107,7 +107,7 @@ sub verify {
. $session->{ $self->conf->{whatToTrace} } ); . $session->{ $self->conf->{whatToTrace} } );
$self->logger->warn( $self->logger->warn(
"Radius server replied: " . $self->radius->get_error ); "Radius server replied: " . $self->radius->get_error );
return PE_BADCREDENTIALS; return PE_BADOTP;
} }
$self->logger->debug("Radius server accepted 2F credentials"); $self->logger->debug("Radius server accepted 2F credentials");
PE_OK; PE_OK;

View File

@ -8,7 +8,7 @@ use strict;
use Mouse; use Mouse;
use JSON qw(from_json to_json); use JSON qw(from_json to_json);
use Lemonldap::NG::Portal::Main::Constants qw( use Lemonldap::NG::Portal::Main::Constants qw(
PE_BADCREDENTIALS PE_BADOTP
PE_ERROR PE_ERROR
PE_FORMEMPTY PE_FORMEMPTY
PE_OK PE_OK
@ -99,7 +99,7 @@ sub verify {
unless ($secret) { unless ($secret) {
$self->logger->debug("No TOTP secret found"); $self->logger->debug("No TOTP secret found");
return PE_BADCREDENTIALS; return PE_BADOTP;
} }
my $r = $self->verifyCode( my $r = $self->verifyCode(
@ -117,7 +117,7 @@ sub verify {
$self->userLogger->notice( 'Invalid TOTP for ' $self->userLogger->notice( 'Invalid TOTP for '
. $session->{ $self->conf->{whatToTrace} } . $session->{ $self->conf->{whatToTrace} }
. ')' ); . ')' );
return PE_BADCREDENTIALS; return PE_BADOTP;
} }
} }

View File

@ -9,7 +9,7 @@ use Mouse;
use JSON qw(from_json to_json); use JSON qw(from_json to_json);
use Lemonldap::NG::Portal::Main::Constants qw( use Lemonldap::NG::Portal::Main::Constants qw(
PE_ERROR PE_ERROR
PE_BADCREDENTIALS PE_BADOTP
PE_FORMEMPTY PE_FORMEMPTY
PE_OK PE_OK
PE_SENDRESPONSE PE_SENDRESPONSE
@ -96,7 +96,7 @@ sub run {
$self->userLogger->warn( 'User ' $self->userLogger->warn( 'User '
. $req->{sessionInfo}->{ $self->conf->{whatToTrace} } . $req->{sessionInfo}->{ $self->conf->{whatToTrace} }
. ' has no Yubikey registered' ); . ' has no Yubikey registered' );
return PE_BADCREDENTIALS; return PE_BADOTP;
} }
$self->logger->debug("Found Yubikey : $yubikey"); $self->logger->debug("Found Yubikey : $yubikey");
@ -149,11 +149,11 @@ sub verify {
) )
{ {
$self->userLogger->warn('Yubikey not registered'); $self->userLogger->warn('Yubikey not registered');
return PE_BADCREDENTIALS; return PE_BADOTP;
} }
if ( $self->yubi->otp($code) ne 'OK' ) { if ( $self->yubi->otp($code) ne 'OK' ) {
$self->userLogger->warn('Yubikey verification failed'); $self->userLogger->warn('Yubikey verification failed');
return PE_BADCREDENTIALS; return PE_BADOTP;
} }
PE_OK; PE_OK;
} }

View File

@ -6,7 +6,7 @@ package Lemonldap::NG::Portal::Auth::AD;
use strict; use strict;
use Mouse; use Mouse;
use Lemonldap::NG::Portal::Main::Constants use Lemonldap::NG::Portal::Main::Constants
qw(PE_OK PE_PP_PASSWORD_EXPIRED PE_PP_CHANGE_AFTER_RESET); qw(PE_OK PE_PP_PASSWORD_EXPIRED PE_PP_CHANGE_AFTER_RESET PE_BADCREDENTIALS);
our $VERSION = '2.1.0'; our $VERSION = '2.1.0';
@ -66,7 +66,8 @@ sub authenticate {
my ( $self, $req ) = @_; my ( $self, $req ) = @_;
my $res = $self->SUPER::authenticate($req); my $res = $self->SUPER::authenticate($req);
my $pls = $self->ldap->getLdapValue( $req->data->{ldapentry}, 'pwdLastSet' ); my $pls =
$self->ldap->getLdapValue( $req->data->{ldapentry}, 'pwdLastSet' );
my $computed = $self->ldap->getLdapValue( $req->data->{ldapentry}, my $computed = $self->ldap->getLdapValue( $req->data->{ldapentry},
'msDS-User-Account-Control-Computed' ); 'msDS-User-Account-Control-Computed' );
my $_adUac = my $_adUac =
@ -75,6 +76,15 @@ sub authenticate {
unless ( $res == PE_OK ) { unless ( $res == PE_OK ) {
# Explicit bad credentials message
if ( $req->data->{ldapError}
and $req->data->{ldapError} =~ /LdapErr: .* data ([^,]+),.*/ )
{
if ( $1 eq '52e' ) {
return PE_BADCREDENTIALS;
}
}
# Check specific AD attributes # Check specific AD attributes
my $mask = 0xf00000; # mask to get the 8 at 6th position my $mask = 0xf00000; # mask to get the 8 at 6th position
my $expired_flag = my $expired_flag =

View File

@ -152,6 +152,17 @@ sub authForce {
return 0; return 0;
} }
#sub setSecurity {
# my $self = shift;
# my ($req) = @_;
# $self->getStack( $req, 'extractFormInfo' ) or return PE_ERROR;
# $req->userData->{_combinationTry} ||= 0;
# eval {
# $req->data->{combinationStack}->[ $req->userData->{_combinationTry} ]
# ->[0]->( 'setSecurity', @_ );
# };
#}
## UserDB steps ## UserDB steps
############### ###############
# Note that UserDB::Combination uses the same object. # Note that UserDB::Combination uses the same object.

View File

@ -6,6 +6,7 @@ use Mouse;
use Lemonldap::NG::Common::FormEncode; use Lemonldap::NG::Common::FormEncode;
use Lemonldap::NG::Portal::Main::Constants qw( use Lemonldap::NG::Portal::Main::Constants qw(
PE_BADURL PE_BADURL
PE_BADCREDENTIALS
PE_CONFIRM PE_CONFIRM
PE_ERROR PE_ERROR
PE_LOGOUT_OK PE_LOGOUT_OK
@ -547,11 +548,12 @@ sub run {
} }
my $scope_messages = { my $scope_messages = {
openid => 'yourIdentity', openid => 'yourIdentity',
profile => 'yourProfile', profile => 'yourProfile',
email => 'yourEmail', email => 'yourEmail',
address => 'yourAddress', address => 'yourAddress',
phone => 'yourPhone', phone => 'yourPhone',
offline_access => 'yourOffline',
}; };
my @list; my @list;
foreach my $requested_scope ( foreach my $requested_scope (
@ -609,6 +611,47 @@ sub run {
); );
} }
# WIP: Offline access
my $offline = 0;
if ( $oidc_request->{'scope'} =~ /\boffline_access\b/ ) {
$offline = 1;
# MUST ensure that the prompt parameter contains consent unless
# other conditions for processing the request permitting offline
# access to the requested resources are in place; unless one or
# both of these conditions are fulfilled, then it MUST ignore
# the offline_access request,
unless ( $bypassConsent
or ( $prompt and $prompt =~ /\bconsent\b/ ) )
{
$self->logger->warn( "Offline access ignored, "
. "prompt parameter must contain \"consent\"" );
$offline = 0;
}
# MUST ignore the offline_access request unless the Client is
# using a response_type value that would result in an
# Authorization Code being returned,
if ( $response_type !~ /\bcode\b/ ) {
$self->logger->warn( "Offline access incompatible "
. "with response type $response_type" );
$offline = 0;
}
# Ignore offline_access request if not authorized by the RP
unless ( $self->conf->{oidcRPMetaDataOptions}->{$rp}
->{oidcRPMetaDataOptionsAllowOffline} )
{
$self->logger->warn(
"Offline access not authorized for RP $rp");
$offline = 0;
}
# Strip offline_access from scopes from now on
$oidc_request->{'scope'} = join " ", grep !/^offline_access$/,
split /\s+/, $oidc_request->{'scope'};
}
# Authorization Code Flow # Authorization Code Flow
if ( $flow eq "authorizationcode" ) { if ( $flow eq "authorizationcode" ) {
@ -616,14 +659,15 @@ sub run {
my $codeSession = $self->newAuthorizationCode( my $codeSession = $self->newAuthorizationCode(
$rp, $rp,
{ {
code_challenge => $oidc_request->{'code_challenge'},
code_challenge_method =>
$oidc_request->{'code_challenge_method'},
nonce => $oidc_request->{'nonce'},
offline => $offline,
redirect_uri => $oidc_request->{'redirect_uri'}, redirect_uri => $oidc_request->{'redirect_uri'},
scope => $oidc_request->{'scope'}, scope => $oidc_request->{'scope'},
client_id => $client_id, client_id => $client_id,
user_session_id => $req->id, user_session_id => $req->id,
nonce => $oidc_request->{'nonce'},
code_challenge => $oidc_request->{'code_challenge'},
code_challenge_method =>
$oidc_request->{'code_challenge_method'},
} }
); );
@ -687,8 +731,10 @@ sub run {
} }
# ID token payload # ID token payload
my $id_token_exp = $self->conf->{oidcRPMetaDataOptions}->{$rp} my $id_token_exp =
->{oidcRPMetaDataOptionsIDTokenExpiration}; $self->conf->{oidcRPMetaDataOptions}->{$rp}
->{oidcRPMetaDataOptionsIDTokenExpiration}
|| $self->conf->{oidcServiceIDTokenExpiration};
$id_token_exp += time; $id_token_exp += time;
my $authenticationLevel = my $authenticationLevel =
@ -728,12 +774,15 @@ sub run {
$id_token_payload_hash->{'acr'} = $id_token_acr $id_token_payload_hash->{'acr'} = $id_token_acr
if $id_token_acr; if $id_token_acr;
if ( $response_type !~ /\btoken\b/ ) { if ( $response_type !~ /\btoken\b/
|| $self->force_id_claims($rp) )
{
# No access_token # No access_token
# Claims must be set in id_token # Claims must be set in id_token
my $claims = my $claims =
$self->buildUserInfoResponse( $oidc_request->{'scope'}, $self->buildUserInfoResponseFromId(
$oidc_request->{'scope'},
$rp, $req->id ); $rp, $req->id );
foreach ( keys %$claims ) { foreach ( keys %$claims ) {
@ -749,8 +798,10 @@ sub run {
$self->logger->debug("Generated id token: $id_token"); $self->logger->debug("Generated id token: $id_token");
# Send token response # Send token response
my $expires_in = $self->conf->{oidcRPMetaDataOptions}->{$rp} my $expires_in =
->{oidcRPMetaDataOptionsAccessTokenExpiration}; $self->conf->{oidcRPMetaDataOptions}->{$rp}
->{oidcRPMetaDataOptionsAccessTokenExpiration}
|| $self->conf->{oidcServiceAccessTokenExpiration};
# Build Response # Build Response
my $response_url = $self->buildImplicitAuthnResponse( my $response_url = $self->buildImplicitAuthnResponse(
@ -783,11 +834,12 @@ sub run {
my $codeSession = $self->newAuthorizationCode( my $codeSession = $self->newAuthorizationCode(
$rp, $rp,
{ {
nonce => $oidc_request->{'nonce'},
offline => $offline,
redirect_uri => $oidc_request->{'redirect_uri'}, redirect_uri => $oidc_request->{'redirect_uri'},
client_id => $client_id, client_id => $client_id,
scope => $oidc_request->{'scope'}, scope => $oidc_request->{'scope'},
user_session_id => $req->id, user_session_id => $req->id,
nonce => $oidc_request->{'nonce'},
} }
); );
@ -836,11 +888,13 @@ sub run {
# ID token payload # ID token payload
my $id_token_exp = my $id_token_exp =
$self->conf->{oidcRPMetaDataOptions}->{$rp} $self->conf->{oidcRPMetaDataOptions}->{$rp}
->{oidcRPMetaDataOptionsIDTokenExpiration}; ->{oidcRPMetaDataOptionsIDTokenExpiration}
|| $self->conf->{oidcServiceIDTokenExpiration};
$id_token_exp += time; $id_token_exp += time;
my $id_token_acr = my $id_token_acr =
"loa-" . $req->{sessionInfo}->{authenticationLevel}; "loa-"
. ( $req->{sessionInfo}->{authenticationLevel} || 0 );
my $user_id_attribute = my $user_id_attribute =
$self->conf->{oidcRPMetaDataOptions}->{$rp} $self->conf->{oidcRPMetaDataOptions}->{$rp}
@ -866,11 +920,13 @@ sub run {
$id_token_payload_hash->{'at_hash'} = $at_hash if $at_hash; $id_token_payload_hash->{'at_hash'} = $at_hash if $at_hash;
$id_token_payload_hash->{'c_hash'} = $c_hash if $c_hash; $id_token_payload_hash->{'c_hash'} = $c_hash if $c_hash;
if ( $response_type !~ /\btoken\b/ ) { if ( $response_type !~ /\btoken\b/
|| $self->force_id_claims($rp) )
{
# No access_token # No access_token
# Claims must be set in id_token # Claims must be set in id_token
my $claims = $self->buildUserInfoResponse( my $claims = $self->buildUserInfoResponseFromId(
$oidc_request->{'scope'}, $oidc_request->{'scope'},
$rp, $req->id ); $rp, $req->id );
@ -887,8 +943,10 @@ sub run {
$self->logger->debug("Generated id token: $id_token"); $self->logger->debug("Generated id token: $id_token");
} }
my $expires_in = $self->conf->{oidcRPMetaDataOptions}->{$rp} my $expires_in =
->{oidcRPMetaDataOptionsAccessTokenExpiration}; $self->conf->{oidcRPMetaDataOptions}->{$rp}
->{oidcRPMetaDataOptionsAccessTokenExpiration}
|| $self->conf->{oidcServiceAccessTokenExpiration};
# Build Response # Build Response
my $response_url = $self->buildHybridAuthnResponse( my $response_url = $self->buildHybridAuthnResponse(
@ -1013,152 +1071,466 @@ sub token {
my $client_id = $self->oidcRPList->{$rp}->{oidcRPMetaDataOptionsClientID}; my $client_id = $self->oidcRPList->{$rp}->{oidcRPMetaDataOptionsClientID};
# Get code session my $grant_type = $req->param('grant_type');
my $code = $req->param('code');
unless ($code) { # Autorization Code grant
$self->logger->error("No code found on token endpoint"); if ( $grant_type eq 'authorization_code' ) {
return $self->p->sendError( $req, 'invalid_request', 400 ); my $code = $req->param('code');
}
$self->logger->debug("OpenID Connect Code: $code"); unless ($code) {
$self->logger->error("No code found on token endpoint");
return $self->p->sendError( $req, 'invalid_request', 400 );
}
my $codeSession = $self->getAuthorizationCode($code); my $codeSession = $self->getAuthorizationCode($code);
unless ($codeSession) { unless ($codeSession) {
$self->logger->error("Unable to find OIDC session $code"); $self->logger->error("Unable to find OIDC session $code");
return $self->p->sendError( $req, 'invalid_request', 400 ); return $self->p->sendError( $req, 'invalid_request', 400 );
} }
# Check PKCE $codeSession->remove();
if ( $self->conf->{oidcRPMetaDataOptions}->{$rp}
->{oidcRPMetaDataOptionsRequirePKCE} ) # Check PKCE
{ if ( $self->conf->{oidcRPMetaDataOptions}->{$rp}
unless ( ->{oidcRPMetaDataOptionsRequirePKCE} )
$self->validatePKCEChallenge(
$req->param('code_verifier'),
$codeSession->data->{'code_challenge'},
$codeSession->data->{'code_challenge_method'}
)
)
{ {
unless (
$self->validatePKCEChallenge(
$req->param('code_verifier'),
$codeSession->data->{'code_challenge'},
$codeSession->data->{'code_challenge_method'}
)
)
{
return $self->p->sendError( $req, 'invalid_grant', 400 );
}
}
# Check we have the same client_id value
unless ( $client_id eq $codeSession->data->{client_id} ) {
$self->userLogger->error( "Provided client_id does not match "
. $codeSession->data->{client_id} );
return $self->p->sendError( $req, 'invalid_grant', 400 ); return $self->p->sendError( $req, 'invalid_grant', 400 );
} }
}
# Check we have the same client_id value # Check we have the same redirect_uri value
unless ( $client_id eq $codeSession->data->{client_id} ) { unless (
$self->userLogger->error( "Provided client_id does not match " $req->param("redirect_uri") eq $codeSession->data->{redirect_uri} )
. $codeSession->data->{client_id} );
return $self->p->sendError( $req, 'invalid_grant', 400 );
}
# Check we have the same redirect_uri value
unless ( $req->param("redirect_uri") eq $codeSession->data->{redirect_uri} )
{
$self->userLogger->error( "Provided redirect_uri does not match "
. $codeSession->data->{redirect_uri} );
return $self->p->sendError( $req, 'invalid_request', 400 );
}
# Get user identifier
my $apacheSession =
$self->p->getApacheSession( $codeSession->data->{user_session_id},
noInfo => 1 );
unless ($apacheSession) {
$self->userLogger->error(
"Unable to find user session linked to OIDC session $code");
$codeSession->remove();
return $self->p->sendError( $req, 'invalid_request', 400 );
}
my $user_id_attribute =
$self->conf->{oidcRPMetaDataOptions}->{$rp}
->{oidcRPMetaDataOptionsUserIDAttr}
|| $self->conf->{whatToTrace};
my $user_id = $apacheSession->data->{$user_id_attribute};
$self->logger->debug("Found corresponding user: $user_id");
# Generate access_token
my $accessTokenSession = $self->newAccessToken(
$rp,
{ {
scope => $codeSession->data->{scope}, $self->userLogger->error( "Provided redirect_uri does not match "
rp => $rp, . $codeSession->data->{redirect_uri} );
user_session_id => $apacheSession->id, return $self->p->sendError( $req, 'invalid_grant', 400 );
} }
);
unless ($accessTokenSession) { # Get user identifier
my $apacheSession =
$self->p->getApacheSession( $codeSession->data->{user_session_id},
noInfo => 1 );
unless ($apacheSession) {
$self->userLogger->error("Unable to find user session");
return $self->p->sendError( $req, 'invalid_grant', 400 );
}
my $user_id_attribute =
$self->conf->{oidcRPMetaDataOptions}->{$rp}
->{oidcRPMetaDataOptionsUserIDAttr}
|| $self->conf->{whatToTrace};
my $user_id = $apacheSession->data->{$user_id_attribute};
$self->logger->debug("Found corresponding user: $user_id");
# Generate access_token
my $accessTokenSession = $self->newAccessToken(
$rp,
{
scope => $codeSession->data->{scope},
rp => $rp,
user_session_id => $apacheSession->id,
}
);
unless ($accessTokenSession) {
$self->userLogger->error(
"Unable to create OIDC session for access_token");
#FIXME: should be an error 500
return $self->p->sendError( $req, 'invalid_request', 400 );
}
my $access_token = $accessTokenSession->id;
$self->logger->debug("Generated access token: $access_token");
# Generate refresh_token
my $refresh_token = undef;
# For offline access, the refresh token isn't tied to the session ID
if ( $codeSession->{data}->{offline} ) {
# We need to remove _sessionType, _sessionid and _utime from the
# session data before storing session data in the refresh token
my %userInfo;
for my $userKey ( grep !/^(_session|_utime$)/,
keys %{ $apacheSession->data } )
{
$userInfo{$userKey} = $apacheSession->data->{$userKey};
}
my $refreshTokenSession = $self->newRefreshToken(
$rp,
{
%userInfo,
redirect_uri => $codeSession->data->{redirect_uri},
scope => $codeSession->data->{scope},
client_id => $client_id,
_session_uid => $apacheSession->data->{_user},
auth_time => $apacheSession->data->{_lastAuthnUTime},
},
1,
);
unless ($refreshTokenSession) {
$self->userLogger->error(
"Unable to create OIDC session for refresh_token");
return $self->p->sendError( $req, 'invalid_request', 400 );
}
$refresh_token = $refreshTokenSession->id;
$self->logger->debug("Generated refresh token: $refresh_token");
}
# For online access, if configured
elsif ( $self->conf->{oidcRPMetaDataOptions}->{$rp}
->{oidcRPMetaDataOptionsRefreshToken} )
{
my $refreshTokenSession = $self->newRefreshToken(
$rp,
{
redirect_uri => $codeSession->data->{redirect_uri},
scope => $codeSession->data->{scope},
client_id => $client_id,
user_session_id => $codeSession->data->{user_session_id},
},
0,
);
unless ($refreshTokenSession) {
$self->userLogger->error(
"Unable to create OIDC session for refresh_token");
return $self->p->sendError( $req, 'invalid_request', 400 );
}
$refresh_token = $refreshTokenSession->id;
$self->logger->debug("Generated refresh token: $refresh_token");
}
# Compute hash to store in at_hash
my $alg = $self->conf->{oidcRPMetaDataOptions}->{$rp}
->{oidcRPMetaDataOptionsIDTokenSignAlg};
my ($hash_level) = ( $alg =~ /(?:\w{2})(\d{3})/ );
my $at_hash = $self->createHash( $access_token, $hash_level )
if $hash_level;
# ID token payload
my $id_token_exp =
$self->conf->{oidcRPMetaDataOptions}->{$rp}
->{oidcRPMetaDataOptionsIDTokenExpiration}
|| $self->conf->{oidcServiceIDTokenExpiration};
$id_token_exp += time;
my $id_token_acr = "loa-" . $apacheSession->data->{authenticationLevel};
my $id_token_payload_hash = {
iss => $self->conf->{oidcServiceMetaDataIssuer}, # Issuer Identifier
sub => $user_id, # Subject Identifier
aud => [$client_id], # Audience
exp => $id_token_exp, # expiration
iat => time, # Issued time
auth_time => $apacheSession->data->{_lastAuthnUTime}
, # Authentication time
acr => $id_token_acr, # Authentication Context Class Reference
azp => $client_id, # Authorized party
# TODO amr
};
my $nonce = $codeSession->data->{nonce};
$id_token_payload_hash->{nonce} = $nonce if defined $nonce;
$id_token_payload_hash->{'at_hash'} = $at_hash if $at_hash;
if ( $self->force_id_claims($rp) ) {
my $claims =
$self->buildUserInfoResponseFromId( $codeSession->data->{'scope'},
$rp, $codeSession->data->{user_session_id} );
foreach ( keys %$claims ) {
$id_token_payload_hash->{$_} = $claims->{$_}
unless ( $_ eq "sub" );
}
}
# Create ID Token
my $id_token = $self->createIDToken( $id_token_payload_hash, $rp );
$self->logger->debug("Generated id token: $id_token");
# Send token response
my $expires_in =
$self->conf->{oidcRPMetaDataOptions}->{$rp}
->{oidcRPMetaDataOptionsAccessTokenExpiration}
|| $self->conf->{oidcServiceAccessTokenExpiration};
my $token_response = {
access_token => $access_token,
token_type => 'Bearer',
expires_in => $expires_in,
id_token => $id_token,
( $refresh_token ? ( refresh_token => $refresh_token ) : () ),
};
my $cRP = $apacheSession->data->{_oidcConnectedRP} || '';
unless ( $cRP =~ /\b$rp\b/ ) {
$self->p->updateSession( $req, { _oidcConnectedRP => "$rp,$cRP" },
$apacheSession->id );
}
$self->logger->debug("Send token response");
return $self->p->sendJSONresponse( $req, $token_response );
}
# Refresh token
elsif ( $grant_type eq 'refresh_token' ) {
my $refresh_token = $req->param('refresh_token');
unless ($refresh_token) {
$self->logger->error("Missing refresh_token parameter");
return $self->p->sendError( $req, 'invalid_request', 400 );
}
$self->logger->debug("OpenID Refresh Token: $refresh_token");
my $refreshSession = $self->getRefreshToken($refresh_token);
unless ($refreshSession) {
$self->logger->error("Unable to find OIDC session $refresh_token");
return $self->p->sendError( $req, 'invalid_request', 400 );
}
# Check we have the same client_id value
unless ( $client_id eq $refreshSession->data->{client_id} ) {
$self->userLogger->error( "Provided client_id does not match "
. $refreshSession->data->{client_id} );
return $self->p->sendError( $req, 'invalid_grant', 400 );
}
my $access_token;
my $user_id;
my $auth_time;
my $session;
# If this refresh token is tied to a SSO session
if ( $refreshSession->data->{user_session_id} ) {
my $user_session_id = $refreshSession->data->{user_session_id};
$session = $self->p->getApacheSession($user_session_id);
unless ($session) {
$self->logger->error("Unable to find user session");
return $self->returnBearerError( 'invalid_request',
'Invalid request', 401 );
}
my $user_id_attribute =
$self->conf->{oidcRPMetaDataOptions}->{$rp}
->{oidcRPMetaDataOptionsUserIDAttr}
|| $self->conf->{whatToTrace};
$user_id = $session->data->{$user_id_attribute};
$auth_time = $session->data->{_lastAuthnUTime};
# Generate access_token
my $accessTokenSession = $self->newAccessToken(
$rp,
{
scope => $refreshSession->data->{scope},
rp => $rp,
user_session_id => $user_session_id,
}
);
unless ($accessTokenSession) {
$self->userLogger->error(
"Unable to create OIDC session for access_token");
return $self->p->sendError( $req,
'Unable to create Access Token', 500 );
}
$access_token = $accessTokenSession->id;
$self->logger->debug("Generated access token: $access_token");
}
# Else, we are in an offline session
else {
# Lookup attributes and macros for user
$req->user( $refreshSession->data->{_session_uid} );
$req->steps( [
'getUser', @{ $self->p->betweenAuthAndData },
'setSessionInfo', 'setMacros',
'setGroups', 'setLocalGroups',
]
);
$req->{error} = $self->p->process($req);
if ( $req->error > 0 ) {
# PE_BADCREDENTIAL is returned by UserDB modules when the user was
# explicitely not found. And not in case of temporary failures
if ( $req->error == PE_BADCREDENTIALS ) {
$self->logger->error( "User: "
. $req->user
. " no longer exists, removing offline session" );
$refreshSession->remove;
}
else {
$self->logger->error(
"Could not resolve user: " . $req->user );
}
return $self->p->sendError( $req, 'invalid_grant', 400 );
}
# Cleanup sessionInfo
delete $req->sessionInfo->{_utime};
delete $req->sessionInfo->{_startTime};
# Update refresh session
$self->updateRefreshToken( $refreshSession->id, $req->sessionInfo );
$session = $refreshSession;
for ( keys %{ $req->sessionInfo } ) {
$refreshSession->data->{$_} = $req->sessionInfo->{$_};
}
my $user_id_attribute =
$self->conf->{oidcRPMetaDataOptions}->{$rp}
->{oidcRPMetaDataOptionsUserIDAttr}
|| $self->conf->{whatToTrace};
$user_id = $req->sessionInfo->{$user_id_attribute};
$self->logger->debug("Found corresponding user: $user_id");
$auth_time = $refreshSession->data->{auth_time};
# Generate access_token
my $accessTokenSession = $self->newAccessToken(
$rp,
{
scope => $refreshSession->data->{scope},
rp => $rp,
offline_session_id => $refreshSession->id,
}
);
unless ($accessTokenSession) {
$self->userLogger->error(
"Unable to create OIDC session for access_token");
return $self->p->sendError( $req,
'Unable to create Access Token', 500 );
}
$access_token = $accessTokenSession->id;
$self->logger->debug("Generated access token: $access_token");
}
# Compute hash to store in at_hash
my $alg = $self->conf->{oidcRPMetaDataOptions}->{$rp}
->{oidcRPMetaDataOptionsIDTokenSignAlg};
my ($hash_level) = ( $alg =~ /(?:\w{2})(\d{3})/ );
my $at_hash = $self->createHash( $access_token, $hash_level )
if $hash_level;
# ID token payload
my $id_token_exp =
$self->conf->{oidcRPMetaDataOptions}->{$rp}
->{oidcRPMetaDataOptionsIDTokenExpiration}
|| $self->conf->{oidcServiceIDTokenExpiration};
$id_token_exp += time;
# Authentication level using refresh tokens should probably stay at 0
my $id_token_acr = "loa-0";
my $id_token_payload_hash = {
iss => $self->conf->{oidcServiceMetaDataIssuer}, # Issuer Identifier
sub => $user_id, # Subject Identifier
aud => [$client_id], # Audience
exp => $id_token_exp, # expiration
iat => time, # Issued time
# TODO: is this the right value when using refresh tokens??
auth_time => $auth_time, # Authentication time
acr => $id_token_acr, # Authentication Context Class Reference
azp => $client_id, # Authorized party
# TODO amr
};
my $nonce = $refreshSession->data->{nonce};
$id_token_payload_hash->{nonce} = $nonce if defined $nonce;
$id_token_payload_hash->{'at_hash'} = $at_hash if $at_hash;
# If we forced sending claims in ID token
if ( $self->force_id_claims($rp) ) {
my $claims =
$self->buildUserInfoResponse( $refreshSession->data->{scope},
$rp, $session );
foreach ( keys %$claims ) {
$id_token_payload_hash->{$_} = $claims->{$_}
unless ( $_ eq "sub" );
}
}
# Create ID Token
my $id_token = $self->createIDToken( $id_token_payload_hash, $rp );
$self->logger->debug("Generated id token: $id_token");
# Send token response
my $expires_in =
$self->conf->{oidcRPMetaDataOptions}->{$rp}
->{oidcRPMetaDataOptionsAccessTokenExpiration}
|| $self->conf->{oidcServiceAccessTokenExpiration};
my $token_response = {
access_token => $access_token,
token_type => 'Bearer',
expires_in => $expires_in,
id_token => $id_token,
};
# TODO
#my $cRP = $apacheSession->data->{_oidcConnectedRP} || '';
#unless ( $cRP =~ /\b$rp\b/ ) {
# $self->p->updateSession( $req, { _oidcConnectedRP => "$rp,$cRP" },
# $apacheSession->id );
#}
$self->logger->debug("Send token response");
return $self->p->sendJSONresponse( $req, $token_response );
}
# Unknown or unspecified grant type
else {
$self->userLogger->error( $self->userLogger->error(
"Unable to create OIDC session for access_token"); $grant_type
$codeSession->remove(); ? "Missing grant_type parameter"
return $self->p->sendError( $req, 'invalid_request', 400 ); : "Unknown grant type: $grant_type"
);
return $self->p->sendError( $req, 'unsupported_grant_type', 400 );
} }
my $access_token = $accessTokenSession->id;
$self->logger->debug("Generated access token: $access_token");
# Compute hash to store in at_hash
my $alg = $self->conf->{oidcRPMetaDataOptions}->{$rp}
->{oidcRPMetaDataOptionsIDTokenSignAlg};
my ($hash_level) = ( $alg =~ /(?:\w{2})(\d{3})/ );
my $at_hash = $self->createHash( $access_token, $hash_level )
if $hash_level;
# ID token payload
my $id_token_exp = $self->conf->{oidcRPMetaDataOptions}->{$rp}
->{oidcRPMetaDataOptionsIDTokenExpiration};
$id_token_exp += time;
my $id_token_acr = "loa-" . $apacheSession->data->{authenticationLevel};
my $id_token_payload_hash = {
iss => $self->iss,
sub => $user_id, # Subject Identifier
aud => [$client_id], # Audience
exp => $id_token_exp, # expiration
iat => time, # Issued time
auth_time => $apacheSession->data->{_lastAuthnUTime}
, # Authentication time
acr => $id_token_acr, # Authentication Context Class Reference
azp => $client_id, # Authorized party
# TODO amr
};
my $nonce = $codeSession->data->{nonce};
$id_token_payload_hash->{nonce} = $nonce if defined $nonce;
$id_token_payload_hash->{'at_hash'} = $at_hash if $at_hash;
# Create ID Token
my $id_token = $self->createIDToken( $id_token_payload_hash, $rp );
$self->logger->debug("Generated id token: $id_token");
# Send token response
my $expires_in = $self->conf->{oidcRPMetaDataOptions}->{$rp}
->{oidcRPMetaDataOptionsAccessTokenExpiration};
my $token_response = {
access_token => $access_token,
token_type => 'Bearer',
expires_in => $expires_in,
id_token => $id_token,
};
my $cRP = $apacheSession->data->{_oidcConnectedRP} || '';
unless ( $cRP =~ /\b$rp\b/ ) {
$self->p->updateSession( $req, { _oidcConnectedRP => "$rp,$cRP" },
$apacheSession->id );
}
$self->logger->debug("Send token response");
$codeSession->remove();
return $self->p->sendJSONresponse( $req, $token_response );
} }
# Handle userinfo endpoint # Handle userinfo endpoint
@ -1190,8 +1562,39 @@ sub userInfo {
my $rp = $accessTokenSession->data->{rp}; my $rp = $accessTokenSession->data->{rp};
my $user_session_id = $accessTokenSession->data->{user_session_id}; my $user_session_id = $accessTokenSession->data->{user_session_id};
my $session;
# If using a refreshed access token
if ($user_session_id) {
# Get user identifier
$session = $self->p->getApacheSession($user_session_id);
unless ($session) {
$self->logger->error("Unable to find user session");
return $self->returnBearerError( 'invalid_request',
'Invalid request', 401 );
}
}
else {
my $offline_session_id =
$accessTokenSession->data->{offline_session_id};
unless ($offline_session_id) {
return $self->returnBearerError( 'invalid_request',
'Invalid request', 401 );
}
$session = $self->getRefreshToken($offline_session_id);
unless ($session) {
$self->logger->error("Unable to find refresh session");
return $self->returnBearerError( 'invalid_request',
'Invalid request', 401 );
}
}
my $userinfo_response = my $userinfo_response =
$self->buildUserInfoResponse( $scope, $rp, $user_session_id ); $self->buildUserInfoResponse( $scope, $rp, $session );
unless ($userinfo_response) { unless ($userinfo_response) {
return $self->returnBearerError( 'invalid_request', 'Invalid request', return $self->returnBearerError( 'invalid_request', 'Invalid request',
401 ); 401 );

View File

@ -1717,7 +1717,8 @@ sub sloServer {
$req->data->{samlSLOCalled} = 1; $req->data->{samlSLOCalled} = 1;
# Launch normal logout and ignore errors # Launch normal logout and ignore errors
$self->p->do( $req, [ @{ $self->p->beforeLogout }, 'deleteSession' ] ); $req->steps( [ @{ $self->p->beforeLogout }, 'deleteSession' ] );
$self->p->process($req);
# Signature # Signature
my $signSLOMessage = $self->conf->{samlSPMetaDataOptions}->{$spConfKey} my $signSLOMessage = $self->conf->{samlSPMetaDataOptions}->{$spConfKey}

View File

@ -114,7 +114,14 @@ sub init {
sub getUser { sub getUser {
my ( $self, $req, %args ) = @_; my ( $self, $req, %args ) = @_;
return PE_LDAPCONNECTFAILED unless $self->ldap and $self->bind(); $self->validateLdap;
unless ( $self->ldap ) {
return PE_LDAPCONNECTFAILED;
}
$self->bind();
my $mesg = $self->ldap->search( my $mesg = $self->ldap->search(
base => $self->conf->{ldapBase}, base => $self->conf->{ldapBase},
scope => 'sub', scope => 'sub',

View File

@ -249,6 +249,9 @@ sub userBind {
if ( $mesg->code == 0 ) { if ( $mesg->code == 0 ) {
return PE_OK; return PE_OK;
} }
else {
$req->data->{ldapError} = $mesg->error;
}
} }
$self->{portal}->userLogger->warn("Bad password for $req->{user}"); $self->{portal}->userLogger->warn("Bad password for $req->{user}");
return PE_BADCREDENTIALS; return PE_BADCREDENTIALS;

View File

@ -3,6 +3,7 @@ package Lemonldap::NG::Portal::Lib::Notifications::JSON;
use strict; use strict;
use Mouse; use Mouse;
use JSON qw(from_json); use JSON qw(from_json);
use POSIX qw(strftime);
our $VERSION = '2.1.0'; our $VERSION = '2.1.0';
@ -34,18 +35,22 @@ sub checkForNotifications {
return 0 unless ($notifs); return 0 unless ($notifs);
# Transform notifications # Transform notifications
my $i = 0; #Files count my $i = 0; # Files count
my @res; my @res;
my $now = strftime "%Y-%m-%d", localtime;
foreach my $file ( values %$notifs ) { foreach my $file ( values %$notifs ) {
my $json = from_json( $file, { allow_nonref => 1 } ); my $json = eval { from_json( $file, { allow_nonref => 1 } ) };
my $j = 0; #Notifications count $self->userLogger->warn(
"Bad JSON file: a notification for $uid was not done ($@)")
if ($@);
my $j = 0; # Notifications count
$json = [$json] unless ( ref $json eq 'ARRAY' ); $json = [$json] unless ( ref $json eq 'ARRAY' );
LOOP: foreach my $notif ( @{$json} ) { LOOP: foreach my $notif ( @{$json} ) {
# Get the reference # Get the reference
my $reference = $notif->{reference}; my $reference = $notif->{reference};
$self->logger->debug("Get reference: $reference");
$self->logger->debug("Get reference $reference");
# Check it in session # Check it in session
if ( exists $req->{sessionInfo}->{"notification_$reference"} ) { if ( exists $req->{sessionInfo}->{"notification_$reference"} ) {
@ -55,6 +60,35 @@ sub checkForNotifications {
"Notification $reference was already accepted"); "Notification $reference was already accepted");
next LOOP; next LOOP;
} }
# Check date
my $date = $notif->{date};
$self->logger->debug("Get date: $date");
unless ( $date and $date =~ /\b\d{4}-\d{2}-\d{2}\b/ ) {
$self->logger->error('Malformed date');
next LOOP;
}
unless ( $date le $now ) {
$self->logger->debug('Notification date not reached');
next LOOP;
}
# Check condition if any
if ( my $condition = $notif->{condition} ) {
$self->logger->debug("Get condition $condition");
$condition = $self->p->HANDLER->substitute($condition);
unless ( $condition = $self->p->HANDLER->buildSub($condition) )
{
$self->logger->error( 'Notification condition error: '
. $self->p->HANDLER->tsv->{jail}->error );
next LOOP;
}
unless ( $condition->( $req, $req->sessionInfo ) ) {
$self->logger->debug(
'Notification condition not authorized');
next LOOP;
}
}
push @res, $notif; push @res, $notif;
$j++; $j++;
} }
@ -70,6 +104,7 @@ sub checkForNotifications {
# Stop here if nothing to display # Stop here if nothing to display
return 0 unless $i; return 0 unless $i;
$self->userLogger->info("$i pending notification(s) found for $uid");
# Returns HTML fragment # Returns HTML fragment
return $form; return $form;
@ -114,8 +149,9 @@ sub getNotifBack {
$self->p->importHandlerData($req); $self->p->importHandlerData($req);
my $uid = $req->sessionInfo->{ $self->notifObject->notifField }; my $uid = $req->sessionInfo->{ $self->notifObject->notifField };
my ( $notifs, $forUser ); # ALL notifications are returned here => Need to check active ones only
eval { ( $notifs, $forUser ) = $self->notifObject->getNotifications($uid) }; my ( $notifs, $forUser ) =
eval { $self->notifObject->getNotifications($uid) };
return $self->p->sendError( $req, $@, 500 ) if ($@); return $self->p->sendError( $req, $@, 500 ) if ($@);
if ($notifs) { if ($notifs) {
@ -134,6 +170,7 @@ sub getNotifBack {
} }
my $result = 1; my $result = 1;
my $now = strftime "%Y-%m-%d", localtime;
foreach my $fileName ( keys %$notifs ) { foreach my $fileName ( keys %$notifs ) {
my $file = $notifs->{$fileName}; my $file = $notifs->{$fileName};
my $fileResult = 1; my $fileResult = 1;
@ -141,14 +178,48 @@ sub getNotifBack {
$json = [$json] unless ( ref $json eq 'ARRAY' ); $json = [$json] unless ( ref $json eq 'ARRAY' );
# Get pending notifications and verify that they have been accepted # Get pending notifications and verify that they have been accepted
foreach my $notif (@$json) { LOOP: foreach my $notif (@$json) {
my $reference = $notif->{reference}; my $reference = $notif->{reference};
# Check date
my $date = $notif->{date};
$self->logger->debug("Get date: $date");
unless ( $date and $date =~ /\b\d{4}-\d{2}-\d{2}\b/ ) {
$self->logger->error('Malformed date');
next LOOP;
}
unless ( $date le $now ) {
$self->logger->debug('Notification date not reached');
$fileResult = 0; # Do not delete notification
next LOOP;
}
# Check condition if any
if ( my $condition = $notif->{condition} ) {
$self->logger->debug("Get condition $condition");
$condition = $self->p->HANDLER->substitute($condition);
unless ( $condition =
$self->p->HANDLER->buildSub($condition) )
{
$self->logger->error( 'Notification condition error: '
. $self->p->HANDLER->tsv->{jail}->error );
next LOOP;
}
unless ( $condition->( $req, $req->sessionInfo ) ) {
$self->logger->debug(
'Notification condition not authorized');
$fileResult = 0; # Do not delete notification
next LOOP;
}
}
# Check if this pending notification has been seen # Check if this pending notification has been seen
if ( my $refId = $refs->{$reference} ) { if ( my $refId = $refs->{$reference} ) {
# Verity that checkboxes have been checked # Verity that checkboxes have been checked
if ( $notif->{check} ) { if ( $notif->{check} ) {
$notif->{check} = [ $notif->{check} ]
unless ( ref( $notif->{check} ) eq 'ARRAY' );
if ( my $toCheckCount = @{ $notif->{check} } ) { if ( my $toCheckCount = @{ $notif->{check} } ) {
unless ($checks->{$refId} unless ($checks->{$refId}
and $toCheckCount == @{ $checks->{$refId} } ) and $toCheckCount == @{ $checks->{$refId} } )
@ -157,7 +228,7 @@ sub getNotifBack {
"$uid has not accepted notification $reference" "$uid has not accepted notification $reference"
); );
$result = $fileResult = 0; $result = $fileResult = 0;
next; next LOOP;
} }
} }
} }
@ -165,10 +236,10 @@ sub getNotifBack {
else { else {
# Current pending notification has not been found in # Current pending notification has not been found in
# request # request
$result = $fileResult = 0;
$self->logger->debug( $self->logger->debug(
'Current pending notification has not been found'); 'Current pending notification has not been found');
next; $result = $fileResult = 0;
next LOOP;
} }
# Register acceptation # Register acceptation
@ -217,6 +288,8 @@ sub toForm {
@notifs = map { @notifs = map {
$i++; $i++;
if ( $_->{check} ) { if ( $_->{check} ) {
$_->{check} = [ $_->{check} ]
unless ( ref( $_->{check} ) eq 'ARRAY' );
my $j = 0; my $j = 0;
$_->{check} = $_->{check} =
[ map { $j++; { id => '1x' . $i . 'x' . $j, value => $_ } } [ map { $j++; { id => '1x' . $i . 'x' . $j, value => $_ } }
@ -238,8 +311,10 @@ sub notificationServer {
my ( $res, $err ); my ( $res, $err );
if ( $req->method =~ /^POST$/i ) { if ( $req->method =~ /^POST$/i ) {
$self->p->logger->debug("POST request"); $self->p->logger->debug("POST request");
( $res, $err ) = ( $res, $err ) = eval {
eval { $self->notifObject->newNotification( $req->content ) }; $self->notifObject->newNotification( $req->content,
$self->conf->{notificationDefaultCond} );
};
return $self->p->sendError( $req, $@, 500 ) if ($@); return $self->p->sendError( $req, $@, 500 ) if ($@);
} }
elsif ( $req->method =~ /^GET$/i ) { elsif ( $req->method =~ /^GET$/i ) {
@ -252,10 +327,8 @@ sub notificationServer {
$res = []; $res = [];
foreach my $notif ( keys %$notifs ) { foreach my $notif ( keys %$notifs ) {
$self->p->logger->debug("Found notification $notif"); $self->p->logger->debug("Found notification $notif");
my $json; my $json =
eval { eval { from_json( $notifs->{$notif}, { allow_nonref => 1 } ) };
$json = from_json( $notifs->{$notif}, { allow_nonref => 1 } );
};
return $self->p->sendError( $req, "Unable to decode JSON file: $@", return $self->p->sendError( $req, "Unable to decode JSON file: $@",
400 ) 400 )
if ($@); if ($@);

View File

@ -4,6 +4,7 @@ use strict;
use Mouse; use Mouse;
use XML::LibXML; use XML::LibXML;
use XML::LibXSLT; use XML::LibXSLT;
use POSIX qw(strftime);
our $VERSION = '2.1.0'; our $VERSION = '2.1.0';
@ -44,6 +45,10 @@ has stylesheet => (
# Underlying notifications storage object (File, DBI, LDAP,...) # Underlying notifications storage object (File, DBI, LDAP,...)
has notifObject => ( is => 'rw' ); has notifObject => ( is => 'rw' );
# Notification server accessors
has imported => ( is => 'rw', default => 0 );
has server => ( is => 'rw' );
# INITIALIZATION # INITIALIZATION
sub init { sub init {
@ -61,10 +66,12 @@ sub checkForNotifications {
return 0 unless ($notifs); return 0 unless ($notifs);
# Transform notifications # Transform notifications
my $i = 0; #Files count my $i = 0; # Files count
my $now = strftime "%Y-%m-%d", localtime;
foreach my $file ( values %$notifs ) { foreach my $file ( values %$notifs ) {
my $xml = $self->parser->parse_string($file); my $xml = $self->parser->parse_string($file);
my $j = 0; #Notifications count my $j = 0; # Notifications count
LOOP: foreach my $notif ( LOOP: foreach my $notif (
eval { eval {
$xml->documentElement->getElementsByTagName('notification'); $xml->documentElement->getElementsByTagName('notification');
@ -74,7 +81,6 @@ sub checkForNotifications {
# Get the reference # Get the reference
my $reference = $notif->getAttribute('reference'); my $reference = $notif->getAttribute('reference');
$self->logger->debug("Get reference $reference"); $self->logger->debug("Get reference $reference");
# Check it in session # Check it in session
@ -89,21 +95,35 @@ sub checkForNotifications {
next LOOP; next LOOP;
} }
# Check date
my $date = $notif->getAttribute('date');
$self->logger->debug("Get date: $date");
unless ( $date and $date =~ /\b\d{4}-\d{2}-\d{2}\b/ ) {
$self->logger->error('Malformed date');
$notif->unbindNode();
next LOOP;
}
unless ( $date le $now ) {
$self->logger->debug('Notification date not reached');
# Remove it from XML
$notif->unbindNode();
next LOOP;
}
# Check condition if any # Check condition if any
my $condition = $notif->getAttribute('condition'); if ( my $condition = $notif->getAttribute('condition') ) {
if ($condition) {
$self->logger->debug("Get condition $condition"); $self->logger->debug("Get condition $condition");
$condition = $self->p->HANDLER->substitute($condition); $condition = $self->p->HANDLER->substitute($condition);
unless ( $condition = $self->p->HANDLER->buildSub($condition) ) unless ( $condition = $self->p->HANDLER->buildSub($condition) )
{ {
$self->logger->error( 'Notification condition error: ' $self->logger->error( 'Notification condition error: '
. $self->p->HANDLER->tsv->{jail}->error ); . $self->p->HANDLER->tsv->{jail}->error );
# Remove it from XML
$notif->unbindNode(); $notif->unbindNode();
next LOOP; next LOOP;
} }
unless ( $condition->( $req, $req->sessionInfo ) ) { unless ( $condition->( $req, $req->sessionInfo ) ) {
$self->logger->debug( $self->logger->debug(
'Notification condition not authorized'); 'Notification condition not authorized');
@ -113,7 +133,6 @@ sub checkForNotifications {
next LOOP; next LOOP;
} }
} }
$j++; $j++;
} }
@ -133,6 +152,7 @@ sub checkForNotifications {
# Stop here if nothing to display # Stop here if nothing to display
return 0 unless $i; return 0 unless $i;
$self->userLogger->info("$i pending notification(s) found for $uid");
# Returns HTML fragment # Returns HTML fragment
return $form; return $form;
@ -177,7 +197,11 @@ sub getNotifBack {
$self->p->importHandlerData($req); $self->p->importHandlerData($req);
my $uid = $req->sessionInfo->{ $self->notifObject->notifField }; my $uid = $req->sessionInfo->{ $self->notifObject->notifField };
my ( $notifs, $forUser ) = $self->notifObject->getNotifications($uid); # ALL notifications are returned here => Need to check active ones only
my ( $notifs, $forUser ) =
eval { $self->notifObject->getNotifications($uid) };
return $self->p->sendError( $req, $@, 500 ) if ($@);
if ($notifs) { if ($notifs) {
# Get accepted notifications # Get accepted notifications
@ -194,17 +218,51 @@ sub getNotifBack {
} }
my $result = 1; my $result = 1;
my $now = strftime "%Y-%m-%d", localtime;
foreach my $fileName ( keys %$notifs ) { foreach my $fileName ( keys %$notifs ) {
my $file = $notifs->{$fileName}; my $file = $notifs->{$fileName};
my $fileResult = 1; my $fileResult = 1;
my $xml = $self->parser->parse_string($file); my $xml = $self->parser->parse_string($file);
# Get pending notifications and verify that they have been accepted # Get pending notifications and verify that they have been accepted
LOOP:
foreach my $notif ( foreach my $notif (
$xml->documentElement->getElementsByTagName('notification') ) $xml->documentElement->getElementsByTagName('notification') )
{ {
my $reference = $notif->getAttribute('reference'); my $reference = $notif->getAttribute('reference');
# Check date
my $date = $notif->getAttribute('date');
$self->logger->debug("Get date: $date");
unless ( $date and $date =~ /\b\d{4}-\d{2}-\d{2}\b/ ) {
$self->logger->error('Malformed date');
next LOOP;
}
unless ( $date le $now ) {
$self->logger->debug('Notification date not reached');
$fileResult = 0; # Do not delete notification
next LOOP;
}
# Check condition if any
if ( my $condition = $notif->getAttribute('condition') ) {
$self->logger->debug("Get condition $condition");
$condition = $self->p->HANDLER->substitute($condition);
unless ( $condition =
$self->p->HANDLER->buildSub($condition) )
{
$self->logger->error( 'Notification condition error: '
. $self->p->HANDLER->tsv->{jail}->error );
next LOOP;
}
unless ( $condition->( $req, $req->sessionInfo ) ) {
$self->logger->debug(
'Notification condition not authorized');
$fileResult = 0; # Do not delete notification
next LOOP;
}
}
# Check if this pending notification has been seen # Check if this pending notification has been seen
if ( my $refId = $refs->{$reference} ) { if ( my $refId = $refs->{$reference} ) {
@ -260,7 +318,7 @@ sub getNotifBack {
# launch 'controlUrl' to restore "urldc" using do() # launch 'controlUrl' to restore "urldc" using do()
$self->logger->debug('All pending notifications have been accepted'); $self->logger->debug('All pending notifications have been accepted');
$self->p->rebuildCookies($req); $self->p->rebuildCookies($req);
return $self->p->do( $req, ['controlUrl', @{ $self->p->endAuth }] ); return $self->p->do( $req, [ 'controlUrl', @{ $self->p->endAuth } ] );
} }
else { else {
# No notifications checked here, this entry point must not be called. # No notifications checked here, this entry point must not be called.
@ -271,9 +329,6 @@ sub getNotifBack {
} }
} }
has imported => ( is => 'rw', default => 0 );
has server => ( is => 'rw' );
sub notificationServer { sub notificationServer {
my ( $self, $req ) = @_; my ( $self, $req ) = @_;
unless ( $self->imported ) { unless ( $self->imported ) {
@ -299,7 +354,8 @@ sub notificationServer {
sub newNotification { sub newNotification {
my ( $self, $req, $xml ) = @_; my ( $self, $req, $xml ) = @_;
return $self->notifObject->newNotification($xml); return $self->notifObject->newNotification( $xml,
$self->conf->{notificationDefaultCond} );
} }
1; 1;

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