Merge branch 'v2.0'

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

View File

@ -8,6 +8,18 @@
paths:
- 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:

View File

@ -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 \

View File

@ -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

View File

@ -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

View File

@ -66,7 +66,7 @@ describe('00 Lemonldap::NG', function() {
browser.driver.findElement(by.xpath("//input[@name='password']")).sendKeys('ohwd');
browser.driver.findElement(by.xpath("//input[@name='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

View File

@ -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

View File

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

View File

@ -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',
},
);

View File

@ -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]

View File

@ -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;

View File

@ -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/ );
}
}
}

View File

@ -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' );

View File

@ -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;',

View File

@ -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;

View File

@ -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 ($@) {

View File

@ -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) {

View File

@ -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 );

View File

@ -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;

View File

@ -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 {

View File

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

View File

@ -52,6 +52,7 @@ t/61-Lemonldap-NG-Handler-PSGI-Server.t
t/62-Lemonldap-NG-Handler-Nginx.t
t/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

View File

@ -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}->(@_);
}
}
},
);

View File

@ -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");

View File

@ -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;

View File

@ -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'
};
}

View File

@ -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'] );

View File

@ -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;
}

View File

@ -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" );

View File

@ -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(

View File

@ -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' );

View File

@ -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'] ];
}

View File

@ -40,6 +40,36 @@ ok( $h{'Auth-User'} eq 'dwho', 'Header Auth-User is set to "dwho"' )
or explain( \%h, 'Auth-User => "dwho"' );
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' );

View File

@ -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' );

View File

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

View File

@ -8,19 +8,19 @@ init(
'Lemonldap::NG::Handler::Server',
{
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);

View File

@ -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;

View File

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

View File

@ -43,6 +43,10 @@
"test1.example.com": {
"^/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]",

View File

@ -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;

View File

@ -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

View File

@ -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'
},

View File

@ -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(@_) },

View File

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

View File

@ -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,
};
}

View File

@ -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',
],
},
{

View File

@ -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;

View File

@ -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);
}

View File

@ -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;

View File

@ -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

View File

@ -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

View File

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

View File

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

View File

@ -5,11 +5,11 @@
<table class="table table-striped">
<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

View File

@ -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

View File

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

View File

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

View File

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -5,7 +5,9 @@
*/
(function() {
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

View File

@ -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

View File

@ -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":"احفظ التقرير",

View File

@ -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",

View File

@ -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",

View File

@ -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",

View File

@ -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

View File

@ -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",

View File

@ -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

View File

@ -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">

View File

@ -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>

View File

@ -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) {

View File

@ -109,12 +109,14 @@ lib/Lemonldap/NG/Portal/Plugins/ContextSwitching.pm
lib/Lemonldap/NG/Portal/Plugins/DecryptValue.pm
lib/Lemonldap/NG/Portal/Plugins/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

View File

@ -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

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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 =

View File

@ -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.

View File

@ -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 );

View File

@ -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}

View File

@ -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',

View File

@ -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;

View File

@ -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 ($@);

View File

@ -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