commit
947dd9a438
|
@ -8,6 +8,18 @@
|
|||
paths:
|
||||
- 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:
|
||||
- build
|
||||
- sign
|
||||
|
@ -46,6 +58,10 @@ build_bionic:
|
|||
image: buildpkg/ubuntu:bionic
|
||||
<<: *job_build
|
||||
|
||||
build_centos_7:
|
||||
image: buildpkg/centos:7
|
||||
extends: .build_job_centos
|
||||
|
||||
sign:
|
||||
image: buildpkg/debian:stretch
|
||||
stage: sign
|
||||
|
@ -60,6 +76,7 @@ sign:
|
|||
- build_buster
|
||||
- build_disco
|
||||
- build_bionic
|
||||
- build_centos_7
|
||||
artifacts:
|
||||
expire_in: 1 day
|
||||
paths:
|
||||
|
|
5
Makefile
5
Makefile
|
@ -129,8 +129,8 @@ E2E_TESTS='portal/*.js'
|
|||
|
||||
# LDAP backend test
|
||||
LLNGTESTLDAP_SLAPD_BIN=/usr/sbin/slapd
|
||||
LLNGTESTLDAP_SLAPADD_BIN=/usr/sbin/slapdadd
|
||||
LLNGTESTLDAP_SCHEMA_DIR=/etc/slapd/schema
|
||||
LLNGTESTLDAP_SLAPADD_BIN=/usr/sbin/slapadd
|
||||
LLNGTESTLDAP_SCHEMA_DIR=/etc/ldap/schema
|
||||
|
||||
# Other
|
||||
SRCCOMMONDIR=lemonldap-ng-common
|
||||
|
@ -623,6 +623,7 @@ install_bin: install_conf_dir
|
|||
${SRCPORTALDIR}/site/cron/purgeCentralCache \
|
||||
${SRCPORTALDIR}/scripts/llngDeleteSession \
|
||||
${SRCCOMMONDIR}/scripts/convertConfig \
|
||||
${SRCCOMMONDIR}/scripts/convertSessions \
|
||||
${SRCCOMMONDIR}/scripts/lmMigrateConfFiles2ini \
|
||||
${SRCCOMMONDIR}/scripts/rotateOidcKeys \
|
||||
${SRCMANAGERDIR}/scripts/lmConfigEditor \
|
||||
|
|
|
@ -18,6 +18,16 @@
|
|||
auth_request_set $headervalue9 $upstream_http_headervalue9;
|
||||
auth_request_set $headername10 $upstream_http_headername10;
|
||||
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;
|
||||
access_by_lua '
|
||||
i = 1
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
/etc/lemonldap-ng/lemonldap-ng.ini
|
||||
/etc/lemonldap-ng/for_etc_hosts
|
||||
/usr/share/man/man1/convertConfig.1p
|
||||
/usr/share/man/man1/convertSessions.1p
|
||||
/usr/share/man/man1/lemonldap-ng-cli.1p
|
||||
/usr/share/man/man3/Lemonldap::NG::Common*
|
||||
/usr/share/perl5/auto/Lemonldap/NG/Common
|
||||
/usr/share/perl5/Lemonldap/NG/Common*
|
||||
/usr/share/lemonldap-ng/ressources
|
||||
/usr/share/lemonldap-ng/bin/convertConfig
|
||||
/usr/share/lemonldap-ng/bin/convertSessions
|
||||
/usr/share/lemonldap-ng/bin/importMetadata
|
||||
/usr/share/lemonldap-ng/bin/lmMigrateConfFiles2ini
|
||||
/usr/share/lemonldap-ng/bin/rotateOidcKeys
|
||||
|
|
|
@ -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='checkLogins']")).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();
|
||||
|
||||
// Login attempt
|
||||
|
|
|
@ -129,7 +129,7 @@
|
|||
.\" ========================================================================
|
||||
.\"
|
||||
.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
|
||||
.\" way too many mistakes in technical documents.
|
||||
.if n .ad l
|
||||
|
|
|
@ -71,6 +71,7 @@ META.json
|
|||
META.yml
|
||||
README
|
||||
scripts/convertConfig
|
||||
scripts/convertSessions
|
||||
scripts/importMetadata
|
||||
scripts/lemonldap-ng-cli
|
||||
scripts/lmMigrateConfFiles2ini
|
||||
|
|
|
@ -90,6 +90,7 @@ WriteMakefile(
|
|||
},
|
||||
MAN1PODS => {
|
||||
'scripts/convertConfig' => 'blib/man1/convertConfig.1p',
|
||||
'scripts/convertSessions' => 'blib/man1/convertSessions.1p',
|
||||
'scripts/lemonldap-ng-cli' => 'blib/man1/lemonldap-ng-cli.1p',
|
||||
},
|
||||
);
|
||||
|
|
|
@ -85,11 +85,12 @@ logLevel = warn
|
|||
|
||||
; CONFIGURATION CHECK
|
||||
;
|
||||
; By default, LLNG verify configuration at server start. If you use "reload"
|
||||
; mechanism local cache will be updated. configuration is checked locally every
|
||||
; LLNG verify configuration at server start. If you use "reload" mechanism,
|
||||
; local cache will be updated. Configuration is checked locally every
|
||||
; 10 minutes by each LLNG component. You can change this value using
|
||||
; `checkTime` (time in seconds):
|
||||
;checkTime = 600
|
||||
; `checkTime` (time in seconds).
|
||||
; To increase performances, you should comment this parameter and rely on cache.
|
||||
checkTime = 1
|
||||
|
||||
[configuration]
|
||||
|
||||
|
|
|
@ -219,18 +219,24 @@ sub _dbiGKFAS {
|
|||
$sth->execute;
|
||||
my %res;
|
||||
while ( my @row = $sth->fetchrow_array ) {
|
||||
if ( ref($data) eq 'CODE' ) {
|
||||
my $tmp =
|
||||
&$data( $args->{unserialize}->( $row[1], $next ), $row[0] );
|
||||
$res{ $row[0] } = $tmp if ( defined($tmp) );
|
||||
}
|
||||
elsif ($data) {
|
||||
$data = [$data] unless ( ref($data) );
|
||||
my $tmp = $args->{unserialize}->( $row[1], $next );
|
||||
$res{ $row[0] }->{$_} = $tmp->{$_} foreach (@$data);
|
||||
}
|
||||
else {
|
||||
$res{ $row[0] } = $args->{unserialize}->( $row[1], $next );
|
||||
eval {
|
||||
if ( ref($data) eq 'CODE' ) {
|
||||
my $tmp =
|
||||
&$data( $args->{unserialize}->( $row[1], $next ), $row[0] );
|
||||
$res{ $row[0] } = $tmp if ( defined($tmp) );
|
||||
}
|
||||
elsif ($data) {
|
||||
$data = [$data] unless ( ref($data) );
|
||||
my $tmp = $args->{unserialize}->( $row[1], $next );
|
||||
$res{ $row[0] }->{$_} = $tmp->{$_} foreach (@$data);
|
||||
}
|
||||
else {
|
||||
$res{ $row[0] } = $args->{unserialize}->( $row[1], $next );
|
||||
}
|
||||
};
|
||||
if ($@) {
|
||||
print STDERR "Error in session $row[0]\n";
|
||||
delete $res{ $row[0] };
|
||||
}
|
||||
}
|
||||
return \%res;
|
||||
|
@ -249,26 +255,32 @@ sub _FileGKFAS {
|
|||
my %res;
|
||||
for my $f (@t) {
|
||||
open F, '<', "$args->{Directory}/$f";
|
||||
my $row = join '', <F>;
|
||||
if ( ref($data) eq 'CODE' ) {
|
||||
eval { $res{$f} = &$data( $args->{unserialize}->($row), $f ); };
|
||||
if ($@) {
|
||||
$res{$f} = &$data( undef, $f );
|
||||
eval {
|
||||
my $row = join '', <F>;
|
||||
if ( ref($data) eq 'CODE' ) {
|
||||
eval { $res{$f} = &$data( $args->{unserialize}->($row), $f ); };
|
||||
if ($@) {
|
||||
$res{$f} = &$data( undef, $f );
|
||||
}
|
||||
}
|
||||
}
|
||||
elsif ($data) {
|
||||
$data = [$data] unless ( ref($data) );
|
||||
my $tmp;
|
||||
eval { $tmp = $args->{unserialize}->($row); };
|
||||
if ($@) {
|
||||
$res{$f}->{$_} = undef foreach (@$data);
|
||||
elsif ($data) {
|
||||
$data = [$data] unless ( ref($data) );
|
||||
my $tmp;
|
||||
eval { $tmp = $args->{unserialize}->($row); };
|
||||
if ($@) {
|
||||
$res{$f}->{$_} = undef foreach (@$data);
|
||||
}
|
||||
else {
|
||||
$res{$f}->{$_} = $tmp->{$_} foreach (@$data);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$res{$f}->{$_} = $tmp->{$_} foreach (@$data);
|
||||
eval { $res{$f} = $args->{unserialize}->($row); };
|
||||
}
|
||||
}
|
||||
else {
|
||||
eval { $res{$f} = $args->{unserialize}->($row); };
|
||||
};
|
||||
if ($@) {
|
||||
print STDERR "Error in session $f\n";
|
||||
delete $res{$f};
|
||||
}
|
||||
}
|
||||
return \%res;
|
||||
|
@ -318,17 +330,23 @@ sub _DBFileGKFAS {
|
|||
|
||||
my %res;
|
||||
foreach my $k ( keys %{ $class->{dbm} } ) {
|
||||
if ( ref($data) eq 'CODE' ) {
|
||||
$res{$k} =
|
||||
&$data( $args->{unserialize}->( $class->{dbm}->{$k} ), $k );
|
||||
}
|
||||
elsif ($data) {
|
||||
$data = [$data] unless ( ref($data) );
|
||||
my $tmp = $args->{unserialize}->( $class->{dbm}->{$k} );
|
||||
$res{$k}->{$_} = $tmp->{$_} foreach (@$data);
|
||||
}
|
||||
else {
|
||||
$res{$k} = $args->{unserialize}->( $class->{dbm}->{$k} );
|
||||
eval {
|
||||
if ( ref($data) eq 'CODE' ) {
|
||||
$res{$k} =
|
||||
&$data( $args->{unserialize}->( $class->{dbm}->{$k} ), $k );
|
||||
}
|
||||
elsif ($data) {
|
||||
$data = [$data] unless ( ref($data) );
|
||||
my $tmp = $args->{unserialize}->( $class->{dbm}->{$k} );
|
||||
$res{$k}->{$_} = $tmp->{$_} foreach (@$data);
|
||||
}
|
||||
else {
|
||||
$res{$k} = $args->{unserialize}->( $class->{dbm}->{$k} );
|
||||
}
|
||||
};
|
||||
if ($@) {
|
||||
print STDERR "Error in session $k\n";
|
||||
delete $res{$k};
|
||||
}
|
||||
}
|
||||
return \%res;
|
||||
|
|
|
@ -50,10 +50,12 @@ sub compactConf {
|
|||
|
||||
# Disabled for now:
|
||||
|
||||
# Remove unused issuerDB parameters
|
||||
# Remove unused issuerDB parameters except options
|
||||
foreach my $k ( keys %$issuerParameters ) {
|
||||
unless ( $conf->{ $k . "Activation" } ) {
|
||||
delete $conf->{$_} foreach ( @{ $issuerParameters->{$k} } );
|
||||
foreach ( @{ $issuerParameters->{$k} } ) {
|
||||
delete $conf->{$_} unless ( $_ =~ /^issuers/ );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,8 +23,8 @@ use constant HANDLERSECTION => "handler";
|
|||
use constant MANAGERSECTION => "manager";
|
||||
use constant SESSIONSEXPLORERSECTION => "sessionsExplorer";
|
||||
use constant APPLYSECTION => "apply";
|
||||
our $hashParameters = qr/^(?:(?:l(?:o(?:ca(?:lSessionStorageOption|tionRule)|goutService)|dapExportedVar|wp(?:Ssl)?Opt)|(?:(?:d(?:emo|bi)|facebook|webID)ExportedVa|exported(?:Heade|Va)|issuerDBGetParamete)r|re(?:moteGlobalStorageOption|st2f(?:Verify|Init)Arg|loadUrl)|g(?:r(?:antSessionRule|oup)|lobalStorageOption)|n(?:otificationStorageOption|ginxCustomHandler)|macro)s|o(?:idc(?: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 $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 $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(?: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' );
|
||||
|
||||
|
|
|
@ -78,6 +78,8 @@ sub defaultValues {
|
|||
'failedLoginNumber' => 5,
|
||||
'favAppsMaxNumber' => 3,
|
||||
'formTimeout' => 120,
|
||||
'globalLogoutRule' => 0,
|
||||
'globalLogoutTimer' => 1,
|
||||
'globalStorage' => 'Apache::Session::File',
|
||||
'globalStorageOptions' => {
|
||||
'Directory' => '/var/lib/lemonldap-ng/sessions/',
|
||||
|
@ -167,21 +169,25 @@ sub defaultValues {
|
|||
'multiValuesSeparator' => '; ',
|
||||
'mySessionAuthorizedRWKeys' =>
|
||||
[ '_appsListOrder', '_oidcConnectedRP', '_oidcConsents' ],
|
||||
'notificationServerPOST' => 1,
|
||||
'notificationDefaultCond' => '',
|
||||
'notificationServerPOST' => 1,
|
||||
'notificationServerSentAttributes' =>
|
||||
'uid reference date title subtitle text check',
|
||||
'notificationStorage' => 'File',
|
||||
'notificationStorageOptions' => {
|
||||
'dirName' => '/var/lib/lemonldap-ng/notifications'
|
||||
},
|
||||
'notificationWildcard' => 'allusers',
|
||||
'notifyDeleted' => 1,
|
||||
'nullAuthnLevel' => 0,
|
||||
'oidcAuthnLevel' => 1,
|
||||
'oidcRPCallbackGetParam' => 'openidconnectcallback',
|
||||
'oidcRPStateTimeout' => 600,
|
||||
'oidcServiceAllowAuthorizationCodeFlow' => 1,
|
||||
'oidcServiceMetaDataAuthnContext' => {
|
||||
'notificationWildcard' => 'allusers',
|
||||
'notifyDeleted' => 1,
|
||||
'nullAuthnLevel' => 0,
|
||||
'oidcAuthnLevel' => 1,
|
||||
'oidcRPCallbackGetParam' => 'openidconnectcallback',
|
||||
'oidcRPStateTimeout' => 600,
|
||||
'oidcServiceAccessTokenExpiration' => 3600,
|
||||
'oidcServiceAllowAuthorizationCodeFlow' => 1,
|
||||
'oidcServiceAuthorizationCodeExpiration' => 60,
|
||||
'oidcServiceIDTokenExpiration' => 3600,
|
||||
'oidcServiceMetaDataAuthnContext' => {
|
||||
'loa-1' => 1,
|
||||
'loa-2' => 2,
|
||||
'loa-3' => 3,
|
||||
|
@ -198,6 +204,7 @@ sub defaultValues {
|
|||
'oidcServiceMetaDataRegistrationURI' => 'register',
|
||||
'oidcServiceMetaDataTokenURI' => 'token',
|
||||
'oidcServiceMetaDataUserInfoURI' => 'userinfo',
|
||||
'oidcServiceOfflineSessionExpiration' => 2592000,
|
||||
'openIdAuthnLevel' => 1,
|
||||
'openIdExportedVars' => {},
|
||||
'openIdIDPList' => '0;',
|
||||
|
|
|
@ -22,12 +22,12 @@ our $specialNodeHash = {
|
|||
};
|
||||
|
||||
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 $casAppMetaDataNodeKeys = 'casAppMetaData(?:Options(?:UserAttribut|Servic|Rul)e|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 $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 $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)';
|
||||
|
@ -68,6 +68,6 @@ our $issuerParameters = {
|
|||
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 $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;
|
||||
|
|
|
@ -21,6 +21,7 @@ $hash = \&Digest::SHA::sha256;
|
|||
use constant HMAC_LENGTH => 32;
|
||||
use constant IV_LENGTH => 16;
|
||||
|
||||
# Build initialization vector subroutine
|
||||
BEGIN {
|
||||
eval { require Crypt::URandom; Crypt::URandom::urandom(IV_LENGTH) };
|
||||
if ($@) {
|
||||
|
|
|
@ -7,8 +7,9 @@ use JSON qw(from_json to_json);
|
|||
our $VERSION = '2.1.0';
|
||||
|
||||
sub newNotification {
|
||||
my ( $self, $jsonString ) = @_;
|
||||
my ( $self, $jsonString, $defaultCond ) = @_;
|
||||
my $json;
|
||||
$defaultCond ||= '';
|
||||
eval { $json = from_json( $jsonString, { allow_nonref => 1 } ) };
|
||||
if ( my $err = $@ ) {
|
||||
eval { $self->logger->error("Unable to decode JSON file: $err") };
|
||||
|
@ -35,8 +36,16 @@ sub newNotification {
|
|||
}
|
||||
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;
|
||||
foreach (@notifs) {
|
||||
|
|
|
@ -18,7 +18,8 @@ has parser => (
|
|||
# @param $xml XML string containing notification
|
||||
# @return number of notifications done
|
||||
sub newNotification {
|
||||
my ( $self, $xml ) = @_;
|
||||
my ( $self, $xml, $defaultCond ) = @_;
|
||||
$defaultCond ||= '';
|
||||
eval { $xml = $self->parser->parse_string($xml) };
|
||||
if ( my $err = $@ ) {
|
||||
eval { $self->logger->error("Unable to read XML file : $err") };
|
||||
|
@ -53,7 +54,12 @@ sub newNotification {
|
|||
if ( $tmp = $notif->getAttribute($_) ) {
|
||||
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 );
|
||||
|
|
|
@ -18,7 +18,7 @@ our $VERSION = '2.1.0';
|
|||
# Not that only functions, not methods, can be written here
|
||||
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)
|
||||
|
@ -72,6 +72,36 @@ sub checkLogonHours {
|
|||
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
|
||||
# Get current 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;
|
||||
}
|
||||
|
||||
sub varIsInUri {
|
||||
my ( $uri, $wanteduri, $attribute, $restricted ) = @_;
|
||||
return $restricted
|
||||
? $uri =~ /$wanteduri$attribute$/o
|
||||
: $uri =~ /$wanteduri$attribute/o;
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -24,6 +24,13 @@ sub setTypes {
|
|||
( $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 {
|
||||
|
|
|
@ -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>
|
||||
|
|
@ -52,6 +52,7 @@ t/61-Lemonldap-NG-Handler-PSGI-Server.t
|
|||
t/62-Lemonldap-NG-Handler-Nginx.t
|
||||
t/63-Lemonldap-NG-Handler-PSGI-Try.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/66-Lemonldap-NG-Handler-PSGI-wildcard.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/test-psgi-lib.pm
|
||||
t/test.pm
|
||||
t/Time-Fake.pm
|
||||
|
|
|
@ -37,6 +37,12 @@ my %builder = (
|
|||
},
|
||||
psgi => 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};
|
||||
return $_apps{$script}->(@_) if ( $_apps{$script} );
|
||||
$_apps{$script} = do $script;
|
||||
|
@ -44,7 +50,7 @@ my %builder = (
|
|||
die "Unable to load $_[0]->{SCRIPT_FILENAME}";
|
||||
}
|
||||
return $_apps{$script}->(@_);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
@ -4,6 +4,60 @@ use strict;
|
|||
|
||||
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 {
|
||||
my ( $class, $req ) = @_;
|
||||
|
||||
|
@ -21,10 +75,16 @@ sub fetchId {
|
|||
|
||||
# Get access token session
|
||||
my $infos = $class->getOIDCInfos($access_token);
|
||||
# If this token is tied to a regular session ID
|
||||
if ( my $_session_id = $infos->{user_session_id} ) {
|
||||
$class->logger->debug( 'Get user session id ' . $_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);
|
||||
}
|
||||
|
@ -50,7 +110,8 @@ sub getOIDCInfos {
|
|||
unless ( $oidcSession->error ) {
|
||||
$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 {
|
||||
$class->logger->info("OIDC Session $id can't be retrieved");
|
||||
|
|
|
@ -56,10 +56,10 @@ sub fetchId {
|
|||
return 0;
|
||||
}
|
||||
|
||||
# Send service headers if exist
|
||||
# Send service headers to protected application if exist
|
||||
if (%serviceHeaders) {
|
||||
$class->logger->debug("Append service header(s)...");
|
||||
$class->set_header_out( $req, %serviceHeaders );
|
||||
$class->logger->info("Append service header(s)...");
|
||||
$class->set_header_in( $req, %serviceHeaders );
|
||||
}
|
||||
|
||||
return $_session_id;
|
||||
|
|
|
@ -99,9 +99,10 @@ sub portalConsts {
|
|||
'93' => 'PE_IMPERSONATION_SERVICE_NOT_ALLOWED',
|
||||
'94' => 'PE_ISSUERMISSINGREQATTR',
|
||||
'95' => 'PE_DECRYPTVALUE_SERVICE_NOT_ALLOWED',
|
||||
'96' => 'PE_RESETCERTIFICATE_INVALID',
|
||||
'96' => 'PE_BADOTP',
|
||||
'97' => 'PE_RESETCERTIFICATE_FORMEMPTY',
|
||||
'98' => 'PE_RESETCERTIFICATE_FIRSTACCESS'
|
||||
'98' => 'PE_RESETCERTIFICATE_FIRSTACCESS',
|
||||
'99' => 'PE_RESETCERTIFICATE_INVALID'
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -21,10 +21,11 @@ BEGIN {
|
|||
}
|
||||
}
|
||||
|
||||
has customFunctions => ( is => 'rw', isa => 'Maybe[Str]' );
|
||||
has useSafeJail => ( is => 'rw', isa => 'Maybe[Int]' );
|
||||
has jail => ( is => 'rw' );
|
||||
has error => ( is => 'rw' );
|
||||
has customFunctions => ( is => 'rw', isa => 'Maybe[Str]' );
|
||||
has useSafeJail => ( is => 'rw', isa => 'Maybe[Int]' );
|
||||
has multiValuesSeparator => ( is => 'rw', isa => 'Maybe[Str]' );
|
||||
has jail => ( is => 'rw' );
|
||||
has error => ( is => 'rw' );
|
||||
|
||||
our $VERSION = '2.1.0';
|
||||
our @builtCustomFunctions;
|
||||
|
@ -88,7 +89,18 @@ sub build_jail {
|
|||
$self->jail->share_from( 'Lemonldap::NG::Common::Safelib',
|
||||
$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( 'Lemonldap::NG::Handler::Main', ['$_v'] );
|
||||
|
|
|
@ -13,6 +13,7 @@ use Scalar::Util qw(weaken);
|
|||
|
||||
use constant UNPROTECT => 1;
|
||||
use constant SKIP => 2;
|
||||
use constant MAYSKIP => 3;
|
||||
|
||||
our @_onReload;
|
||||
|
||||
|
@ -179,8 +180,9 @@ sub jailInit {
|
|||
my ( $class, $conf ) = @_;
|
||||
|
||||
$class->tsv->{jail} = Lemonldap::NG::Handler::Main::Jail->new( {
|
||||
useSafeJail => $conf->{useSafeJail},
|
||||
customFunctions => $conf->{customFunctions},
|
||||
useSafeJail => $conf->{useSafeJail},
|
||||
customFunctions => $conf->{customFunctions},
|
||||
multiValuesSeparator => $conf->{multiValuesSeparator},
|
||||
}
|
||||
);
|
||||
$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
|
||||
$cond = $class->substitute($cond);
|
||||
my $sub;
|
||||
|
@ -554,7 +559,7 @@ sub conditionSub {
|
|||
}
|
||||
|
||||
# Return sub and protected flag
|
||||
return ( $sub, 0 );
|
||||
return ( $sub, $mayskip );
|
||||
}
|
||||
|
||||
## @method arrayref aliasInit
|
||||
|
@ -589,6 +594,7 @@ sub substitute {
|
|||
$expr =~ s/\$ENV\{/\$r->{env}->\{/g;
|
||||
$expr =~ s/\$env->\{/\$r->{env}->\{/g;
|
||||
$expr =~ s/\$_rulematch\[/\$m->\[/g;
|
||||
$expr =~ s/\bskip\b/q\{999_SKIP\}/g;
|
||||
|
||||
return $expr;
|
||||
}
|
||||
|
|
|
@ -192,6 +192,11 @@ sub run {
|
|||
$class->cleanHeaders($req);
|
||||
return $class->OK;
|
||||
}
|
||||
elsif ( $protection == $class->MAYSKIP
|
||||
and $class->grant( $req, $session, $uri, $cond ) eq '999_SKIP' )
|
||||
{
|
||||
return $class->OK;
|
||||
}
|
||||
|
||||
else {
|
||||
|
||||
|
@ -441,7 +446,7 @@ sub fetchId {
|
|||
my $value =
|
||||
$lookForHttpCookie
|
||||
? ( $t =~ /${cn}http=([^,; ]+)/o ? $1 : 0 )
|
||||
: ( $t =~ /$cn=([^,; ]+)/o ? $1 : 0 );
|
||||
: ( $t =~ /$cn=([^,; ]+)/o ? $1 : 0 );
|
||||
|
||||
if ( $value && $lookForHttpCookie && $class->tsv->{securedCookie} == 3 ) {
|
||||
$value = $class->tsv->{cipher}->decryptHex( $value, "http" );
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
# change 'tests => 1' to 'tests => last_test_to_print';
|
||||
|
||||
use strict;
|
||||
use Test::More tests => 9;
|
||||
use Test::More tests => 13;
|
||||
require 't/test.pm';
|
||||
BEGIN { use_ok('Lemonldap::NG::Handler::Main::Jail') }
|
||||
|
||||
|
@ -18,8 +18,9 @@ my $res;
|
|||
|
||||
ok(
|
||||
my $jail = Lemonldap::NG::Handler::Main::Jail->new(
|
||||
'jail' => undef,
|
||||
'useSafeJail' => 1,
|
||||
'jail' => undef,
|
||||
'useSafeJail' => 1,
|
||||
'multiValuesSeparator' => '; ',
|
||||
),
|
||||
'new jail object'
|
||||
);
|
||||
|
@ -38,6 +39,18 @@ ok(
|
|||
ok( $res = &$code, "Function works" );
|
||||
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')) }";
|
||||
$code = $jail->jail_reval($sub);
|
||||
ok(
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
# change 'tests => 1' to 'tests => last_test_to_print';
|
||||
|
||||
use Test::More tests => 5;
|
||||
use Test::More tests => 9;
|
||||
require 't/test.pm';
|
||||
BEGIN { use_ok('Lemonldap::NG::Handler::Main::Jail') }
|
||||
|
||||
|
@ -16,9 +16,10 @@ BEGIN { use_ok('Lemonldap::NG::Handler::Main::Jail') }
|
|||
|
||||
ok(
|
||||
my $jail = Lemonldap::NG::Handler::Main::Jail->new(
|
||||
'jail' => undef,
|
||||
'useSafeJail' => 0,
|
||||
'customFunctions' => undef
|
||||
'jail' => undef,
|
||||
'useSafeJail' => 0,
|
||||
'customFunctions' => undef,
|
||||
'multiValuesSeparator' => '; ',
|
||||
),
|
||||
'new fake jail object'
|
||||
);
|
||||
|
@ -41,3 +42,15 @@ my $sub3 = "sub { return(checkDate('20000000000000','21000000000000')) }";
|
|||
my $checkDate = $jail->jail_reval($sub3);
|
||||
ok( &$checkDate == "1",
|
||||
'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' );
|
||||
|
|
|
@ -8,6 +8,7 @@ require 't/test-psgi-lib.pm';
|
|||
init('Lemonldap::NG::Handler::PSGI');
|
||||
|
||||
my $res;
|
||||
my $SKIPUSER = 0;
|
||||
|
||||
# Unauthentified query
|
||||
# --------------------
|
||||
|
@ -38,7 +39,45 @@ count(2);
|
|||
ok( $res = $client->_get( '/user_dwho/', undef, undef, "lemonldap=$sessionId" ),
|
||||
'Regexp query' );
|
||||
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);
|
||||
|
||||
# Denied query
|
||||
|
@ -124,14 +163,25 @@ ok(
|
|||
);
|
||||
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() );
|
||||
|
||||
clean();
|
||||
|
||||
sub Lemonldap::NG::Handler::PSGI::handler {
|
||||
my ( $self, $req ) = @_;
|
||||
ok( $req->env->{HTTP_AUTH_USER} eq 'dwho', 'Header is given to app' )
|
||||
or explain( $req->env->{HTTP_AUTH_USER}, 'dwho' );
|
||||
count(1);
|
||||
unless ($SKIPUSER) {
|
||||
ok( $req->env->{HTTP_AUTH_USER} eq 'dwho', 'Header is given to app' )
|
||||
or explain( $req->env->{HTTP_AUTH_USER}, 'dwho' );
|
||||
count(1);
|
||||
}
|
||||
return [ 200, [ 'Content-Type', 'text/plain' ], ['Hello'] ];
|
||||
}
|
||||
|
|
|
@ -40,6 +40,36 @@ ok( $h{'Auth-User'} eq 'dwho', 'Header Auth-User is set to "dwho"' )
|
|||
or explain( \%h, 'Auth-User => "dwho"' );
|
||||
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
|
||||
ok( $res = $client->_get( '/deny', undef, undef, "lemonldap=$sessionId" ),
|
||||
'Denied query' );
|
||||
|
|
|
@ -44,6 +44,30 @@ ok( $h{'Headervalue1'} eq 'dwho', 'Headervalue1 is set to "dwho"' )
|
|||
or explain( \%h, 'Headervalue1 => "dwho"' );
|
||||
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
|
||||
ok( $res = $client->_get( '/deny', undef, undef, "lemonldap=$sessionId" ),
|
||||
'Denied query' );
|
||||
|
|
|
@ -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();
|
|
@ -8,19 +8,19 @@ init(
|
|||
'Lemonldap::NG::Handler::Server',
|
||||
{
|
||||
logLevel => 'error',
|
||||
handlerServiceTokenTTL => 2,
|
||||
handlerServiceTokenTTL => 120,
|
||||
vhostOptions => {
|
||||
'test1.example.com' => {
|
||||
vhostHttps => 0,
|
||||
vhostPort => 80,
|
||||
vhostMaintenance => 0,
|
||||
vhostServiceTokenTTL => 3,
|
||||
vhostServiceTokenTTL => 180,
|
||||
},
|
||||
'test2.example.com' => {
|
||||
vhostHttps => 0,
|
||||
vhostPort => 80,
|
||||
vhostMaintenance => 0,
|
||||
vhostServiceTokenTTL => 5,
|
||||
vhostServiceTokenTTL => 300,
|
||||
}
|
||||
},
|
||||
exportedHeaders => {
|
||||
|
@ -38,7 +38,7 @@ 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",
|
||||
'XFromVH=app1-auth.example.com', "serviceHeader1=$sessionId","serviceHeader2=$sessionId",
|
||||
'test2.example.com', '*.example.com'
|
||||
);
|
||||
|
||||
|
@ -48,21 +48,21 @@ ok(
|
|||
VHOSTTYPE => 'ServiceToken',
|
||||
'HTTP_X_LLNG_TOKEN' => $token,
|
||||
),
|
||||
'Query with token'
|
||||
'Query with token 1'
|
||||
);
|
||||
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res->[0], 200 );
|
||||
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] };
|
||||
ok( @headers == 2, 'Found 2 service headers' )
|
||||
ok( @headers == 3, 'Found 3 service headers' )
|
||||
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] );
|
||||
count(2);
|
||||
|
||||
diag 'Waiting';
|
||||
sleep 1;
|
||||
# Waiting
|
||||
Time::Fake->offset("+90s");
|
||||
|
||||
ok(
|
||||
$res = $client->_get(
|
||||
|
@ -70,21 +70,21 @@ ok(
|
|||
VHOSTTYPE => 'ServiceToken',
|
||||
'HTTP_X_LLNG_TOKEN' => $token,
|
||||
),
|
||||
'Query with token'
|
||||
'Query with token 2'
|
||||
);
|
||||
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res->[0], 200 );
|
||||
count(2);
|
||||
|
||||
@headers = grep { /service|^XFromVH$/ } @{ $res->[1] };
|
||||
@headers = grep { /^serviceHeader\d$|^XFromVH$/ } @{ $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] );
|
||||
ok( @values == 2, 'Found 2 service header values' )
|
||||
ok( @values == 3, 'Found 3 service header values' )
|
||||
or print STDERR Data::Dumper::Dumper( $res->[1] );
|
||||
count(2);
|
||||
|
||||
diag 'Waiting';
|
||||
sleep 2;
|
||||
# Waiting
|
||||
Time::Fake->offset("+210s");
|
||||
|
||||
ok(
|
||||
$res = $client->_get(
|
||||
|
@ -92,18 +92,18 @@ ok(
|
|||
VHOSTTYPE => 'ServiceToken',
|
||||
'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);
|
||||
|
||||
@headers = grep { /service|^XFromVH$/ } @{ $res->[1] };
|
||||
@headers = grep { /^serviceHeader\d$|^XFromVH$/ } @{ $res->[1] };
|
||||
ok( @headers == 0, 'NONE service header found' )
|
||||
or print STDERR Data::Dumper::Dumper( $res->[1] );
|
||||
count(1);
|
||||
|
||||
diag 'Waiting';
|
||||
sleep 1;
|
||||
# Waiting
|
||||
Time::Fake->offset("+270s");
|
||||
|
||||
ok(
|
||||
$res = $client->_get(
|
||||
|
@ -111,7 +111,7 @@ ok(
|
|||
VHOSTTYPE => 'ServiceToken',
|
||||
'HTTP_X_LLNG_TOKEN' => $token,
|
||||
),
|
||||
'Query with token'
|
||||
'Query with token 4'
|
||||
);
|
||||
ok( $res->[0] == 200, 'Code is 200' ) or explain( $res->[0], 200 );
|
||||
count(2);
|
||||
|
@ -123,16 +123,16 @@ ok( $headers{'empty'} eq '', 'Found "empty" header without value' )
|
|||
or print STDERR Data::Dumper::Dumper( $res->[1] );
|
||||
count(2);
|
||||
|
||||
@headers = grep { /service|^XFromVH$/ } @{ $res->[1] };
|
||||
@headers = grep { /^serviceHeader\d$|^XFromVH$/ } @{ $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] );
|
||||
ok( @values == 2, 'Found 2 service header values' )
|
||||
ok( @values == 3, 'Found 3 service header values' )
|
||||
or print STDERR Data::Dumper::Dumper( $res->[1] );
|
||||
count(2);
|
||||
|
||||
diag 'Waiting';
|
||||
sleep 1;
|
||||
# Waiting
|
||||
Time::Fake->offset("+330s");
|
||||
|
||||
ok(
|
||||
$res = $client->_get(
|
||||
|
@ -140,12 +140,12 @@ ok(
|
|||
VHOSTTYPE => 'ServiceToken',
|
||||
'HTTP_X_LLNG_TOKEN' => $token,
|
||||
),
|
||||
'Query with token'
|
||||
'Query with token 5'
|
||||
);
|
||||
ok( $res->[0] == 302, 'Code is 302' ) or explain( $res->[0], 302 );
|
||||
count(2);
|
||||
|
||||
@headers = grep { /service|^XFromVH$/ } @{ $res->[1] };
|
||||
@headers = grep { /^serviceHeader\d$|^XFromVH$/ } @{ $res->[1] };
|
||||
ok( @headers == 0, 'NONE service header found' )
|
||||
or print STDERR Data::Dumper::Dumper( $res->[1] );
|
||||
count(1);
|
||||
|
@ -156,12 +156,12 @@ ok(
|
|||
VHOSTTYPE => 'ServiceToken',
|
||||
'HTTP_X_LLNG_TOKEN' => $token,
|
||||
),
|
||||
'Query with token'
|
||||
'Query with token 6'
|
||||
);
|
||||
ok( $res->[0] == 302, 'Code is 302' ) or explain( $res->[0], 302 );
|
||||
count(2);
|
||||
|
||||
@headers = grep { /service|^XFromVH$/ } @{ $res->[1] };
|
||||
@headers = grep { /^serviceHeader\d$|^XFromVH$/ } @{ $res->[1] };
|
||||
ok( @headers == 0, 'NONE service header found' )
|
||||
or print STDERR Data::Dumper::Dumper( $res->[1] );
|
||||
count(1);
|
||||
|
@ -173,12 +173,12 @@ ok(
|
|||
VHOSTTYPE => 'ServiceToken',
|
||||
'HTTP_X_LLNG_TOKEN' => $token,
|
||||
),
|
||||
'Query with token'
|
||||
'Query with token 7'
|
||||
);
|
||||
ok( $res->[0] == 302, 'Code is 302' ) or explain( $res->[0], 302 );
|
||||
count(2);
|
||||
|
||||
@headers = grep { /service|^XFromVH$/ } @{ $res->[1] };
|
||||
@headers = grep { /^serviceHeader\d$|^XFromVH$/ } @{ $res->[1] };
|
||||
ok( @headers == 0, 'NONE service header found' )
|
||||
or print STDERR Data::Dumper::Dumper( $res->[1] );
|
||||
count(1);
|
||||
|
|
|
@ -11,6 +11,11 @@ SKIP: {
|
|||
if ($@) {
|
||||
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; };
|
||||
if ($@) {
|
||||
skip 'Apache::Session::Generate::MD5 not found', $maintests;
|
||||
|
|
|
@ -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.
|
|
@ -43,6 +43,10 @@
|
|||
"test1.example.com": {
|
||||
"^/AuthStrong(?#AuthnLevel=5)": "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",
|
||||
"^/deny": "deny",
|
||||
"^/user_(\\w+)/": "$uid eq $_rulematch[1]",
|
||||
|
|
|
@ -9,6 +9,10 @@ use_ok('Lemonldap::NG::Common::PSGI::Cli::Lib');
|
|||
our $client;
|
||||
our $count = 1;
|
||||
|
||||
BEGIN {
|
||||
require 't/Time-Fake.pm';
|
||||
}
|
||||
|
||||
no warnings 'redefine';
|
||||
|
||||
my $module;
|
||||
|
|
|
@ -183,6 +183,7 @@ site/htdocs/static/languages/de.json
|
|||
site/htdocs/static/languages/en.json
|
||||
site/htdocs/static/languages/fr.json
|
||||
site/htdocs/static/languages/it.json
|
||||
site/htdocs/static/languages/tr.json
|
||||
site/htdocs/static/languages/vi.json
|
||||
site/htdocs/static/languages/zh.json
|
||||
site/htdocs/static/logos/ar.png
|
||||
|
|
|
@ -1070,6 +1070,12 @@ qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.]
|
|||
'customPassword' => {
|
||||
'type' => 'text'
|
||||
},
|
||||
'customPlugins' => {
|
||||
'type' => 'text'
|
||||
},
|
||||
'customPluginsParams' => {
|
||||
'type' => 'keyTextContainer'
|
||||
},
|
||||
'customRegister' => {
|
||||
'type' => 'text'
|
||||
},
|
||||
|
@ -1264,6 +1270,14 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
|
|||
'default' => 120,
|
||||
'type' => 'int'
|
||||
},
|
||||
'globalLogoutRule' => {
|
||||
'default' => 0,
|
||||
'type' => 'boolOrExpr'
|
||||
},
|
||||
'globalLogoutTimer' => {
|
||||
'default' => 1,
|
||||
'type' => 'bool'
|
||||
},
|
||||
'globalStorage' => {
|
||||
'default' => 'Apache::Session::File',
|
||||
'type' => 'PerlModule'
|
||||
|
@ -1795,7 +1809,7 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
|
|||
'managerDn' => {
|
||||
'default' => '',
|
||||
'msgFail' => '__badValue__',
|
||||
'test' => qr/^(?:\w+=.*)?$/,
|
||||
'test' => qr/^.*$/,
|
||||
'type' => 'text'
|
||||
},
|
||||
'managerPassword' => {
|
||||
|
@ -1835,6 +1849,10 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
|
|||
'default' => 0,
|
||||
'type' => 'bool'
|
||||
},
|
||||
'notificationDefaultCond' => {
|
||||
'default' => '',
|
||||
'type' => 'text'
|
||||
},
|
||||
'notificationServer' => {
|
||||
'default' => 0,
|
||||
'type' => 'bool'
|
||||
|
@ -2021,8 +2039,14 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
|
|||
'type' => 'subContainer'
|
||||
},
|
||||
'oidcRPMetaDataOptionsAccessTokenExpiration' => {
|
||||
'default' => 3600,
|
||||
'type' => 'int'
|
||||
'type' => 'int'
|
||||
},
|
||||
'oidcRPMetaDataOptionsAllowOffline' => {
|
||||
'default' => 0,
|
||||
'type' => 'bool'
|
||||
},
|
||||
'oidcRPMetaDataOptionsAuthorizationCodeExpiration' => {
|
||||
'type' => 'int'
|
||||
},
|
||||
'oidcRPMetaDataOptionsBypassConsent' => {
|
||||
'default' => 0,
|
||||
|
@ -2045,8 +2069,10 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
|
|||
'type' => 'text'
|
||||
},
|
||||
'oidcRPMetaDataOptionsIDTokenExpiration' => {
|
||||
'default' => 3600,
|
||||
'type' => 'int'
|
||||
'type' => 'int'
|
||||
},
|
||||
'oidcRPMetaDataOptionsIDTokenForceClaims' => {
|
||||
'type' => 'bool'
|
||||
},
|
||||
'oidcRPMetaDataOptionsIDTokenSignAlg' => {
|
||||
'default' => 'HS512',
|
||||
|
@ -2101,6 +2127,9 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
|
|||
'oidcRPMetaDataOptionsLogoutUrl' => {
|
||||
'type' => 'url'
|
||||
},
|
||||
'oidcRPMetaDataOptionsOfflineSessionExpiration' => {
|
||||
'type' => 'int'
|
||||
},
|
||||
'oidcRPMetaDataOptionsPostLogoutRedirectUris' => {
|
||||
'type' => 'text'
|
||||
},
|
||||
|
@ -2111,6 +2140,10 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
|
|||
'oidcRPMetaDataOptionsRedirectUris' => {
|
||||
'type' => 'text'
|
||||
},
|
||||
'oidcRPMetaDataOptionsRefreshToken' => {
|
||||
'default' => 0,
|
||||
'type' => 'bool'
|
||||
},
|
||||
'oidcRPMetaDataOptionsRequirePKCE' => {
|
||||
'default' => 0,
|
||||
'type' => 'bool'
|
||||
|
@ -2128,6 +2161,10 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
|
|||
'default' => 600,
|
||||
'type' => 'int'
|
||||
},
|
||||
'oidcServiceAccessTokenExpiration' => {
|
||||
'default' => 3600,
|
||||
'type' => 'int'
|
||||
},
|
||||
'oidcServiceAllowAuthorizationCodeFlow' => {
|
||||
'default' => 1,
|
||||
'type' => 'bool'
|
||||
|
@ -2144,6 +2181,14 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
|
|||
'default' => 0,
|
||||
'type' => 'bool'
|
||||
},
|
||||
'oidcServiceAuthorizationCodeExpiration' => {
|
||||
'default' => 60,
|
||||
'type' => 'int'
|
||||
},
|
||||
'oidcServiceIDTokenExpiration' => {
|
||||
'default' => 3600,
|
||||
'type' => 'int'
|
||||
},
|
||||
'oidcServiceKeyIdSig' => {
|
||||
'type' => 'text'
|
||||
},
|
||||
|
@ -2198,6 +2243,10 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
|
|||
'default' => 'userinfo',
|
||||
'type' => 'text'
|
||||
},
|
||||
'oidcServiceOfflineSessionExpiration' => {
|
||||
'default' => 2592000,
|
||||
'type' => 'int'
|
||||
},
|
||||
'oidcServicePrivateKeySig' => {
|
||||
'type' => 'RSAPrivateKey'
|
||||
},
|
||||
|
@ -2588,6 +2637,9 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
|
|||
],
|
||||
'type' => 'select'
|
||||
},
|
||||
'refreshSessions' => {
|
||||
'type' => 'bool'
|
||||
},
|
||||
'registerConfirmSubject' => {
|
||||
'type' => 'text'
|
||||
},
|
||||
|
|
|
@ -450,9 +450,10 @@ sub attributes {
|
|||
flags => 'p',
|
||||
},
|
||||
checkUserSearchAttributes => {
|
||||
type => 'text',
|
||||
documentation => 'Attributes used for retrieving sessions in user DataBase',
|
||||
flags => 'p',
|
||||
type => 'text',
|
||||
documentation =>
|
||||
'Attributes used for retrieving sessions in user DataBase',
|
||||
flags => 'p',
|
||||
},
|
||||
checkUserDisplayPersistentInfo => {
|
||||
default => 0,
|
||||
|
@ -466,6 +467,18 @@ sub attributes {
|
|||
documentation => 'Display session empty values',
|
||||
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 => {
|
||||
default => 3,
|
||||
type => 'int',
|
||||
|
@ -553,10 +566,14 @@ sub attributes {
|
|||
documentation =>
|
||||
'Avoid asking confirmation when an Issuer asks to renew auth',
|
||||
},
|
||||
refreshSessions => {
|
||||
type => 'bool',
|
||||
documentation => 'Refresh sessions plugin',
|
||||
},
|
||||
forceGlobalStorageIssuerOTT => {
|
||||
type => 'bool',
|
||||
documentation =>
|
||||
'Force Issuer tokens be stored into Global Storage',
|
||||
'Force Issuer tokens to be stored into Global Storage',
|
||||
},
|
||||
handlerInternalCache => {
|
||||
type => 'int',
|
||||
|
@ -1110,6 +1127,11 @@ sub attributes {
|
|||
type => 'bool',
|
||||
documentation => 'Notification server activation',
|
||||
},
|
||||
notificationDefaultCond => {
|
||||
type => 'text',
|
||||
default => '',
|
||||
documentation => 'Notification default condition',
|
||||
},
|
||||
notificationServerGET => {
|
||||
default => 0,
|
||||
type => 'bool',
|
||||
|
@ -2042,15 +2064,15 @@ sub attributes {
|
|||
vhostType => {
|
||||
type => 'select',
|
||||
select => [
|
||||
{ k => 'AuthBasic', v => 'AuthBasic' },
|
||||
{ k => 'CDA', v => 'CDA' },
|
||||
{ k => 'DevOps', v => 'DevOps' },
|
||||
{ k => 'DevOpsST', v => 'DevOpsST' },
|
||||
{ k => 'Main', v => 'Main' },
|
||||
{ k => 'OAuth2', v => 'OAuth2' },
|
||||
{ k => 'SecureToken', v => 'SecureToken' },
|
||||
{ k => 'ServiceToken', v => 'ServiceToken' },
|
||||
{ k => 'ZimbraPreAuth',v => 'ZimbraPreAuth' },
|
||||
{ k => 'AuthBasic', v => 'AuthBasic' },
|
||||
{ k => 'CDA', v => 'CDA' },
|
||||
{ k => 'DevOps', v => 'DevOps' },
|
||||
{ k => 'DevOpsST', v => 'DevOpsST' },
|
||||
{ k => 'Main', v => 'Main' },
|
||||
{ k => 'OAuth2', v => 'OAuth2' },
|
||||
{ k => 'SecureToken', v => 'SecureToken' },
|
||||
{ k => 'ServiceToken', v => 'ServiceToken' },
|
||||
{ k => 'ZimbraPreAuth', v => 'ZimbraPreAuth' },
|
||||
],
|
||||
default => 'Main',
|
||||
documentation => 'Handler type',
|
||||
|
@ -2925,7 +2947,7 @@ sub attributes {
|
|||
# LDAP
|
||||
managerDn => {
|
||||
type => 'text',
|
||||
test => qr/^(?:\w+=.*)?$/,
|
||||
test => qr/^.*$/,
|
||||
msgFail => '__badValue__',
|
||||
default => '',
|
||||
documentation => 'LDAP manager DN',
|
||||
|
@ -3573,6 +3595,16 @@ m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
|
|||
documentation => 'Custom additional parameters',
|
||||
},
|
||||
|
||||
# Custom plugins
|
||||
customPlugins => {
|
||||
type => 'text',
|
||||
documentation => 'Custom plugins',
|
||||
},
|
||||
customPluginsParams => {
|
||||
type => 'keyTextContainer',
|
||||
documentation => 'Custom plugins parameters',
|
||||
},
|
||||
|
||||
# OpenID Connect auth params
|
||||
oidcAuthnLevel => {
|
||||
type => 'int',
|
||||
|
@ -3679,6 +3711,26 @@ m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
|
|||
default => 0,
|
||||
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 => {
|
||||
type => 'PerlModule',
|
||||
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',
|
||||
},
|
||||
oidcRPMetaDataOptionsIDTokenExpiration =>
|
||||
{ type => 'int', default => 3600 },
|
||||
oidcRPMetaDataOptionsAccessTokenExpiration =>
|
||||
{ type => 'int', default => 3600 },
|
||||
oidcRPMetaDataOptionsRedirectUris => { type => 'text', },
|
||||
oidcRPMetaDataOptionsIDTokenExpiration => { type => 'int' },
|
||||
oidcRPMetaDataOptionsIDTokenForceClaims => { type => 'bool' },
|
||||
oidcRPMetaDataOptionsAccessTokenExpiration => { type => 'int' },
|
||||
oidcRPMetaDataOptionsAuthorizationCodeExpiration => { type => 'int' },
|
||||
oidcRPMetaDataOptionsOfflineSessionExpiration => { type => 'int' },
|
||||
oidcRPMetaDataOptionsRedirectUris => { type => 'text', },
|
||||
oidcRPMetaDataOptionsExtraClaims =>
|
||||
{ type => 'keyTextContainer', default => {} },
|
||||
oidcRPMetaDataOptionsBypassConsent => {
|
||||
|
@ -3820,6 +3873,16 @@ m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
|
|||
default => 0,
|
||||
documentation => 'Require PKCE',
|
||||
},
|
||||
oidcRPMetaDataOptionsAllowOffline => {
|
||||
type => 'bool',
|
||||
default => 0,
|
||||
documentation => 'Allow offline access',
|
||||
},
|
||||
oidcRPMetaDataOptionsRefreshToken => {
|
||||
type => 'bool',
|
||||
default => 0,
|
||||
documentation => 'Issue refresh tokens',
|
||||
},
|
||||
oidcRPMetaDataOptionsRule => {
|
||||
type => 'text',
|
||||
test => sub { return perlExpr(@_) },
|
||||
|
|
|
@ -203,7 +203,12 @@ sub cTrees {
|
|||
'oidcRPMetaDataOptionsUserIDAttr',
|
||||
'oidcRPMetaDataOptionsIDTokenSignAlg',
|
||||
'oidcRPMetaDataOptionsIDTokenExpiration',
|
||||
'oidcRPMetaDataOptionsIDTokenForceClaims',
|
||||
'oidcRPMetaDataOptionsAccessTokenExpiration',
|
||||
'oidcRPMetaDataOptionsAuthorizationCodeExpiration',
|
||||
'oidcRPMetaDataOptionsAllowOffline',
|
||||
'oidcRPMetaDataOptionsRefreshToken',
|
||||
'oidcRPMetaDataOptionsOfflineSessionExpiration',
|
||||
'oidcRPMetaDataOptionsRedirectUris',
|
||||
'oidcRPMetaDataOptionsBypassConsent',
|
||||
{
|
||||
|
|
|
@ -103,9 +103,10 @@ sub portalConstants {
|
|||
PE_IMPERSONATION_SERVICE_NOT_ALLOWED => 93,
|
||||
PE_ISSUERMISSINGREQATTR => 94,
|
||||
PE_DECRYPTVALUE_SERVICE_NOT_ALLOWED => 95,
|
||||
PE_RESETCERTIFICATE_INVALID => 96,
|
||||
PE_BADOTP => 96,
|
||||
PE_RESETCERTIFICATE_FORMEMPTY => 97,
|
||||
PE_RESETCERTIFICATE_FIRSTACCESS => 98,
|
||||
PE_RESETCERTIFICATE_INVALID => 99,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -628,6 +628,7 @@ sub tree {
|
|||
help => 'notifications.html#server',
|
||||
nodes => [
|
||||
'notificationServer',
|
||||
'notificationDefaultCond',
|
||||
'notificationServerSentAttributes',
|
||||
{
|
||||
title =>
|
||||
|
@ -712,6 +713,13 @@ sub tree {
|
|||
help => 'autosignin.html',
|
||||
nodes => ['autoSigninRules'],
|
||||
},
|
||||
{
|
||||
title => 'globalLogout',
|
||||
help => 'globallogout.html',
|
||||
form => 'simpleInputContainer',
|
||||
nodes =>
|
||||
[ 'globalLogoutRule', 'globalLogoutTimer', ],
|
||||
},
|
||||
{
|
||||
title => 'stateCheck',
|
||||
help => 'checkstate.html',
|
||||
|
@ -757,11 +765,15 @@ sub tree {
|
|||
title => 'decryptValue',
|
||||
help => 'decryptvalue.html',
|
||||
form => 'simpleInputContainer',
|
||||
nodes => [
|
||||
'decryptValueRule',
|
||||
'decryptValueFunctions',
|
||||
]
|
||||
nodes =>
|
||||
[ 'decryptValueRule', 'decryptValueFunctions', ]
|
||||
},
|
||||
{
|
||||
title => 'customPluginsNode',
|
||||
help => 'plugincustom.html',
|
||||
nodes => [ 'customPlugins', 'customPluginsParams' ]
|
||||
},
|
||||
'refreshSessions',
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -1206,6 +1218,10 @@ sub tree {
|
|||
'oidcServiceAllowAuthorizationCodeFlow',
|
||||
'oidcServiceAllowImplicitFlow',
|
||||
'oidcServiceAllowHybridFlow',
|
||||
'oidcServiceAuthorizationCodeExpiration',
|
||||
'oidcServiceAccessTokenExpiration',
|
||||
'oidcServiceIDTokenExpiration',
|
||||
'oidcServiceOfflineSessionExpiration',
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
@ -453,7 +453,6 @@ sub tests {
|
|||
|
||||
# Warn if 2F dependencies seem missing
|
||||
sfaDependencies => sub {
|
||||
|
||||
my $ok = 0;
|
||||
foreach (qw(u totp utotp yubikey)) {
|
||||
$ok ||= $conf->{ $_ . '2fActivation' };
|
||||
|
@ -684,8 +683,24 @@ sub tests {
|
|||
"Notifications enabled WITHOUT persistent session storage" )
|
||||
if ( $conf->{notification} );
|
||||
return ( 1,
|
||||
"BruteForceProtection plugin enabled WITHOUT persistent session storage" )
|
||||
if ( $conf->{bruteForceProtection} );
|
||||
"BruteForceProtection plugin enabled WITHOUT persistent session storage"
|
||||
) 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 1;
|
||||
|
|
|
@ -18,7 +18,6 @@ extends 'Lemonldap::NG::Common::Conf::AccessLib';
|
|||
our $VERSION = '2.1.0';
|
||||
|
||||
has notifAccess => ( is => 'rw' );
|
||||
|
||||
has notifFormat => ( is => 'rw' );
|
||||
|
||||
#############################
|
||||
|
@ -75,7 +74,6 @@ sub addRoutes {
|
|||
{ done => { ':notificationId' => 'deleteDoneNotification' } },
|
||||
['DELETE']
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
sub setNotifAccess {
|
||||
|
@ -158,6 +156,7 @@ sub notifications {
|
|||
my ( $notifs, $res );
|
||||
|
||||
$notifs = $self->notifAccess->$sub();
|
||||
my $total = ( keys %$notifs );
|
||||
|
||||
# Restrict to wanted values
|
||||
if (
|
||||
|
@ -201,6 +200,7 @@ sub notifications {
|
|||
result => 1,
|
||||
count => $count,
|
||||
values => $res,
|
||||
total => $total
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -221,7 +221,7 @@ sub notifications {
|
|||
}
|
||||
}
|
||||
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}");
|
||||
|
||||
unless ( $json->{date} =~ /^\d{4}-\d{2}-\d{2}$/ ) {
|
||||
$self->logger->error("Malformed date");
|
||||
return $self->sendError( $req, "Malformed date", 200 );
|
||||
}
|
||||
|
||||
|
@ -312,8 +313,8 @@ sub newNotification {
|
|||
delete $json->{xml};
|
||||
};
|
||||
if ($@) {
|
||||
$self->logger->error("Notification malformed $@");
|
||||
return $self->sendError( $req, "Notification malformed: $@", 200 );
|
||||
$self->logger->error("Malformed notification $@");
|
||||
return $self->sendError( $req, "Malformed notification: $@", 200 );
|
||||
}
|
||||
$newNotif = to_json($json);
|
||||
}
|
||||
|
|
|
@ -99,6 +99,7 @@ sub sessions {
|
|||
my $params = $req->parameters();
|
||||
my $type = delete $params->{sessionType};
|
||||
$type = $type eq 'global' ? 'SSO' : ucfirst($type);
|
||||
$type = $type eq 'Offline' ? 'OIDCI' : ucfirst($type);
|
||||
|
||||
my $res;
|
||||
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
# LemonLDAP::NG Notifications Explorer client
|
||||
###
|
||||
|
||||
# Max number of notifications to display (see overScheme)
|
||||
max = 25
|
||||
|
||||
scheme = [
|
||||
(v) ->
|
||||
"groupBy=substr(uid,1)"
|
||||
|
@ -11,6 +14,19 @@ scheme = [
|
|||
"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
|
||||
menu =
|
||||
actives: [
|
||||
|
@ -109,11 +125,12 @@ llapp.controller 'NotificationsExplorerCtrl', [ '$scope', '$translator', '$locat
|
|||
$scope.waiting = false
|
||||
$scope.init()
|
||||
|
||||
# Open node
|
||||
$scope.stoggle = (scope) ->
|
||||
node = scope.$modelValue
|
||||
if node.nodes.length == 0
|
||||
$scope.updateTree node.value, node.nodes, node.level, node.query
|
||||
scope.toggle()
|
||||
$scope.updateTree node.value, node.nodes, node.level, node.over, node.query, node.count
|
||||
scope.toggle()
|
||||
|
||||
$scope.notifDate = (s) ->
|
||||
if s?
|
||||
|
@ -139,20 +156,35 @@ llapp.controller 'NotificationsExplorerCtrl', [ '$scope', '$translator', '$locat
|
|||
$scope.init()
|
||||
|
||||
autoId = 0
|
||||
$scope.updateTree = (value, node, level, currentQuery) ->
|
||||
$scope.updateTree = (value, node, level, over, currentQuery, count) ->
|
||||
$scope.waiting = true
|
||||
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) ->
|
||||
data = response.data
|
||||
if data.result
|
||||
for n in data.values
|
||||
autoId++
|
||||
n.id = "node#{autoId}"
|
||||
if level <scheme.length - 1
|
||||
if level < scheme.length - 1
|
||||
n.nodes = []
|
||||
n.level = level + 1
|
||||
n.query = query
|
||||
n.over = over
|
||||
node.push n
|
||||
$scope.total = data.total if value == ''
|
||||
$scope.waiting = false
|
||||
, (resp) ->
|
||||
$scope.waiting = false
|
||||
|
@ -246,7 +278,7 @@ llapp.controller 'NotificationsExplorerCtrl', [ '$scope', '$translator', '$locat
|
|||
$scope.currentNotification = null
|
||||
$q.all [
|
||||
$translator.init $scope.lang
|
||||
$scope.updateTree '', $scope.data, 0
|
||||
$scope.updateTree '', $scope.data, 0, 0
|
||||
]
|
||||
.then ->
|
||||
$scope.waiting = false
|
||||
|
|
|
@ -78,26 +78,26 @@ overScheme =
|
|||
_whatToTrace: (t,v,level,over) ->
|
||||
# "v.length > over" avoids a loop if one user opened more than "max"
|
||||
# sessions
|
||||
console.log 'overSchema => level', level, 'over', over
|
||||
console.log 'overScheme => level', level, 'over', over
|
||||
if level == 1 and v.length > over
|
||||
"#{t}=#{v}*&groupBy=substr(#{t},#{(level+over+1)})"
|
||||
else
|
||||
null
|
||||
# Note: IPv4 only
|
||||
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
|
||||
"#{t}=#{v}*&groupBy=net(#{t},#{16*level+4*(over+1)},#{1+level+over})"
|
||||
else
|
||||
null
|
||||
_startTime: (t,v,level,over) ->
|
||||
console.log 'overSchema => level', level, 'over', over
|
||||
console.log 'overScheme => level', level, 'over', over
|
||||
if level > 3
|
||||
"#{t}=#{v}*&groupBy=substr(#{t},#{(10+level+over)})"
|
||||
else
|
||||
null
|
||||
_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
|
||||
"#{t}=#{v}*&groupBy=substr(#{t},#{(level+over+1)})"
|
||||
else
|
||||
|
@ -420,7 +420,7 @@ llapp.controller 'SessionsExplorerCtrl', ['$scope', '$translator', '$location',
|
|||
sessionType = 'global'
|
||||
if n == null
|
||||
$scope.type = '_whatToTrace'
|
||||
else if n[1].match /^(persistent)$/
|
||||
else if n[1].match /^(persistent|offline)$/
|
||||
sessionType = RegExp.$1
|
||||
$scope.type = '_session_uid'
|
||||
else
|
||||
|
|
|
@ -44,8 +44,12 @@
|
|||
/* Tables */
|
||||
|
||||
.panel .table th {
|
||||
width: 10%;
|
||||
vertical-align: middle;
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
.th_rulesAuthnLevel {
|
||||
width: 2% !important;
|
||||
}
|
||||
|
||||
/* Category tree */
|
||||
|
|
|
@ -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}
|
|
@ -5,11 +5,11 @@
|
|||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="20%" trspan="comments"></th>
|
||||
<th width="30%" trspan="regexps"></th>
|
||||
<th width="40%" trspan="rules"></th>
|
||||
<th width="7%" trspan="rulesAuthnLevel"></th>
|
||||
<th />
|
||||
<th trspan="comments"></th>
|
||||
<th trspan="regexps"></th>
|
||||
<th trspan="rules"></th>
|
||||
<th class="th_rulesAuthnLevel" trspan="rulesAuthnLevel"></th>
|
||||
<th class="th_rulesAuthnLevel" />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -33,7 +33,7 @@
|
|||
<input type="number" class="form-control" ng-model="s.level"/>
|
||||
</td>
|
||||
<td ng-if="s.re=='default'">
|
||||
<input class="form-control" placeholder="defaultLevel" readonly/>
|
||||
<input class="form-control" placeholder="default" readonly/>
|
||||
</td>
|
||||
<td>
|
||||
<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
|
@ -473,19 +473,49 @@ function templates(tpl,key) {
|
|||
"type" : "select"
|
||||
},
|
||||
{
|
||||
"default" : 3600,
|
||||
"get" : tpl+"s/"+key+"/"+"oidcRPMetaDataOptionsIDTokenExpiration",
|
||||
"id" : tpl+"s/"+key+"/"+"oidcRPMetaDataOptionsIDTokenExpiration",
|
||||
"title" : "oidcRPMetaDataOptionsIDTokenExpiration",
|
||||
"type" : "int"
|
||||
},
|
||||
{
|
||||
"default" : 3600,
|
||||
"get" : tpl+"s/"+key+"/"+"oidcRPMetaDataOptionsIDTokenForceClaims",
|
||||
"id" : tpl+"s/"+key+"/"+"oidcRPMetaDataOptionsIDTokenForceClaims",
|
||||
"title" : "oidcRPMetaDataOptionsIDTokenForceClaims",
|
||||
"type" : "bool"
|
||||
},
|
||||
{
|
||||
"get" : tpl+"s/"+key+"/"+"oidcRPMetaDataOptionsAccessTokenExpiration",
|
||||
"id" : tpl+"s/"+key+"/"+"oidcRPMetaDataOptionsAccessTokenExpiration",
|
||||
"title" : "oidcRPMetaDataOptionsAccessTokenExpiration",
|
||||
"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",
|
||||
"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
|
@ -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);
|
||||
//# sourceMappingURL=lemonldap-ng-manager/site/htdocs/static/js/filterFunctions.min.js.map
|
||||
(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);
|
|
@ -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"}
|
|
@ -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
|
@ -5,7 +5,9 @@
|
|||
*/
|
||||
|
||||
(function() {
|
||||
var llapp, menu, scheme;
|
||||
var llapp, max, menu, overScheme, scheme;
|
||||
|
||||
max = 25;
|
||||
|
||||
scheme = [
|
||||
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 = {
|
||||
actives: [
|
||||
{
|
||||
|
@ -131,9 +142,9 @@
|
|||
var node;
|
||||
node = scope.$modelValue;
|
||||
if (node.nodes.length === 0) {
|
||||
$scope.updateTree(node.value, node.nodes, node.level, node.query);
|
||||
return scope.toggle();
|
||||
$scope.updateTree(node.value, node.nodes, node.level, node.over, node.query, node.count);
|
||||
}
|
||||
return scope.toggle();
|
||||
};
|
||||
$scope.notifDate = function(s) {
|
||||
var d;
|
||||
|
@ -164,10 +175,21 @@
|
|||
}
|
||||
});
|
||||
autoId = 0;
|
||||
$scope.updateTree = function(value, node, level, currentQuery) {
|
||||
var query;
|
||||
$scope.updateTree = function(value, node, level, over, currentQuery, count) {
|
||||
var query, tmp;
|
||||
$scope.waiting = true;
|
||||
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) {
|
||||
var data, i, len, n, ref;
|
||||
data = response.data;
|
||||
|
@ -181,9 +203,13 @@
|
|||
n.nodes = [];
|
||||
n.level = level + 1;
|
||||
n.query = query;
|
||||
n.over = over;
|
||||
}
|
||||
node.push(n);
|
||||
}
|
||||
if (value === '') {
|
||||
$scope.total = data.total;
|
||||
}
|
||||
}
|
||||
return $scope.waiting = false;
|
||||
}, function(resp) {
|
||||
|
@ -301,7 +327,7 @@
|
|||
$scope.data = [];
|
||||
$scope.currentScope = 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;
|
||||
}, function(resp) {
|
||||
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
|
@ -83,7 +83,7 @@
|
|||
|
||||
overScheme = {
|
||||
_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) {
|
||||
return t + "=" + v + "*&groupBy=substr(" + t + "," + (level + over + 1) + ")";
|
||||
} else {
|
||||
|
@ -91,7 +91,7 @@
|
|||
}
|
||||
},
|
||||
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) {
|
||||
return t + "=" + v + "*&groupBy=net(" + t + "," + (16 * level + 4 * (over + 1)) + "," + (1 + level + over) + ")";
|
||||
} else {
|
||||
|
@ -99,7 +99,7 @@
|
|||
}
|
||||
},
|
||||
_startTime: function(t, v, level, over) {
|
||||
console.log('overSchema => level', level, 'over', over);
|
||||
console.log('overScheme => level', level, 'over', over);
|
||||
if (level > 3) {
|
||||
return t + "=" + v + "*&groupBy=substr(" + t + "," + (10 + level + over) + ")";
|
||||
} else {
|
||||
|
@ -107,7 +107,7 @@
|
|||
}
|
||||
},
|
||||
_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) {
|
||||
return t + "=" + v + "*&groupBy=substr(" + t + "," + (level + over + 1) + ")";
|
||||
} else {
|
||||
|
@ -511,7 +511,7 @@
|
|||
sessionType = 'global';
|
||||
if (n === null) {
|
||||
$scope.type = '_whatToTrace';
|
||||
} else if (n[1].match(/^(persistent)$/)) {
|
||||
} else if (n[1].match(/^(persistent|offline)$/)) {
|
||||
sessionType = RegExp.$1;
|
||||
$scope.type = '_session_uid';
|
||||
} 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
|
@ -219,6 +219,9 @@
|
|||
"customModule":"وحدة مخصصة",
|
||||
"customParams":"أسماء الوحدات المخصصة",
|
||||
"customPassword":"وحدة كلمة المرورالمخصصة",
|
||||
"customPlugins":"Modules list",
|
||||
"customPluginsNode":"Custom plugins",
|
||||
"customPluginsParams":"Additional parameters",
|
||||
"customPortalSkin":"غلاف البوابة مخصص",
|
||||
"customRegister":"وحدة تسجيل مخصص",
|
||||
"customToTrace":"REMOTE_CUSTOM",
|
||||
|
@ -247,7 +250,7 @@
|
|||
"dbiUserTable":"جدول المستخدم",
|
||||
"decryptValue":"Decrypt value",
|
||||
"decryptValueFunctions":"Decrypt functions",
|
||||
"decryptValueRule":"Use rule",
|
||||
"decryptValueRule":"استخدام القاعدة",
|
||||
"default":"الاعْتيادي",
|
||||
"defaultRule":"القاعدة الاعتيادية ",
|
||||
"demoModeOn":"هذا المدير يعمل في طريقة العرض",
|
||||
|
@ -313,6 +316,9 @@
|
|||
"forms":"إستمارات",
|
||||
"friendlyName":"اسم مألوف",
|
||||
"generalParameters":"المعاييرالعامة",
|
||||
"globalLogout":"Global logout",
|
||||
"globalLogoutRule":"Activation",
|
||||
"globalLogoutTimer":"قبول تلقائي للوقت",
|
||||
"globalStorage":"أباتشي :: وحدة الجلسة",
|
||||
"globalStorageOptions":"أباتشي :: معايير وحدة الجلسة",
|
||||
"gpgAuthnLevel":"مستوى إثبات الهوية",
|
||||
|
@ -430,7 +436,7 @@
|
|||
"loadFromUrl":"تحميل من أل يو أر ل",
|
||||
"localSessionStorage":"وحدة ذاكرة التخزين المؤقت",
|
||||
"localSessionStorageOptions":"خيارات وحدة التخزين المؤقت",
|
||||
"locationRules":"قاعدة الدخول",
|
||||
"locationRules":"Access rules",
|
||||
"loginHistory":"سجل تسجيل الدخول",
|
||||
"loginHistoryEnabled":"تفعيل",
|
||||
"logo":"شعار",
|
||||
|
@ -507,6 +513,8 @@
|
|||
"notAValidPerlExpression":"عبارة بيرل ليست صحيحة",
|
||||
"notification":"تفعيل",
|
||||
"notifications":"إشعار",
|
||||
"notification_s":"notification(s)",
|
||||
"notificationDefaultCond":"Default condition",
|
||||
"notificationServer":"إشعارالخادم",
|
||||
"notificationServerDELETE":"DELETE method",
|
||||
"notificationServerGET":"GET method",
|
||||
|
@ -533,6 +541,7 @@
|
|||
"nullParams":"لا شيء في المعايير",
|
||||
"number":"رقم",
|
||||
"off":"إيقاف",
|
||||
"offlineSessions":"Offline sessions",
|
||||
"oldValue":"قيمة قديمة",
|
||||
"on":"تنشيط",
|
||||
"oidcAuthnLevel":"مستوى إثبات الهوية",
|
||||
|
@ -544,6 +553,7 @@
|
|||
"oidcOPMetaDataNode":" أوبين أيدي كونيكت بروفيدر",
|
||||
"oidcOPMetaDataOptions":"الخيارات",
|
||||
"oidcRPMetaDataOptionsAuthentication":"إثبات الهوية",
|
||||
"oidcRPMetaDataOptionsAllowOffline":"Allow offline access",
|
||||
"oidcOPMetaDataOptionsCheckJWTSignature":"توقيع",
|
||||
"oidcOPMetaDataOptionsClientID":"معرف العميل",
|
||||
"oidcOPMetaDataOptionsClientSecret":"سرالعميل",
|
||||
|
@ -573,6 +583,7 @@
|
|||
"oidcRPMetaDataNode":"الأطراف المعتمد لي أوبين أيدي كونيكت",
|
||||
"oidcRPMetaDataOptions":"الخيارات",
|
||||
"oidcRPMetaDataOptionsAccessTokenExpiration":"انتهاء صلاحية التوكن",
|
||||
"oidcRPMetaDataOptionsAuthorizationCodeExpiration":"Authorization Code expiration",
|
||||
"oidcRPMetaDataOptionsBypassConsent":"تخطى الموافقة ",
|
||||
"oidcRPMetaDataOptionsClientID":"معرف العميل",
|
||||
"oidcRPMetaDataOptionsClientSecret":"سرالعميل",
|
||||
|
@ -581,6 +592,9 @@
|
|||
"oidcRPMetaDataOptionsIcon":"شعار",
|
||||
"oidcRPMetaDataOptionsIDTokenExpiration":" انتهاء صلاحية تعريف التوكن",
|
||||
"oidcRPMetaDataOptionsIDTokenSignAlg":"خوارزمية توقيع آي دي التوكن",
|
||||
"oidcRPMetaDataOptionsIDTokenForceClaims":"Force claims to be returned in ID Token",
|
||||
"oidcRPMetaDataOptionsOfflineSessionExpiration":"Offline session expiration",
|
||||
"oidcRPMetaDataOptionsRefreshToken":"Use refresh tokens",
|
||||
"oidcRPMetaDataOptionsUserIDAttr":"خاصّيّة المستخدم",
|
||||
"oidcRPName":"اسم أوبين أيدي كونيكت RP",
|
||||
"oidcRPStateTimeout":"حالة مهلة الجلسة",
|
||||
|
@ -600,6 +614,10 @@
|
|||
"oidcServicePrivateKeySig":"توقيع على المفتاح الخاص",
|
||||
"oidcServicePublicKeySig":"توقيع على المفتاح العمومي",
|
||||
"oidcServiceKeyIdSig":"توقيع على هوية المفتاح ",
|
||||
"oidcServiceAuthorizationCodeExpiration":"Authorization Code expiration",
|
||||
"oidcServiceAccessTokenExpiration":"انتهاء صلاحية التوكن",
|
||||
"oidcServiceIDTokenExpiration":" انتهاء صلاحية تعريف التوكن",
|
||||
"oidcServiceOfflineSessionExpiration":"Offline session expiration",
|
||||
"oidcStorage":"اسم وحدة الجلسات",
|
||||
"oidcStorageOptions":"خيارات وحدة الجلسات",
|
||||
"oidcOPMetaDataNodes":" أوبين أيدي كونيكت بروفيدر",
|
||||
|
@ -620,6 +638,7 @@
|
|||
"oidcServiceAllowAuthorizationCodeFlow":"ترخيص كود التدفق",
|
||||
"oidcServiceAllowImplicitFlow":"التدفق الضمني",
|
||||
"oidcServiceAllowHybridFlow":"تدفق هجين",
|
||||
"oidcServiceAllowOffline":"Allow offline access",
|
||||
"ok":"حسنا",
|
||||
"oldNotifFormat":"استخدام صيغة xml القديمة",
|
||||
"openIdAttr":"تسجيل الدخول في أوبين أيدي",
|
||||
|
@ -726,6 +745,7 @@
|
|||
"redirectFormMethod":"طريقة إعادة توجيه الإستمارة",
|
||||
"redirection":"معالج إعادة التوجيه",
|
||||
"reference":"مرجع",
|
||||
"refreshSessions":"Refresh sessions API",
|
||||
"regexp":"التعبير النمطي",
|
||||
"regexps":"التعبير النمطي",
|
||||
"register":"تسجيل حساب جديد",
|
||||
|
@ -768,9 +788,9 @@
|
|||
"returnUrl":"إرجاع اليو آر إل",
|
||||
"rp":"Relying Party",
|
||||
"rule":"القاعدة",
|
||||
"ruleAuthnLevel":"Required authentication level",
|
||||
"ruleAuthnLevel":"مستوى إثبات الهوية واجب",
|
||||
"rules":"القواعد",
|
||||
"rulesAuthnLevel":"Required authentication levels",
|
||||
"rulesAuthnLevel":"Required auth levels",
|
||||
"Same":"نفسه",
|
||||
"save":"حفظ",
|
||||
"saveReport":"احفظ التقرير",
|
||||
|
|
|
@ -219,6 +219,9 @@
|
|||
"customModule":"Custom module",
|
||||
"customParams":"Custom module names",
|
||||
"customPassword":"Custom password module",
|
||||
"customPlugins":"Modules list",
|
||||
"customPluginsNode":"Custom plugins",
|
||||
"customPluginsParams":"Additional parameters",
|
||||
"customPortalSkin":"Custom portal skin",
|
||||
"customRegister":"Custom register module",
|
||||
"customToTrace":"REMOTE_CUSTOM",
|
||||
|
@ -312,6 +315,9 @@
|
|||
"forms":"Forms",
|
||||
"friendlyName":"Friendly name",
|
||||
"generalParameters":"General Parameters",
|
||||
"globalLogout":"Global logout",
|
||||
"globalLogoutRule":"Activation",
|
||||
"globalLogoutTimer":"Auto accept time",
|
||||
"globalStorage":"Apache::Session module",
|
||||
"globalStorageOptions":"Apache::Session module parameters",
|
||||
"gpgAuthnLevel":"Authentication level",
|
||||
|
@ -506,6 +512,8 @@
|
|||
"notAValidPerlExpression":"Not a valid Perl expression",
|
||||
"notification":"Activation",
|
||||
"notifications":"Notifications",
|
||||
"notification_s":"notification(s)",
|
||||
"notificationDefaultCond":"Default condition",
|
||||
"notificationServer":"Notification server",
|
||||
"notificationServerDELETE":"DELETE method",
|
||||
"notificationServerGET":"GET method",
|
||||
|
@ -515,8 +523,8 @@
|
|||
"serverNotification":"Server",
|
||||
"notificationCreated":"Notification has been created",
|
||||
"notificationDeleted":"Notification deleted",
|
||||
"notificationDone":"notification done",
|
||||
"notificationsDone":"notifications done",
|
||||
"notificationDone":"Notification done",
|
||||
"notificationsDone":"Notifications done",
|
||||
"notificationNotCreated":"The notification was not created",
|
||||
"notificationNotDeleted":"The notification was not marked as done",
|
||||
"notificationNotFound":"The notification was not found",
|
||||
|
@ -532,6 +540,7 @@
|
|||
"nullParams":"Null parameters",
|
||||
"number":"Number",
|
||||
"off":"Off",
|
||||
"offlineSessions":"Offline sessions",
|
||||
"oldValue":"Old value",
|
||||
"on":"On",
|
||||
"oidcAuthnLevel":"Authentication level",
|
||||
|
@ -543,6 +552,7 @@
|
|||
"oidcOPMetaDataNode":"OpenID Connect Providers",
|
||||
"oidcOPMetaDataOptions":"Optionen",
|
||||
"oidcRPMetaDataOptionsAuthentication":"Authentication",
|
||||
"oidcRPMetaDataOptionsAllowOffline":"Allow offline access",
|
||||
"oidcOPMetaDataOptionsCheckJWTSignature":"Check JWT signature",
|
||||
"oidcOPMetaDataOptionsClientID":"Client ID",
|
||||
"oidcOPMetaDataOptionsClientSecret":"Client secret",
|
||||
|
@ -571,7 +581,8 @@
|
|||
"oidcRPMetaDataExportedVars":"Exported attributes",
|
||||
"oidcRPMetaDataNode":"OpenID Connect Relying Parties",
|
||||
"oidcRPMetaDataOptions":"Options",
|
||||
"oidcRPMetaDataOptionsAccessTokenExpiration":"Access token expiration",
|
||||
"oidcRPMetaDataOptionsAccessTokenExpiration":"Access Token expiration",
|
||||
"oidcRPMetaDataOptionsAuthorizationCodeExpiration":"Authorization Code expiration",
|
||||
"oidcRPMetaDataOptionsBypassConsent":"Bypass consent",
|
||||
"oidcRPMetaDataOptionsClientID":"Client ID",
|
||||
"oidcRPMetaDataOptionsClientSecret":"Client secret",
|
||||
|
@ -580,6 +591,9 @@
|
|||
"oidcRPMetaDataOptionsIcon":"Logo",
|
||||
"oidcRPMetaDataOptionsIDTokenExpiration":"ID Token expiration",
|
||||
"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",
|
||||
"oidcRPName":"OpenID Connect RP Name",
|
||||
"oidcRPStateTimeout":"State session timeout",
|
||||
|
@ -599,6 +613,10 @@
|
|||
"oidcServicePrivateKeySig":"Signing private key",
|
||||
"oidcServicePublicKeySig":"Signing public key",
|
||||
"oidcServiceKeyIdSig":"Signing key ID",
|
||||
"oidcServiceAuthorizationCodeExpiration":"Authorization Code expiration",
|
||||
"oidcServiceAccessTokenExpiration":"Access Token expiration",
|
||||
"oidcServiceIDTokenExpiration":"ID Token expiration",
|
||||
"oidcServiceOfflineSessionExpiration":"Offline session expiration",
|
||||
"oidcStorage":"Sessions module name",
|
||||
"oidcStorageOptions":"Sessions module options",
|
||||
"oidcOPMetaDataNodes":"OpenID Connect Providers",
|
||||
|
@ -619,6 +637,7 @@
|
|||
"oidcServiceAllowAuthorizationCodeFlow":"Authorization Code Flow",
|
||||
"oidcServiceAllowImplicitFlow":"Implicit Flow",
|
||||
"oidcServiceAllowHybridFlow":"Hybrid Flow",
|
||||
"oidcServiceAllowOffline":"Allow offline access",
|
||||
"ok":"OK",
|
||||
"oldNotifFormat":"Use old XML format",
|
||||
"openIdAttr":"OpenID login",
|
||||
|
@ -725,6 +744,7 @@
|
|||
"redirectFormMethod":"Method for redirect form",
|
||||
"redirection":"Handler redirections",
|
||||
"reference":"Reference",
|
||||
"refreshSessions":"Refresh sessions API",
|
||||
"regexp":"Regular expression",
|
||||
"regexps":"Regular expressions",
|
||||
"register":"Register new account",
|
||||
|
@ -769,7 +789,7 @@
|
|||
"rule":"Rule",
|
||||
"ruleAuthnLevel":"Required authentication level",
|
||||
"rules":"Regeln",
|
||||
"rulesAuthnLevel":"Required authentication levels",
|
||||
"rulesAuthnLevel":"Required auth levels",
|
||||
"Same":"Same",
|
||||
"save":"Save",
|
||||
"saveReport":"Save report",
|
||||
|
|
|
@ -219,6 +219,9 @@
|
|||
"customModule":"Custom module",
|
||||
"customParams":"Custom module names",
|
||||
"customPassword":"Custom password module",
|
||||
"customPlugins":"Modules list",
|
||||
"customPluginsNode":"Custom plugins",
|
||||
"customPluginsParams":"Additional parameters",
|
||||
"customPortalSkin":"Custom portal skin",
|
||||
"customRegister":"Custom register module",
|
||||
"customToTrace":"REMOTE_CUSTOM",
|
||||
|
@ -312,6 +315,9 @@
|
|||
"forms":"Forms",
|
||||
"friendlyName":"Friendly name",
|
||||
"generalParameters":"General Parameters",
|
||||
"globalLogout":"Global logout",
|
||||
"globalLogoutRule":"Activation",
|
||||
"globalLogoutTimer":"Auto accept time",
|
||||
"globalStorage":"Apache::Session module",
|
||||
"globalStorageOptions":"Apache::Session module parameters",
|
||||
"gpgAuthnLevel":"Authentication level",
|
||||
|
@ -506,6 +512,8 @@
|
|||
"notAValidPerlExpression":"Not a valid Perl expression",
|
||||
"notification":"Activation",
|
||||
"notifications":"Notifications",
|
||||
"notification_s":"notification(s)",
|
||||
"notificationDefaultCond":"Default condition",
|
||||
"notificationServer":"Notification server",
|
||||
"notificationServerDELETE":"DELETE method",
|
||||
"notificationServerGET":"GET method",
|
||||
|
@ -515,8 +523,8 @@
|
|||
"serverNotification":"Server",
|
||||
"notificationCreated":"Notification has been created",
|
||||
"notificationDeleted":"Notification deleted",
|
||||
"notificationDone":"notification done",
|
||||
"notificationsDone":"notifications done",
|
||||
"notificationDone":"Notification done",
|
||||
"notificationsDone":"Notifications done",
|
||||
"notificationNotCreated":"The notification was not created",
|
||||
"notificationNotDeleted":"The notification was not marked as done",
|
||||
"notificationNotFound":"The notification was not found",
|
||||
|
@ -532,6 +540,7 @@
|
|||
"nullParams":"Null parameters",
|
||||
"number":"Number",
|
||||
"off":"Off",
|
||||
"offlineSessions":"Offline sessions",
|
||||
"oldValue":"Old value",
|
||||
"on":"On",
|
||||
"oidcAuthnLevel":"Authentication level",
|
||||
|
@ -543,6 +552,7 @@
|
|||
"oidcOPMetaDataNode":"OpenID Connect Providers",
|
||||
"oidcOPMetaDataOptions":"Options",
|
||||
"oidcRPMetaDataOptionsAuthentication":"Authentication",
|
||||
"oidcRPMetaDataOptionsAllowOffline":"Allow offline access",
|
||||
"oidcOPMetaDataOptionsCheckJWTSignature":"Check JWT signature",
|
||||
"oidcOPMetaDataOptionsClientID":"Client ID",
|
||||
"oidcOPMetaDataOptionsClientSecret":"Client secret",
|
||||
|
@ -571,7 +581,8 @@
|
|||
"oidcRPMetaDataExportedVars":"Exported attributes",
|
||||
"oidcRPMetaDataNode":"OpenID Connect Relying Parties",
|
||||
"oidcRPMetaDataOptions":"Options",
|
||||
"oidcRPMetaDataOptionsAccessTokenExpiration":"Access token expiration",
|
||||
"oidcRPMetaDataOptionsAccessTokenExpiration":"Access Token expiration",
|
||||
"oidcRPMetaDataOptionsAuthorizationCodeExpiration":"Authorization Code expiration",
|
||||
"oidcRPMetaDataOptionsBypassConsent":"Bypass consent",
|
||||
"oidcRPMetaDataOptionsClientID":"Client ID",
|
||||
"oidcRPMetaDataOptionsClientSecret":"Client secret",
|
||||
|
@ -580,6 +591,9 @@
|
|||
"oidcRPMetaDataOptionsIcon":"Logo",
|
||||
"oidcRPMetaDataOptionsIDTokenExpiration":"ID Token expiration",
|
||||
"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",
|
||||
"oidcRPName":"OpenID Connect RP Name",
|
||||
"oidcRPStateTimeout":"State session timeout",
|
||||
|
@ -599,6 +613,10 @@
|
|||
"oidcServicePrivateKeySig":"Signing private key",
|
||||
"oidcServicePublicKeySig":"Signing public key",
|
||||
"oidcServiceKeyIdSig":"Signing key ID",
|
||||
"oidcServiceAuthorizationCodeExpiration":"Authorization Code expiration",
|
||||
"oidcServiceAccessTokenExpiration":"Access Token expiration",
|
||||
"oidcServiceIDTokenExpiration":"ID Token expiration",
|
||||
"oidcServiceOfflineSessionExpiration":"Offline session expiration",
|
||||
"oidcStorage":"Sessions module name",
|
||||
"oidcStorageOptions":"Sessions module options",
|
||||
"oidcOPMetaDataNodes":"OpenID Connect Providers",
|
||||
|
@ -619,6 +637,7 @@
|
|||
"oidcServiceAllowAuthorizationCodeFlow":"Authorization Code Flow",
|
||||
"oidcServiceAllowImplicitFlow":"Implicit Flow",
|
||||
"oidcServiceAllowHybridFlow":"Hybrid Flow",
|
||||
"oidcServiceAllowOffline":"Allow offline access",
|
||||
"ok":"OK",
|
||||
"oldNotifFormat":"Use old XML format",
|
||||
"openIdAttr":"OpenID login",
|
||||
|
@ -725,6 +744,7 @@
|
|||
"redirectFormMethod":"Method for redirect form",
|
||||
"redirection":"Handler redirections",
|
||||
"reference":"Reference",
|
||||
"refreshSessions":"Refresh sessions API",
|
||||
"regexp":"Regular expression",
|
||||
"regexps":"Regular expressions",
|
||||
"register":"Register new account",
|
||||
|
@ -769,7 +789,7 @@
|
|||
"rule":"Rule",
|
||||
"ruleAuthnLevel":"Required authentication level",
|
||||
"rules":"Rules",
|
||||
"rulesAuthnLevel":"Required authentication levels",
|
||||
"rulesAuthnLevel":"Required auth levels",
|
||||
"Same":"Same",
|
||||
"save":"Save",
|
||||
"saveReport":"Save report",
|
||||
|
|
|
@ -219,6 +219,9 @@
|
|||
"customModule":"Module personnalisé",
|
||||
"customParams":"Nom des modules personnalisés",
|
||||
"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",
|
||||
"customRegister":"Module d'enregistrement personnalisé",
|
||||
"customToTrace":"REMOTE_CUSTOM",
|
||||
|
@ -312,6 +315,9 @@
|
|||
"forms":"Formulaires",
|
||||
"friendlyName":"Nom alternatif",
|
||||
"generalParameters":"Paramètres généraux",
|
||||
"globalLogout":"Déconnexion globale",
|
||||
"globalLogoutRule":"Activation",
|
||||
"globalLogoutTimer":"Délai d'acceptation automatique",
|
||||
"globalStorage":"Module Apache::Session",
|
||||
"globalStorageOptions":"Paramètres du module Apache::Session",
|
||||
"gpgAuthnLevel":"Niveau d'authentification",
|
||||
|
@ -506,6 +512,8 @@
|
|||
"notAValidPerlExpression":"Pas une expression Perl valide",
|
||||
"notification":"Activation",
|
||||
"notifications":"Notifications",
|
||||
"notification_s":"notification(s)",
|
||||
"notificationDefaultCond":"Condition par défaut",
|
||||
"notificationServer":"Serveur de notifications",
|
||||
"notificationServerDELETE":"Méthode DELETE",
|
||||
"notificationServerGET":"Méthode GET",
|
||||
|
@ -515,8 +523,8 @@
|
|||
"serverNotification":"Serveur",
|
||||
"notificationCreated":"La notification a été créée",
|
||||
"notificationDeleted":"La notification a été marquée comme lue",
|
||||
"notificationDone":"notification validée",
|
||||
"notificationsDone":"notifications validées",
|
||||
"notificationDone":"Notification validée",
|
||||
"notificationsDone":"Notifications validées",
|
||||
"notificationNotCreated":"La notification n'a pas été créée",
|
||||
"notificationNotDeleted":"La notification n'a pas été marquée comme lue",
|
||||
"notificationNotFound":"La notification n'a pas été trouvée",
|
||||
|
@ -532,6 +540,7 @@
|
|||
"nullParams":"Paramètres Null",
|
||||
"number":"Numéro",
|
||||
"off":"Désactivé",
|
||||
"offlineSessions":"Sessions hors ligne",
|
||||
"oldValue":"Ancienne valeur",
|
||||
"on":"Activé",
|
||||
"oidcAuthnLevel":"Niveau d'authentification",
|
||||
|
@ -543,6 +552,7 @@
|
|||
"oidcOPMetaDataNode":"Fournisseurs OpenID Connect",
|
||||
"oidcOPMetaDataOptions":"Options",
|
||||
"oidcRPMetaDataOptionsAuthentication":"Authentification",
|
||||
"oidcRPMetaDataOptionsAllowOffline":"Autoriser l'accès hors ligne",
|
||||
"oidcOPMetaDataOptionsCheckJWTSignature":"Vérifier la signature des jetons",
|
||||
"oidcOPMetaDataOptionsClientID":"Identifiant",
|
||||
"oidcOPMetaDataOptionsClientSecret":"Mot de passe",
|
||||
|
@ -572,6 +582,7 @@
|
|||
"oidcRPMetaDataNode":"Clients OpenID Connect",
|
||||
"oidcRPMetaDataOptions":"Options",
|
||||
"oidcRPMetaDataOptionsAccessTokenExpiration":"Expiration des jetons d'accès",
|
||||
"oidcRPMetaDataOptionsAuthorizationCodeExpiration":"Expiration des codes d'autorisation",
|
||||
"oidcRPMetaDataOptionsBypassConsent":"Contourner le consentement",
|
||||
"oidcRPMetaDataOptionsClientID":"Identifiant",
|
||||
"oidcRPMetaDataOptionsClientSecret":"Mot de passe",
|
||||
|
@ -580,6 +591,9 @@
|
|||
"oidcRPMetaDataOptionsIcon":"Logo",
|
||||
"oidcRPMetaDataOptionsIDTokenExpiration":"Expiration 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",
|
||||
"oidcRPName":"Nom du client OpenID Connect",
|
||||
"oidcRPStateTimeout":"Durée d'une session state",
|
||||
|
@ -599,6 +613,10 @@
|
|||
"oidcServicePrivateKeySig":"Clef privée de signature",
|
||||
"oidcServicePublicKeySig":"Clef publique 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",
|
||||
"oidcStorageOptions":"Options du module des sessions",
|
||||
"oidcOPMetaDataNodes":"Fournisseurs OpenID Connect",
|
||||
|
@ -619,6 +637,7 @@
|
|||
"oidcServiceAllowAuthorizationCodeFlow":"Authorization Code Flow",
|
||||
"oidcServiceAllowImplicitFlow":"Implicit Flow",
|
||||
"oidcServiceAllowHybridFlow":"Hybrid Flow",
|
||||
"oidcServiceAllowOffline":"Autoriser l'accès hors ligne",
|
||||
"ok":"OK",
|
||||
"oldNotifFormat":"Utiliser l'ancien format XML",
|
||||
"openIdAttr":"Identifiant OpenID",
|
||||
|
@ -725,6 +744,7 @@
|
|||
"redirectFormMethod":"Méthode du formulaire de redirection",
|
||||
"redirection":"Redirections du Handler",
|
||||
"reference":"Référence",
|
||||
"refreshSessions":"API de rafraîchissement des sessions",
|
||||
"regexp":"Expression régulière",
|
||||
"regexps":"Expressions régulières",
|
||||
"register":"Créer un nouveau compte",
|
||||
|
@ -769,7 +789,7 @@
|
|||
"rule":"Règle",
|
||||
"ruleAuthnLevel":"Niveau d'authentication requis",
|
||||
"rules":"Règles",
|
||||
"rulesAuthnLevel":"Niveaux d'authentification requis",
|
||||
"rulesAuthnLevel":"Niveaux auth requis",
|
||||
"Same":"Identique",
|
||||
"save":"Sauver",
|
||||
"saveReport":"Rapport de sauvegarde",
|
||||
|
|
|
@ -219,6 +219,9 @@
|
|||
"customModule":"Personalizza modulo",
|
||||
"customParams":"Personalizza i nomi dei moduli",
|
||||
"customPassword":"Personalizza il modulo password",
|
||||
"customPlugins":"Modules list",
|
||||
"customPluginsNode":"Custom plugins",
|
||||
"customPluginsParams":"Additional parameters",
|
||||
"customPortalSkin":"Personalizza faccia del portale ",
|
||||
"customRegister":"Personalizza modulo di registro",
|
||||
"customToTrace":"REMOTE_CUSTOM",
|
||||
|
@ -246,7 +249,7 @@
|
|||
"dbiUserTable":"Tabella utente",
|
||||
"decryptValue":"Decrypt value",
|
||||
"decryptValueFunctions":"Decrypt functions",
|
||||
"decryptValueRule":"Use rule",
|
||||
"decryptValueRule":"Utilizza la regola",
|
||||
"default":"Predefinito",
|
||||
"defaultRule":"Regola predefinita",
|
||||
"demoModeOn":"Questo gestore viene eseguito in modalità demo",
|
||||
|
@ -312,6 +315,9 @@
|
|||
"forms":"Moduli",
|
||||
"friendlyName":"Nome amichevole",
|
||||
"generalParameters":"Parametri generali",
|
||||
"globalLogout":"Global logout",
|
||||
"globalLogoutRule":"Activation",
|
||||
"globalLogoutTimer":"Auto accettazione tempo",
|
||||
"globalStorage":"Modulo Apache::Session",
|
||||
"globalStorageOptions":"Parametri di modulo Apache::Session",
|
||||
"gpgAuthnLevel":"Livello di autenticazione",
|
||||
|
@ -429,7 +435,7 @@
|
|||
"loadFromUrl":"Carica a partire dall'URL",
|
||||
"localSessionStorage":"Modulo cache",
|
||||
"localSessionStorageOptions":"Opzioni modulo cache",
|
||||
"locationRules":"Regola di accesso",
|
||||
"locationRules":"Regole di accesso",
|
||||
"loginHistory":"Cronologia dei login",
|
||||
"loginHistoryEnabled":"Attivazione",
|
||||
"logo":"Logo",
|
||||
|
@ -506,6 +512,8 @@
|
|||
"notAValidPerlExpression":"Non una valida espressione Perl",
|
||||
"notification":"Attivazione",
|
||||
"notifications":"Notifiche",
|
||||
"notification_s":"notification(s)",
|
||||
"notificationDefaultCond":"Default condition",
|
||||
"notificationServer":"Server di notifica",
|
||||
"notificationServerDELETE":"DELETE method",
|
||||
"notificationServerGET":"GET method",
|
||||
|
@ -532,6 +540,7 @@
|
|||
"nullParams":"Parametri Null",
|
||||
"number":"Numero",
|
||||
"off":"Off",
|
||||
"offlineSessions":"Offline sessions",
|
||||
"oldValue":"Vecchio valore",
|
||||
"on":"On",
|
||||
"oidcAuthnLevel":"Livello di autenticazione",
|
||||
|
@ -543,6 +552,7 @@
|
|||
"oidcOPMetaDataNode":"Provider di OpenID Connect",
|
||||
"oidcOPMetaDataOptions":"Opzioni",
|
||||
"oidcRPMetaDataOptionsAuthentication":"Autenticazione",
|
||||
"oidcRPMetaDataOptionsAllowOffline":"Allow offline access",
|
||||
"oidcOPMetaDataOptionsCheckJWTSignature":"Controllare la firma JWT",
|
||||
"oidcOPMetaDataOptionsClientID":"ID Client",
|
||||
"oidcOPMetaDataOptionsClientSecret":"Segreto Client",
|
||||
|
@ -572,6 +582,7 @@
|
|||
"oidcRPMetaDataNode":"Parti basate su OpenID Connect",
|
||||
"oidcRPMetaDataOptions":"Opzioni",
|
||||
"oidcRPMetaDataOptionsAccessTokenExpiration":"Scadenza accesso token",
|
||||
"oidcRPMetaDataOptionsAuthorizationCodeExpiration":"Authorization Code expiration",
|
||||
"oidcRPMetaDataOptionsBypassConsent":"Consenso di bypass",
|
||||
"oidcRPMetaDataOptionsClientID":"ID Client",
|
||||
"oidcRPMetaDataOptionsClientSecret":"Segreto Client",
|
||||
|
@ -580,6 +591,9 @@
|
|||
"oidcRPMetaDataOptionsIcon":"Logo",
|
||||
"oidcRPMetaDataOptionsIDTokenExpiration":"Scadenza ID 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",
|
||||
"oidcRPName":"Nome di OpenID Connect RP",
|
||||
"oidcRPStateTimeout":"Durata della sessione stato",
|
||||
|
@ -599,6 +613,10 @@
|
|||
"oidcServicePrivateKeySig":"Firma della chiave privata",
|
||||
"oidcServicePublicKeySig":"Firma della chiave pubblica",
|
||||
"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",
|
||||
"oidcStorageOptions":"Opzioni del modulo Sessioni",
|
||||
"oidcOPMetaDataNodes":"Provider di OpenID Connect",
|
||||
|
@ -619,6 +637,7 @@
|
|||
"oidcServiceAllowAuthorizationCodeFlow":"Flusso del codice di autorizzazione",
|
||||
"oidcServiceAllowImplicitFlow":"Flusso implicito",
|
||||
"oidcServiceAllowHybridFlow":"Flusso ibrido",
|
||||
"oidcServiceAllowOffline":"Allow offline access",
|
||||
"ok":"OK",
|
||||
"oldNotifFormat":"Utilizza il vecchio formato XML",
|
||||
"openIdAttr":"Login OpenID",
|
||||
|
@ -707,12 +726,12 @@
|
|||
"proxyUseSoap":"Usa SOAP invece di REST",
|
||||
"publicKey":"Chiave pubblica",
|
||||
"purgeNotification":"Elimina definitivamente la notifica",
|
||||
"radius2f":"Radius second factor",
|
||||
"radius2f":"Radius secondo fattore",
|
||||
"radius2fActivation":"Attivazione",
|
||||
"radius2fServer":"Nome host del server",
|
||||
"radius2fSecret":"Segreto condiviso",
|
||||
"radius2fUsernameSessionKey":"Session key containing login",
|
||||
"radius2fTimeout":"Authentication timeout",
|
||||
"radius2fTimeout":"Timeout di autenticazione",
|
||||
"radius2fAuthnLevel":"Livello di autenticazione",
|
||||
"radius2fLogo":"Logo",
|
||||
"radius2fLabel":"Label",
|
||||
|
@ -725,6 +744,7 @@
|
|||
"redirectFormMethod":"Metodo per il modulo di reindirizzamento",
|
||||
"redirection":"Redirezioni del gestore",
|
||||
"reference":"Riferimento",
|
||||
"refreshSessions":"Refresh sessions API",
|
||||
"regexp":"Espressione regolare",
|
||||
"regexps":"Espressioni regolari",
|
||||
"register":"Registra nuovo account",
|
||||
|
@ -767,9 +787,9 @@
|
|||
"returnUrl":"URL di ritorno",
|
||||
"rp":"Parte facente affidamento",
|
||||
"rule":"Regola",
|
||||
"ruleAuthnLevel":"Required authentication level",
|
||||
"ruleAuthnLevel":"Livello di autenticazione richiesto",
|
||||
"rules":"Regole",
|
||||
"rulesAuthnLevel":"Required authentication levels",
|
||||
"rulesAuthnLevel":"Required auth levels",
|
||||
"Same":"Stesso",
|
||||
"save":"Salva",
|
||||
"saveReport":"Salva report",
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -219,6 +219,9 @@
|
|||
"customModule":"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",
|
||||
"customPlugins":"Modules list",
|
||||
"customPluginsNode":"Custom plugins",
|
||||
"customPluginsParams":"Additional parameters",
|
||||
"customPortalSkin":"Tùy chỉnh giao diện cổng thông tin",
|
||||
"customRegister":"Module đăng ký tùy chỉnh",
|
||||
"customToTrace":"REMOTE_CUSTOM",
|
||||
|
@ -312,6 +315,9 @@
|
|||
"forms":"Biểu mẫu",
|
||||
"friendlyName":"Tên thân thiện",
|
||||
"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",
|
||||
"globalStorageOptions":"Tham số mô đun Apache :: Session ",
|
||||
"gpgAuthnLevel":"Mức xác thực",
|
||||
|
@ -429,7 +435,7 @@
|
|||
"loadFromUrl":"Nạp từ URL",
|
||||
"localSessionStorage":"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",
|
||||
"loginHistoryEnabled":"Kích hoạt",
|
||||
"logo":"Logo",
|
||||
|
@ -506,6 +512,8 @@
|
|||
"notAValidPerlExpression":"Không phải là một biểu thức Perl hợp lệ",
|
||||
"notification":"Kích hoạt",
|
||||
"notifications":"Thông báo",
|
||||
"notification_s":"notification(s)",
|
||||
"notificationDefaultCond":"Default condition",
|
||||
"notificationServer":"Máy chủ Thông báo",
|
||||
"notificationServerDELETE":"DELETE method",
|
||||
"notificationServerGET":"GET method",
|
||||
|
@ -515,8 +523,8 @@
|
|||
"serverNotification":"Server",
|
||||
"notificationCreated":"Thông báo đã được tạo ra",
|
||||
"notificationDeleted":"Thông báo đã xoá",
|
||||
"notificationDone":"thông báo được thực hiện",
|
||||
"notificationsDone":"thông báo đã hoàn tất",
|
||||
"notificationDone":"Thông báo được thực hiện",
|
||||
"notificationsDone":"Thông báo đã hoàn tất",
|
||||
"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",
|
||||
"notificationNotFound":"Không tìm thấy thông báo",
|
||||
|
@ -532,6 +540,7 @@
|
|||
"nullParams":"Tham số Null",
|
||||
"number":"Số",
|
||||
"off":"Tắt",
|
||||
"offlineSessions":"Offline sessions",
|
||||
"oldValue":"Giá trị cũ",
|
||||
"on":"Vào",
|
||||
"oidcAuthnLevel":"Mức xác thực",
|
||||
|
@ -543,6 +552,7 @@
|
|||
"oidcOPMetaDataNode":"Nhà cung cấp Kết nối OpenID",
|
||||
"oidcOPMetaDataOptions":"Tùy chọn",
|
||||
"oidcRPMetaDataOptionsAuthentication":"Xác thực",
|
||||
"oidcRPMetaDataOptionsAllowOffline":"Allow offline access",
|
||||
"oidcOPMetaDataOptionsCheckJWTSignature":"Kiểm tra chữ ký JWT",
|
||||
"oidcOPMetaDataOptionsClientID":"Client ID",
|
||||
"oidcOPMetaDataOptionsClientSecret":"Trình khách bí mật",
|
||||
|
@ -571,7 +581,8 @@
|
|||
"oidcRPMetaDataExportedVars":"Biến đã được xuất",
|
||||
"oidcRPMetaDataNode":"OpenID Connect Relying Parties",
|
||||
"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 ý",
|
||||
"oidcRPMetaDataOptionsClientID":"Client ID",
|
||||
"oidcRPMetaDataOptionsClientSecret":"Trình khách bí mật",
|
||||
|
@ -580,6 +591,9 @@
|
|||
"oidcRPMetaDataOptionsIcon":"Logo",
|
||||
"oidcRPMetaDataOptionsIDTokenExpiration":"ID Token hết hạn",
|
||||
"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",
|
||||
"oidcRPName":"OpenID Connect RP Name",
|
||||
"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",
|
||||
"oidcServicePublicKeySig":"Ký khóa công khai",
|
||||
"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",
|
||||
"oidcStorageOptions":"Tùy chọn mô-đun phiên",
|
||||
"oidcOPMetaDataNodes":"Nhà cung cấp Kết nối OpenID",
|
||||
|
@ -619,6 +637,7 @@
|
|||
"oidcServiceAllowAuthorizationCodeFlow":"Dòng mã ủy quyền",
|
||||
"oidcServiceAllowImplicitFlow":"Dòng chảy ngầm",
|
||||
"oidcServiceAllowHybridFlow":"Dòng chảy hỗn hợp",
|
||||
"oidcServiceAllowOffline":"Allow offline access",
|
||||
"ok":"OK",
|
||||
"oldNotifFormat":"Sử dụng định dạng XML cũ",
|
||||
"openIdAttr":"Đăng nhập OpenID",
|
||||
|
@ -725,6 +744,7 @@
|
|||
"redirectFormMethod":"Phương pháp chuyển hướng mẫu",
|
||||
"redirection":"chuyển hướng trình điều khiển",
|
||||
"reference":"Tham khảo",
|
||||
"refreshSessions":"Refresh sessions API",
|
||||
"regexp":"Biểu thức chính quy",
|
||||
"regexps":"Biểu thức thông thường",
|
||||
"register":"Đăng ký tài khoản mới",
|
||||
|
@ -769,7 +789,7 @@
|
|||
"rule":"Quy tắc",
|
||||
"ruleAuthnLevel":"Required authentication level",
|
||||
"rules":"Quy tắc",
|
||||
"rulesAuthnLevel":"Required authentication levels",
|
||||
"rulesAuthnLevel":"Required auth levels",
|
||||
"Same":"Tương tự",
|
||||
"save":"Lưu",
|
||||
"saveReport":"Lưu báo cáo",
|
||||
|
|
|
@ -219,6 +219,9 @@
|
|||
"customModule":"定制模块",
|
||||
"customParams":"定制模块名称",
|
||||
"customPassword":"Custom password module",
|
||||
"customPlugins":"Modules list",
|
||||
"customPluginsNode":"Custom plugins",
|
||||
"customPluginsParams":"Additional parameters",
|
||||
"customPortalSkin":"Custom portal skin",
|
||||
"customRegister":"Custom register module",
|
||||
"customToTrace":"REMOTE_CUSTOM",
|
||||
|
@ -312,6 +315,9 @@
|
|||
"forms":"Forms",
|
||||
"friendlyName":"Friendly name",
|
||||
"generalParameters":"通用参数",
|
||||
"globalLogout":"Global logout",
|
||||
"globalLogoutRule":"Activation",
|
||||
"globalLogoutTimer":"自动接收时间",
|
||||
"globalStorage":"Apache::Session 模块",
|
||||
"globalStorageOptions":"Apache::Session module parameters",
|
||||
"gpgAuthnLevel":"认证等级",
|
||||
|
@ -506,6 +512,8 @@
|
|||
"notAValidPerlExpression":"Not a valid Perl expression",
|
||||
"notification":"激活",
|
||||
"notifications":"Notifications",
|
||||
"notification_s":"notification(s)",
|
||||
"notificationDefaultCond":"Default condition",
|
||||
"notificationServer":"Notification server",
|
||||
"notificationServerDELETE":"DELETE method",
|
||||
"notificationServerGET":"GET method",
|
||||
|
@ -515,8 +523,8 @@
|
|||
"serverNotification":"Server",
|
||||
"notificationCreated":"Notification has been created",
|
||||
"notificationDeleted":"Notification deleted",
|
||||
"notificationDone":"notification done",
|
||||
"notificationsDone":"notifications done",
|
||||
"notificationDone":"Notification done",
|
||||
"notificationsDone":"Notifications done",
|
||||
"notificationNotCreated":"The notification was not created",
|
||||
"notificationNotDeleted":"The notification was not marked as done",
|
||||
"notificationNotFound":"The notification was not found",
|
||||
|
@ -532,6 +540,7 @@
|
|||
"nullParams":"Null parameters",
|
||||
"number":"Number",
|
||||
"off":"Off",
|
||||
"offlineSessions":"Offline sessions",
|
||||
"oldValue":"Old value",
|
||||
"on":"On",
|
||||
"oidcAuthnLevel":"认证等级",
|
||||
|
@ -543,6 +552,7 @@
|
|||
"oidcOPMetaDataNode":"OpenID Connect Providers",
|
||||
"oidcOPMetaDataOptions":"Options",
|
||||
"oidcRPMetaDataOptionsAuthentication":"Authentication",
|
||||
"oidcRPMetaDataOptionsAllowOffline":"Allow offline access",
|
||||
"oidcOPMetaDataOptionsCheckJWTSignature":"Check JWT signature",
|
||||
"oidcOPMetaDataOptionsClientID":"Client ID",
|
||||
"oidcOPMetaDataOptionsClientSecret":"Client secret",
|
||||
|
@ -571,7 +581,8 @@
|
|||
"oidcRPMetaDataExportedVars":"Exported attributes",
|
||||
"oidcRPMetaDataNode":"OpenID Connect Relying Parties",
|
||||
"oidcRPMetaDataOptions":"Options",
|
||||
"oidcRPMetaDataOptionsAccessTokenExpiration":"Access token expiration",
|
||||
"oidcRPMetaDataOptionsAccessTokenExpiration":"Access Token expiration",
|
||||
"oidcRPMetaDataOptionsAuthorizationCodeExpiration":"Authorization Code expiration",
|
||||
"oidcRPMetaDataOptionsBypassConsent":"Bypass consent",
|
||||
"oidcRPMetaDataOptionsClientID":"Client ID",
|
||||
"oidcRPMetaDataOptionsClientSecret":"Client secret",
|
||||
|
@ -580,6 +591,9 @@
|
|||
"oidcRPMetaDataOptionsIcon":"Logo",
|
||||
"oidcRPMetaDataOptionsIDTokenExpiration":"ID Token expiration",
|
||||
"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",
|
||||
"oidcRPName":"OpenID Connect RP Name",
|
||||
"oidcRPStateTimeout":"State session timeout",
|
||||
|
@ -599,6 +613,10 @@
|
|||
"oidcServicePrivateKeySig":"Signing private key",
|
||||
"oidcServicePublicKeySig":"Signing public key",
|
||||
"oidcServiceKeyIdSig":"Signing key ID",
|
||||
"oidcServiceAuthorizationCodeExpiration":"Authorization Code expiration",
|
||||
"oidcServiceAccessTokenExpiration":"Access Token expiration",
|
||||
"oidcServiceIDTokenExpiration":"ID Token expiration",
|
||||
"oidcServiceOfflineSessionExpiration":"Offline session expiration",
|
||||
"oidcStorage":"Sessions module name",
|
||||
"oidcStorageOptions":"Sessions module options",
|
||||
"oidcOPMetaDataNodes":"OpenID Connect Providers",
|
||||
|
@ -619,6 +637,7 @@
|
|||
"oidcServiceAllowAuthorizationCodeFlow":"Authorization Code Flow",
|
||||
"oidcServiceAllowImplicitFlow":"Implicit Flow",
|
||||
"oidcServiceAllowHybridFlow":"Hybrid Flow",
|
||||
"oidcServiceAllowOffline":"Allow offline access",
|
||||
"ok":"OK",
|
||||
"oldNotifFormat":"Use old XML format",
|
||||
"openIdAttr":"OpenID login",
|
||||
|
@ -725,6 +744,7 @@
|
|||
"redirectFormMethod":"Method for redirect form",
|
||||
"redirection":"Handler redirections",
|
||||
"reference":"Reference",
|
||||
"refreshSessions":"Refresh sessions API",
|
||||
"regexp":"Regular expression",
|
||||
"regexps":"Regular expressions",
|
||||
"register":"Register new account",
|
||||
|
@ -769,7 +789,7 @@
|
|||
"rule":"Rule",
|
||||
"ruleAuthnLevel":"Required authentication level",
|
||||
"rules":"Rules",
|
||||
"rulesAuthnLevel":"Required authentication levels",
|
||||
"rulesAuthnLevel":"Required auth levels",
|
||||
"Same":"Same",
|
||||
"save":"Save",
|
||||
"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
|
@ -21,6 +21,7 @@
|
|||
</ul>
|
||||
</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">
|
||||
<section id="block-superfish-1" class="block block-superfish clearfix">
|
||||
<div ui-tree data-drag-enabled="false" id="tree-root">
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
</ul>
|
||||
</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>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -35,7 +35,7 @@ foreach my $lang (@langs) {
|
|||
my @l1 = sort keys %$keys;
|
||||
my @l2 = sort keys %$l;
|
||||
ok( $#l1 == $#l2,
|
||||
"'$lang' and 'en' have the same count (" . @l1 . '/' . @l2 . ")" );
|
||||
"'$lang' and 'en' have the same count (" . @l2 . '/' . @l1 . ")" );
|
||||
|
||||
my @unTr;
|
||||
while (@l1) {
|
||||
|
|
|
@ -109,12 +109,14 @@ lib/Lemonldap/NG/Portal/Plugins/ContextSwitching.pm
|
|||
lib/Lemonldap/NG/Portal/Plugins/DecryptValue.pm
|
||||
lib/Lemonldap/NG/Portal/Plugins/FavApps.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/History.pm
|
||||
lib/Lemonldap/NG/Portal/Plugins/Impersonation.pm
|
||||
lib/Lemonldap/NG/Portal/Plugins/MailPasswordReset.pm
|
||||
lib/Lemonldap/NG/Portal/Plugins/Notifications.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/RESTServer.pm
|
||||
lib/Lemonldap/NG/Portal/Plugins/SingleSession.pm
|
||||
|
@ -154,8 +156,10 @@ README
|
|||
scripts/llngDeleteSession
|
||||
site/coffee/2fregistration.coffee
|
||||
site/coffee/autoRenew.coffee
|
||||
site/coffee/captcha.coffee
|
||||
site/coffee/confirm.coffee
|
||||
site/coffee/favapps.coffee
|
||||
site/coffee/globalLogout.coffee
|
||||
site/coffee/idpchoice.coffee
|
||||
site/coffee/info.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.min.js
|
||||
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.min.js
|
||||
site/htdocs/static/common/js/confirm.min.js.map
|
||||
site/htdocs/static/common/js/favapps.js
|
||||
site/htdocs/static/common/js/favapps.min.js
|
||||
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.min.js
|
||||
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/pt.json
|
||||
site/htdocs/static/languages/ro.json
|
||||
site/htdocs/static/languages/tr.json
|
||||
site/htdocs/static/languages/vi.json
|
||||
site/htdocs/static/languages/zh.json
|
||||
site/templates/bootstrap/2fchoice.tpl
|
||||
site/templates/bootstrap/2fregisters.tpl
|
||||
site/templates/bootstrap/captcha.tpl
|
||||
site/templates/bootstrap/casBack2Url.tpl
|
||||
site/templates/bootstrap/certificateReset.tpl
|
||||
site/templates/bootstrap/checklogins.tpl
|
||||
|
@ -383,6 +395,7 @@ site/templates/bootstrap/decryptvalue.tpl
|
|||
site/templates/bootstrap/error.tpl
|
||||
site/templates/bootstrap/ext2fcheck.tpl
|
||||
site/templates/bootstrap/footer.tpl
|
||||
site/templates/bootstrap/globallogout.tpl
|
||||
site/templates/bootstrap/gpgform.tpl
|
||||
site/templates/bootstrap/header.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/it.json
|
||||
site/templates/common/mail/ms.json
|
||||
site/templates/common/mail/tr.json
|
||||
site/templates/common/mail/vi.json
|
||||
site/templates/common/mail/zh_CN.json
|
||||
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-sorted.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-Token-Introspection.t
|
||||
t/32-OIDC-Token-Security.t
|
||||
|
@ -575,6 +591,8 @@ t/43-MailPasswordReset-with-token.t
|
|||
t/43-MailPasswordReset.t
|
||||
t/44-CertificateResetByMail-LDAP.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-internal-function.t
|
||||
t/59-Double-cookies-for-a-Single-session.t
|
||||
|
@ -587,6 +605,7 @@ t/61-ForceAuthn.t
|
|||
t/61-GrantSession.t
|
||||
t/61-Session-ActivityTimeout.t
|
||||
t/61-Session-Timeout.t
|
||||
t/62-Refresh-plugin.t
|
||||
t/62-SingleSession.t
|
||||
t/62-UpgradeSession.t
|
||||
t/63-History.t
|
||||
|
@ -667,4 +686,5 @@ t/test-psgi.pm
|
|||
t/testslapd/confs-sessions.ldif
|
||||
t/testslapd/slapd.ldif
|
||||
t/testslapd/users.ldif
|
||||
t/Time-Fake.pm
|
||||
t/vrfyOTP.pl
|
||||
|
|
|
@ -3,7 +3,7 @@ package Lemonldap::NG::Portal::2F::Ext2F;
|
|||
use strict;
|
||||
use Mouse;
|
||||
use Lemonldap::NG::Portal::Main::Constants qw(
|
||||
PE_BADCREDENTIALS
|
||||
PE_BADOTP
|
||||
PE_ERROR
|
||||
PE_FORMEMPTY
|
||||
PE_OK
|
||||
|
@ -119,7 +119,7 @@ sub verify {
|
|||
$self->userLogger->warn( 'Second factor failed for '
|
||||
. $session->{ $self->conf->{whatToTrace} } );
|
||||
$self->logger->error("External verify command failed (code $c)");
|
||||
return PE_BADCREDENTIALS;
|
||||
return PE_BADOTP;
|
||||
}
|
||||
return PE_OK;
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ sub verify {
|
|||
|
||||
$self->userLogger->warn( 'Second factor failed for '
|
||||
. $session->{ $self->conf->{whatToTrace} } );
|
||||
return PE_BADCREDENTIALS;
|
||||
return PE_BADOTP;
|
||||
}
|
||||
|
||||
# system() is used with an array to avoid shell injection
|
||||
|
|
|
@ -3,7 +3,7 @@ package Lemonldap::NG::Portal::2F::Mail2F;
|
|||
use strict;
|
||||
use Mouse;
|
||||
use Lemonldap::NG::Portal::Main::Constants qw(
|
||||
PE_BADCREDENTIALS
|
||||
PE_BADOTP
|
||||
PE_ERROR
|
||||
PE_FORMEMPTY
|
||||
PE_OK
|
||||
|
@ -147,7 +147,7 @@ sub verify {
|
|||
|
||||
$self->userLogger->warn( 'Second factor failed for '
|
||||
. $session->{ $self->conf->{whatToTrace} } );
|
||||
return PE_BADCREDENTIALS;
|
||||
return PE_BADOTP;
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -3,7 +3,7 @@ package Lemonldap::NG::Portal::2F::REST;
|
|||
use strict;
|
||||
use Mouse;
|
||||
use Lemonldap::NG::Portal::Main::Constants qw(
|
||||
PE_BADCREDENTIALS
|
||||
PE_BADOTP
|
||||
PE_ERROR
|
||||
PE_FORMEMPTY
|
||||
PE_OK
|
||||
|
@ -139,7 +139,7 @@ sub verify {
|
|||
unless ( $res->{result} ) {
|
||||
$self->userLogger->warn( 'REST Second factor failed for '
|
||||
. $session->{ $self->conf->{whatToTrace} } );
|
||||
return PE_BADCREDENTIALS;
|
||||
return PE_BADOTP;
|
||||
}
|
||||
PE_OK;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ package Lemonldap::NG::Portal::2F::Radius;
|
|||
use strict;
|
||||
use Mouse;
|
||||
use Lemonldap::NG::Portal::Main::Constants qw(
|
||||
PE_BADCREDENTIALS
|
||||
PE_BADOTP
|
||||
PE_ERROR
|
||||
PE_MALFORMEDUSER
|
||||
PE_OK
|
||||
|
@ -107,7 +107,7 @@ sub verify {
|
|||
. $session->{ $self->conf->{whatToTrace} } );
|
||||
$self->logger->warn(
|
||||
"Radius server replied: " . $self->radius->get_error );
|
||||
return PE_BADCREDENTIALS;
|
||||
return PE_BADOTP;
|
||||
}
|
||||
$self->logger->debug("Radius server accepted 2F credentials");
|
||||
PE_OK;
|
||||
|
|
|
@ -8,7 +8,7 @@ use strict;
|
|||
use Mouse;
|
||||
use JSON qw(from_json to_json);
|
||||
use Lemonldap::NG::Portal::Main::Constants qw(
|
||||
PE_BADCREDENTIALS
|
||||
PE_BADOTP
|
||||
PE_ERROR
|
||||
PE_FORMEMPTY
|
||||
PE_OK
|
||||
|
@ -99,7 +99,7 @@ sub verify {
|
|||
|
||||
unless ($secret) {
|
||||
$self->logger->debug("No TOTP secret found");
|
||||
return PE_BADCREDENTIALS;
|
||||
return PE_BADOTP;
|
||||
}
|
||||
|
||||
my $r = $self->verifyCode(
|
||||
|
@ -117,7 +117,7 @@ sub verify {
|
|||
$self->userLogger->notice( 'Invalid TOTP for '
|
||||
. $session->{ $self->conf->{whatToTrace} }
|
||||
. ')' );
|
||||
return PE_BADCREDENTIALS;
|
||||
return PE_BADOTP;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ use Mouse;
|
|||
use JSON qw(from_json to_json);
|
||||
use Lemonldap::NG::Portal::Main::Constants qw(
|
||||
PE_ERROR
|
||||
PE_BADCREDENTIALS
|
||||
PE_BADOTP
|
||||
PE_FORMEMPTY
|
||||
PE_OK
|
||||
PE_SENDRESPONSE
|
||||
|
@ -96,7 +96,7 @@ sub run {
|
|||
$self->userLogger->warn( 'User '
|
||||
. $req->{sessionInfo}->{ $self->conf->{whatToTrace} }
|
||||
. ' has no Yubikey registered' );
|
||||
return PE_BADCREDENTIALS;
|
||||
return PE_BADOTP;
|
||||
}
|
||||
$self->logger->debug("Found Yubikey : $yubikey");
|
||||
|
||||
|
@ -149,11 +149,11 @@ sub verify {
|
|||
)
|
||||
{
|
||||
$self->userLogger->warn('Yubikey not registered');
|
||||
return PE_BADCREDENTIALS;
|
||||
return PE_BADOTP;
|
||||
}
|
||||
if ( $self->yubi->otp($code) ne 'OK' ) {
|
||||
$self->userLogger->warn('Yubikey verification failed');
|
||||
return PE_BADCREDENTIALS;
|
||||
return PE_BADOTP;
|
||||
}
|
||||
PE_OK;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ package Lemonldap::NG::Portal::Auth::AD;
|
|||
use strict;
|
||||
use Mouse;
|
||||
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';
|
||||
|
||||
|
@ -66,7 +66,8 @@ sub authenticate {
|
|||
my ( $self, $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},
|
||||
'msDS-User-Account-Control-Computed' );
|
||||
my $_adUac =
|
||||
|
@ -75,6 +76,15 @@ sub authenticate {
|
|||
|
||||
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
|
||||
my $mask = 0xf00000; # mask to get the 8 at 6th position
|
||||
my $expired_flag =
|
||||
|
|
|
@ -152,6 +152,17 @@ sub authForce {
|
|||
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
|
||||
###############
|
||||
# Note that UserDB::Combination uses the same object.
|
||||
|
|
|
@ -6,6 +6,7 @@ use Mouse;
|
|||
use Lemonldap::NG::Common::FormEncode;
|
||||
use Lemonldap::NG::Portal::Main::Constants qw(
|
||||
PE_BADURL
|
||||
PE_BADCREDENTIALS
|
||||
PE_CONFIRM
|
||||
PE_ERROR
|
||||
PE_LOGOUT_OK
|
||||
|
@ -547,11 +548,12 @@ sub run {
|
|||
}
|
||||
|
||||
my $scope_messages = {
|
||||
openid => 'yourIdentity',
|
||||
profile => 'yourProfile',
|
||||
email => 'yourEmail',
|
||||
address => 'yourAddress',
|
||||
phone => 'yourPhone',
|
||||
openid => 'yourIdentity',
|
||||
profile => 'yourProfile',
|
||||
email => 'yourEmail',
|
||||
address => 'yourAddress',
|
||||
phone => 'yourPhone',
|
||||
offline_access => 'yourOffline',
|
||||
};
|
||||
my @list;
|
||||
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
|
||||
if ( $flow eq "authorizationcode" ) {
|
||||
|
||||
|
@ -616,14 +659,15 @@ sub run {
|
|||
my $codeSession = $self->newAuthorizationCode(
|
||||
$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'},
|
||||
scope => $oidc_request->{'scope'},
|
||||
client_id => $client_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
|
||||
my $id_token_exp = $self->conf->{oidcRPMetaDataOptions}->{$rp}
|
||||
->{oidcRPMetaDataOptionsIDTokenExpiration};
|
||||
my $id_token_exp =
|
||||
$self->conf->{oidcRPMetaDataOptions}->{$rp}
|
||||
->{oidcRPMetaDataOptionsIDTokenExpiration}
|
||||
|| $self->conf->{oidcServiceIDTokenExpiration};
|
||||
$id_token_exp += time;
|
||||
|
||||
my $authenticationLevel =
|
||||
|
@ -728,12 +774,15 @@ sub run {
|
|||
$id_token_payload_hash->{'acr'} = $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
|
||||
# Claims must be set in id_token
|
||||
my $claims =
|
||||
$self->buildUserInfoResponse( $oidc_request->{'scope'},
|
||||
$self->buildUserInfoResponseFromId(
|
||||
$oidc_request->{'scope'},
|
||||
$rp, $req->id );
|
||||
|
||||
foreach ( keys %$claims ) {
|
||||
|
@ -749,8 +798,10 @@ sub run {
|
|||
$self->logger->debug("Generated id token: $id_token");
|
||||
|
||||
# Send token response
|
||||
my $expires_in = $self->conf->{oidcRPMetaDataOptions}->{$rp}
|
||||
->{oidcRPMetaDataOptionsAccessTokenExpiration};
|
||||
my $expires_in =
|
||||
$self->conf->{oidcRPMetaDataOptions}->{$rp}
|
||||
->{oidcRPMetaDataOptionsAccessTokenExpiration}
|
||||
|| $self->conf->{oidcServiceAccessTokenExpiration};
|
||||
|
||||
# Build Response
|
||||
my $response_url = $self->buildImplicitAuthnResponse(
|
||||
|
@ -783,11 +834,12 @@ sub run {
|
|||
my $codeSession = $self->newAuthorizationCode(
|
||||
$rp,
|
||||
{
|
||||
nonce => $oidc_request->{'nonce'},
|
||||
offline => $offline,
|
||||
redirect_uri => $oidc_request->{'redirect_uri'},
|
||||
client_id => $client_id,
|
||||
scope => $oidc_request->{'scope'},
|
||||
user_session_id => $req->id,
|
||||
nonce => $oidc_request->{'nonce'},
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -836,11 +888,13 @@ sub run {
|
|||
# ID token payload
|
||||
my $id_token_exp =
|
||||
$self->conf->{oidcRPMetaDataOptions}->{$rp}
|
||||
->{oidcRPMetaDataOptionsIDTokenExpiration};
|
||||
->{oidcRPMetaDataOptionsIDTokenExpiration}
|
||||
|| $self->conf->{oidcServiceIDTokenExpiration};
|
||||
$id_token_exp += time;
|
||||
|
||||
my $id_token_acr =
|
||||
"loa-" . $req->{sessionInfo}->{authenticationLevel};
|
||||
"loa-"
|
||||
. ( $req->{sessionInfo}->{authenticationLevel} || 0 );
|
||||
|
||||
my $user_id_attribute =
|
||||
$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->{'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
|
||||
# Claims must be set in id_token
|
||||
my $claims = $self->buildUserInfoResponse(
|
||||
my $claims = $self->buildUserInfoResponseFromId(
|
||||
$oidc_request->{'scope'},
|
||||
$rp, $req->id );
|
||||
|
||||
|
@ -887,8 +943,10 @@ sub run {
|
|||
$self->logger->debug("Generated id token: $id_token");
|
||||
}
|
||||
|
||||
my $expires_in = $self->conf->{oidcRPMetaDataOptions}->{$rp}
|
||||
->{oidcRPMetaDataOptionsAccessTokenExpiration};
|
||||
my $expires_in =
|
||||
$self->conf->{oidcRPMetaDataOptions}->{$rp}
|
||||
->{oidcRPMetaDataOptionsAccessTokenExpiration}
|
||||
|| $self->conf->{oidcServiceAccessTokenExpiration};
|
||||
|
||||
# Build Response
|
||||
my $response_url = $self->buildHybridAuthnResponse(
|
||||
|
@ -1013,152 +1071,466 @@ sub token {
|
|||
|
||||
my $client_id = $self->oidcRPList->{$rp}->{oidcRPMetaDataOptionsClientID};
|
||||
|
||||
# Get code session
|
||||
my $code = $req->param('code');
|
||||
my $grant_type = $req->param('grant_type');
|
||||
|
||||
unless ($code) {
|
||||
$self->logger->error("No code found on token endpoint");
|
||||
return $self->p->sendError( $req, 'invalid_request', 400 );
|
||||
}
|
||||
# Autorization Code grant
|
||||
if ( $grant_type eq 'authorization_code' ) {
|
||||
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) {
|
||||
$self->logger->error("Unable to find OIDC session $code");
|
||||
return $self->p->sendError( $req, 'invalid_request', 400 );
|
||||
}
|
||||
unless ($codeSession) {
|
||||
$self->logger->error("Unable to find OIDC session $code");
|
||||
return $self->p->sendError( $req, 'invalid_request', 400 );
|
||||
}
|
||||
|
||||
# Check PKCE
|
||||
if ( $self->conf->{oidcRPMetaDataOptions}->{$rp}
|
||||
->{oidcRPMetaDataOptionsRequirePKCE} )
|
||||
{
|
||||
unless (
|
||||
$self->validatePKCEChallenge(
|
||||
$req->param('code_verifier'),
|
||||
$codeSession->data->{'code_challenge'},
|
||||
$codeSession->data->{'code_challenge_method'}
|
||||
)
|
||||
)
|
||||
$codeSession->remove();
|
||||
|
||||
# Check PKCE
|
||||
if ( $self->conf->{oidcRPMetaDataOptions}->{$rp}
|
||||
->{oidcRPMetaDataOptionsRequirePKCE} )
|
||||
{
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
||||
# 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 );
|
||||
}
|
||||
|
||||
# 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,
|
||||
# Check we have the same redirect_uri value
|
||||
unless (
|
||||
$req->param("redirect_uri") eq $codeSession->data->{redirect_uri} )
|
||||
{
|
||||
scope => $codeSession->data->{scope},
|
||||
rp => $rp,
|
||||
user_session_id => $apacheSession->id,
|
||||
$self->userLogger->error( "Provided redirect_uri does not match "
|
||||
. $codeSession->data->{redirect_uri} );
|
||||
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(
|
||||
"Unable to create OIDC session for access_token");
|
||||
$codeSession->remove();
|
||||
return $self->p->sendError( $req, 'invalid_request', 400 );
|
||||
$grant_type
|
||||
? "Missing grant_type parameter"
|
||||
: "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
|
||||
|
@ -1190,8 +1562,39 @@ sub userInfo {
|
|||
my $rp = $accessTokenSession->data->{rp};
|
||||
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 =
|
||||
$self->buildUserInfoResponse( $scope, $rp, $user_session_id );
|
||||
$self->buildUserInfoResponse( $scope, $rp, $session );
|
||||
unless ($userinfo_response) {
|
||||
return $self->returnBearerError( 'invalid_request', 'Invalid request',
|
||||
401 );
|
||||
|
|
|
@ -1717,7 +1717,8 @@ sub sloServer {
|
|||
$req->data->{samlSLOCalled} = 1;
|
||||
|
||||
# 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
|
||||
my $signSLOMessage = $self->conf->{samlSPMetaDataOptions}->{$spConfKey}
|
||||
|
|
|
@ -114,7 +114,14 @@ sub init {
|
|||
sub getUser {
|
||||
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(
|
||||
base => $self->conf->{ldapBase},
|
||||
scope => 'sub',
|
||||
|
|
|
@ -249,6 +249,9 @@ sub userBind {
|
|||
if ( $mesg->code == 0 ) {
|
||||
return PE_OK;
|
||||
}
|
||||
else {
|
||||
$req->data->{ldapError} = $mesg->error;
|
||||
}
|
||||
}
|
||||
$self->{portal}->userLogger->warn("Bad password for $req->{user}");
|
||||
return PE_BADCREDENTIALS;
|
||||
|
|
|
@ -3,6 +3,7 @@ package Lemonldap::NG::Portal::Lib::Notifications::JSON;
|
|||
use strict;
|
||||
use Mouse;
|
||||
use JSON qw(from_json);
|
||||
use POSIX qw(strftime);
|
||||
|
||||
our $VERSION = '2.1.0';
|
||||
|
||||
|
@ -34,18 +35,22 @@ sub checkForNotifications {
|
|||
return 0 unless ($notifs);
|
||||
|
||||
# Transform notifications
|
||||
my $i = 0; #Files count
|
||||
my $i = 0; # Files count
|
||||
my @res;
|
||||
my $now = strftime "%Y-%m-%d", localtime;
|
||||
|
||||
foreach my $file ( values %$notifs ) {
|
||||
my $json = from_json( $file, { allow_nonref => 1 } );
|
||||
my $j = 0; #Notifications count
|
||||
my $json = eval { from_json( $file, { allow_nonref => 1 } ) };
|
||||
$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' );
|
||||
LOOP: foreach my $notif ( @{$json} ) {
|
||||
|
||||
# Get the reference
|
||||
my $reference = $notif->{reference};
|
||||
|
||||
$self->logger->debug("Get reference $reference");
|
||||
$self->logger->debug("Get reference: $reference");
|
||||
|
||||
# Check it in session
|
||||
if ( exists $req->{sessionInfo}->{"notification_$reference"} ) {
|
||||
|
@ -55,6 +60,35 @@ sub checkForNotifications {
|
|||
"Notification $reference was already accepted");
|
||||
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;
|
||||
$j++;
|
||||
}
|
||||
|
@ -70,6 +104,7 @@ sub checkForNotifications {
|
|||
|
||||
# Stop here if nothing to display
|
||||
return 0 unless $i;
|
||||
$self->userLogger->info("$i pending notification(s) found for $uid");
|
||||
|
||||
# Returns HTML fragment
|
||||
return $form;
|
||||
|
@ -114,8 +149,9 @@ sub getNotifBack {
|
|||
$self->p->importHandlerData($req);
|
||||
my $uid = $req->sessionInfo->{ $self->notifObject->notifField };
|
||||
|
||||
my ( $notifs, $forUser );
|
||||
eval { ( $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) {
|
||||
|
@ -134,6 +170,7 @@ sub getNotifBack {
|
|||
}
|
||||
|
||||
my $result = 1;
|
||||
my $now = strftime "%Y-%m-%d", localtime;
|
||||
foreach my $fileName ( keys %$notifs ) {
|
||||
my $file = $notifs->{$fileName};
|
||||
my $fileResult = 1;
|
||||
|
@ -141,14 +178,48 @@ sub getNotifBack {
|
|||
$json = [$json] unless ( ref $json eq 'ARRAY' );
|
||||
|
||||
# Get pending notifications and verify that they have been accepted
|
||||
foreach my $notif (@$json) {
|
||||
LOOP: foreach my $notif (@$json) {
|
||||
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
|
||||
if ( my $refId = $refs->{$reference} ) {
|
||||
|
||||
# Verity that checkboxes have been checked
|
||||
if ( $notif->{check} ) {
|
||||
$notif->{check} = [ $notif->{check} ]
|
||||
unless ( ref( $notif->{check} ) eq 'ARRAY' );
|
||||
if ( my $toCheckCount = @{ $notif->{check} } ) {
|
||||
unless ($checks->{$refId}
|
||||
and $toCheckCount == @{ $checks->{$refId} } )
|
||||
|
@ -157,7 +228,7 @@ sub getNotifBack {
|
|||
"$uid has not accepted notification $reference"
|
||||
);
|
||||
$result = $fileResult = 0;
|
||||
next;
|
||||
next LOOP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -165,10 +236,10 @@ sub getNotifBack {
|
|||
else {
|
||||
# Current pending notification has not been found in
|
||||
# request
|
||||
$result = $fileResult = 0;
|
||||
$self->logger->debug(
|
||||
'Current pending notification has not been found');
|
||||
next;
|
||||
$result = $fileResult = 0;
|
||||
next LOOP;
|
||||
}
|
||||
|
||||
# Register acceptation
|
||||
|
@ -217,6 +288,8 @@ sub toForm {
|
|||
@notifs = map {
|
||||
$i++;
|
||||
if ( $_->{check} ) {
|
||||
$_->{check} = [ $_->{check} ]
|
||||
unless ( ref( $_->{check} ) eq 'ARRAY' );
|
||||
my $j = 0;
|
||||
$_->{check} =
|
||||
[ map { $j++; { id => '1x' . $i . 'x' . $j, value => $_ } }
|
||||
|
@ -238,8 +311,10 @@ sub notificationServer {
|
|||
my ( $res, $err );
|
||||
if ( $req->method =~ /^POST$/i ) {
|
||||
$self->p->logger->debug("POST request");
|
||||
( $res, $err ) =
|
||||
eval { $self->notifObject->newNotification( $req->content ) };
|
||||
( $res, $err ) = eval {
|
||||
$self->notifObject->newNotification( $req->content,
|
||||
$self->conf->{notificationDefaultCond} );
|
||||
};
|
||||
return $self->p->sendError( $req, $@, 500 ) if ($@);
|
||||
}
|
||||
elsif ( $req->method =~ /^GET$/i ) {
|
||||
|
@ -252,10 +327,8 @@ sub notificationServer {
|
|||
$res = [];
|
||||
foreach my $notif ( keys %$notifs ) {
|
||||
$self->p->logger->debug("Found notification $notif");
|
||||
my $json;
|
||||
eval {
|
||||
$json = from_json( $notifs->{$notif}, { allow_nonref => 1 } );
|
||||
};
|
||||
my $json =
|
||||
eval { from_json( $notifs->{$notif}, { allow_nonref => 1 } ) };
|
||||
return $self->p->sendError( $req, "Unable to decode JSON file: $@",
|
||||
400 )
|
||||
if ($@);
|
||||
|
|
|
@ -4,6 +4,7 @@ use strict;
|
|||
use Mouse;
|
||||
use XML::LibXML;
|
||||
use XML::LibXSLT;
|
||||
use POSIX qw(strftime);
|
||||
|
||||
our $VERSION = '2.1.0';
|
||||
|
||||
|
@ -44,6 +45,10 @@ has stylesheet => (
|
|||
# Underlying notifications storage object (File, DBI, LDAP,...)
|
||||
has notifObject => ( is => 'rw' );
|
||||
|
||||
# Notification server accessors
|
||||
has imported => ( is => 'rw', default => 0 );
|
||||
has server => ( is => 'rw' );
|
||||
|
||||
# INITIALIZATION
|
||||
|
||||
sub init {
|
||||
|
@ -61,10 +66,12 @@ sub checkForNotifications {
|
|||
return 0 unless ($notifs);
|
||||
|
||||
# Transform notifications
|
||||
my $i = 0; #Files count
|
||||
my $i = 0; # Files count
|
||||
my $now = strftime "%Y-%m-%d", localtime;
|
||||
|
||||
foreach my $file ( values %$notifs ) {
|
||||
my $xml = $self->parser->parse_string($file);
|
||||
my $j = 0; #Notifications count
|
||||
my $j = 0; # Notifications count
|
||||
LOOP: foreach my $notif (
|
||||
eval {
|
||||
$xml->documentElement->getElementsByTagName('notification');
|
||||
|
@ -74,7 +81,6 @@ sub checkForNotifications {
|
|||
|
||||
# Get the reference
|
||||
my $reference = $notif->getAttribute('reference');
|
||||
|
||||
$self->logger->debug("Get reference $reference");
|
||||
|
||||
# Check it in session
|
||||
|
@ -89,21 +95,35 @@ sub checkForNotifications {
|
|||
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
|
||||
my $condition = $notif->getAttribute('condition');
|
||||
|
||||
if ($condition) {
|
||||
|
||||
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 );
|
||||
|
||||
# Remove it from XML
|
||||
$notif->unbindNode();
|
||||
next LOOP;
|
||||
}
|
||||
|
||||
unless ( $condition->( $req, $req->sessionInfo ) ) {
|
||||
$self->logger->debug(
|
||||
'Notification condition not authorized');
|
||||
|
@ -113,7 +133,6 @@ sub checkForNotifications {
|
|||
next LOOP;
|
||||
}
|
||||
}
|
||||
|
||||
$j++;
|
||||
}
|
||||
|
||||
|
@ -133,6 +152,7 @@ sub checkForNotifications {
|
|||
|
||||
# Stop here if nothing to display
|
||||
return 0 unless $i;
|
||||
$self->userLogger->info("$i pending notification(s) found for $uid");
|
||||
|
||||
# Returns HTML fragment
|
||||
return $form;
|
||||
|
@ -177,7 +197,11 @@ sub getNotifBack {
|
|||
$self->p->importHandlerData($req);
|
||||
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) {
|
||||
|
||||
# Get accepted notifications
|
||||
|
@ -194,17 +218,51 @@ sub getNotifBack {
|
|||
}
|
||||
|
||||
my $result = 1;
|
||||
my $now = strftime "%Y-%m-%d", localtime;
|
||||
foreach my $fileName ( keys %$notifs ) {
|
||||
my $file = $notifs->{$fileName};
|
||||
my $fileResult = 1;
|
||||
my $xml = $self->parser->parse_string($file);
|
||||
|
||||
# Get pending notifications and verify that they have been accepted
|
||||
LOOP:
|
||||
foreach my $notif (
|
||||
$xml->documentElement->getElementsByTagName('notification') )
|
||||
{
|
||||
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
|
||||
if ( my $refId = $refs->{$reference} ) {
|
||||
|
||||
|
@ -260,7 +318,7 @@ sub getNotifBack {
|
|||
# launch 'controlUrl' to restore "urldc" using do()
|
||||
$self->logger->debug('All pending notifications have been accepted');
|
||||
$self->p->rebuildCookies($req);
|
||||
return $self->p->do( $req, ['controlUrl', @{ $self->p->endAuth }] );
|
||||
return $self->p->do( $req, [ 'controlUrl', @{ $self->p->endAuth } ] );
|
||||
}
|
||||
else {
|
||||
# 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 {
|
||||
my ( $self, $req ) = @_;
|
||||
unless ( $self->imported ) {
|
||||
|
@ -299,7 +354,8 @@ sub notificationServer {
|
|||
|
||||
sub newNotification {
|
||||
my ( $self, $req, $xml ) = @_;
|
||||
return $self->notifObject->newNotification($xml);
|
||||
return $self->notifObject->newNotification( $xml,
|
||||
$self->conf->{notificationDefaultCond} );
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue