Merge branch 'v2.0'

This commit is contained in:
Christophe Maudoux 2019-09-01 22:10:41 +02:00
commit 174193e74c
32 changed files with 1845 additions and 1381 deletions

105
lemonldap-ng-common/.prove Normal file
View File

@ -0,0 +1,105 @@
---
generation: 2
last_run_time: 1567071551.30841
tests:
t/01-Common-Conf.t:
elapsed: 0.472490072250366
gen: 2
last_pass_time: 1567071550.71014
last_result: 0
last_run_time: 1567071550.71014
last_todo: 0
seq: 5
total_passes: 1
t/02-Common-Conf-File.t:
elapsed: 0.0793302059173584
gen: 2
last_pass_time: 1567071550.68052
last_result: 0
last_run_time: 1567071550.68052
last_todo: 0
seq: 4
total_passes: 1
t/03-Common-Conf-CDBI.t:
elapsed: 0.61043119430542
gen: 2
last_pass_time: 1567071550.95767
last_result: 0
last_run_time: 1567071550.95767
last_todo: 0
seq: 6
total_passes: 1
t/03-Common-Conf-RDBI.t:
elapsed: 0.66497802734375
gen: 2
last_pass_time: 1567071551.00435
last_result: 0
last_run_time: 1567071551.00435
last_todo: 0
seq: 7
total_passes: 1
t/05-Common-Conf-LDAP.t:
elapsed: 0.64878511428833
gen: 2
last_pass_time: 1567071551.07637
last_result: 0
last_run_time: 1567071551.07637
last_todo: 0
seq: 8
total_passes: 1
t/30-Common-Safelib.t:
elapsed: 0.0283739566802979
gen: 2
last_pass_time: 1567071550.40529
last_result: 0
last_run_time: 1567071550.40529
last_todo: 0
seq: 1
total_passes: 1
t/35-Common-Crypto.t:
elapsed: 0.190783977508545
gen: 2
last_pass_time: 1567071550.63236
last_result: 0
last_run_time: 1567071550.63236
last_todo: 0
seq: 3
total_passes: 1
t/36-Common-Regexp.t:
elapsed: 0.0631709098815918
gen: 2
last_pass_time: 1567071550.50944
last_result: 0
last_run_time: 1567071550.50944
last_todo: 0
seq: 2
total_passes: 1
t/40-Common-Session.t:
elapsed: 0.184284210205078
gen: 2
last_pass_time: 1567071551.11977
last_result: 0
last_run_time: 1567071551.11977
last_todo: 0
seq: 9
total_passes: 1
t/50-Combination-Parser.t:
elapsed: 0.108580827713013
gen: 2
last_pass_time: 1567071551.1593
last_result: 0
last_run_time: 1567071551.1593
last_todo: 0
seq: 10
total_passes: 1
t/99-pod.t:
elapsed: 0.128799915313721
gen: 2
last_pass_time: 1567071551.30716
last_result: 0
last_run_time: 1567071551.30716
last_todo: 0
seq: 11
total_passes: 1
version: 1
...

View File

@ -9,6 +9,8 @@ our $VERSION = '2.1.0';
sub compactConf {
my ( $self, $conf ) = @_;
return $conf if ( $conf->{'dontCompactConf'} );
# Remove unused auth parameters
my %keep;
foreach my $type (qw(authentication userDB passwordDB registerDB)) {

View File

@ -24,7 +24,7 @@ use constant MANAGERSECTION => "manager";
use constant SESSIONSEXPLORERSECTION => "sessionsExplorer";
use constant APPLYSECTION => "apply";
our $hashParameters = qr/^(?:(?:l(?:o(?:ca(?:lSessionStorageOption|tionRule)|goutService)|dapExportedVar|wp(?:Ssl)?Opt)|(?:(?:d(?:emo|bi)|facebook|webID)ExportedVa|exported(?:Heade|Va)|issuerDBGetParamete)r|re(?:moteGlobalStorageOption|st2f(?:Verify|Init)Arg|loadUrl)|g(?:r(?:antSessionRule|oup)|lobalStorageOption)|n(?:otificationStorageOption|ginxCustomHandler)|macro)s|o(?:idc(?: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|howLanguages|slByAjax)|o(?:idc(?:ServiceAllow(?:(?:AuthorizationCode|Implicit|Hybrid)Flow|DynamicRegistration)|RPMetaDataOptions(?:LogoutSessionRequired|BypassConsent|RequirePKCE|Public)|OPMetaDataOptions(?:(?:CheckJWTSignatur|UseNonc)e|StoreIDToken))|ldNotifFormat)|c(?:a(?:ptcha_(?:register|login|mail)_enabled|sSrvMetaDataOptions(?:Gateway|Renew))|heck(?:User(?:Display(?:PersistentInfo|EmptyValues))?|State|XSS)|o(?:ntextSwitchingStopWithLogout|rsEnabled)|da)|p(?:ortal(?:ErrorOn(?:ExpiredSession|MailNotFound)|DisplayRe(?:setPassword|gister)|(?:CheckLogin|Statu)s|OpenLinkInNewWindow|RequireOldPassword|ForceAuthn|AntiFrame)|roxyUseSoap)|l(?:dap(?:(?:Group(?:DecodeSearchedValu|Recursiv)|UsePasswordResetAttribut)e|(?:AllowResetExpired|Set)Password|ChangePasswordAsUser|PpolicyControl)|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)|(?:mai(?:lOnPasswordChang|ntenanc)|vhostMaintenanc)e|d(?:isablePersistentStorage|biDynamicHashEnabled)|rest(?:(?:Session|Config)Server|ExportSecretKeys)|h(?:ideOldPassword|ttpOnly)|yubikey2fUserCanRemoveKey|(?:activeTim|wsdlServ)er|krb(?:RemoveDomain|ByJs)|bruteForceProtection)$/;
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|howLanguages|slByAjax)|o(?:idc(?:ServiceAllow(?:(?:AuthorizationCode|Implicit|Hybrid)Flow|DynamicRegistration)|RPMetaDataOptions(?:LogoutSessionRequired|BypassConsent|RequirePKCE|Public)|OPMetaDataOptions(?:(?:CheckJWTSignatur|UseNonc)e|StoreIDToken))|ldNotifFormat)|c(?:a(?:ptcha_(?:register|login|mail)_enabled|sSrvMetaDataOptions(?:Gateway|Renew))|heck(?:User(?:Display(?:PersistentInfo|EmptyValues))?|State|XSS)|o(?:ntextSwitchingStopWithLogout|rsEnabled)|da)|p(?:ortal(?:ErrorOn(?:ExpiredSession|MailNotFound)|DisplayRe(?:setPassword|gister)|(?:CheckLogin|Statu)s|OpenLinkInNewWindow|RequireOldPassword|ForceAuthn|AntiFrame)|roxyUseSoap)|l(?:dap(?:(?:Group(?:DecodeSearchedValu|Recursiv)|UsePasswordResetAttribut)e|(?:AllowResetExpired|Set)Password|ChangePasswordAsUser|PpolicyControl)|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)|h(?:ideOldPassword|ttpOnly)|yubikey2fUserCanRemoveKey|(?:activeTim|wsdlServ)er|krb(?:RemoveDomain|ByJs)|bruteForceProtection)$/;
our @sessionTypes = ( 'remoteGlobal', 'global', 'localSession', 'persistent', 'saml', 'oidc', 'cas' );

View File

@ -179,55 +179,56 @@ sub defaultValues {
'loa-4' => 4,
'loa-5' => 5
},
'oidcServiceMetaDataAuthorizeURI' => 'authorize',
'oidcServiceMetaDataBackChannelURI' => 'blogout',
'oidcServiceMetaDataCheckSessionURI' => 'checksession.html',
'oidcServiceMetaDataEndSessionURI' => 'logout',
'oidcServiceMetaDataFrontChannelURI' => 'flogout',
'oidcServiceMetaDataJWKSURI' => 'jwks',
'oidcServiceMetaDataRegistrationURI' => 'register',
'oidcServiceMetaDataTokenURI' => 'token',
'oidcServiceMetaDataUserInfoURI' => 'userinfo',
'openIdAuthnLevel' => 1,
'openIdExportedVars' => {},
'openIdIDPList' => '0;',
'openIdSPList' => '0;',
'openIdSreg_email' => 'mail',
'openIdSreg_fullname' => 'cn',
'openIdSreg_nickname' => 'uid',
'openIdSreg_timezone' => '_timezone',
'pamAuthnLevel' => 2,
'pamService' => 'login',
'passwordDB' => 'Demo',
'passwordResetAllowedRetries' => 3,
'port' => -1,
'portal' => 'http://auth.example.com/',
'portalAntiFrame' => 1,
'portalCheckLogins' => 1,
'portalDisplayAppslist' => 1,
'portalDisplayChangePassword' => '$_auth =~ /^(LDAP|DBI|Demo)$/',
'portalDisplayFavApps' => 1,
'portalDisplayLoginHistory' => 1,
'portalDisplayLogout' => 1,
'portalDisplayOidcConsents' => '$_oidcConnectedRP',
'portalDisplayRegister' => 1,
'portalErrorOnExpiredSession' => 1,
'portalForceAuthnInterval' => 5,
'portalMainLogo' => 'common/logos/logo_llng_400px.png',
'portalPingInterval' => 60000,
'portalRequireOldPassword' => 1,
'portalSkin' => 'bootstrap',
'portalUserAttr' => '_user',
'proxyAuthnLevel' => 2,
'radius2fActivation' => 0,
'radius2fTimeout' => 20,
'radiusAuthnLevel' => 3,
'randomPasswordRegexp' => '[A-Z]{3}[a-z]{5}.\\d{2}',
'redirectFormMethod' => 'get',
'registerDB' => 'Null',
'registerTimeout' => 0,
'registerUrl' => 'http://auth.example.com/register',
'reloadTimeout' => 5,
'oidcServiceMetaDataAuthorizeURI' => 'authorize',
'oidcServiceMetaDataBackChannelURI' => 'blogout',
'oidcServiceMetaDataCheckSessionURI' => 'checksession.html',
'oidcServiceMetaDataEndSessionURI' => 'logout',
'oidcServiceMetaDataFrontChannelURI' => 'flogout',
'oidcServiceMetaDataIntrospectionURI' => 'introspect',
'oidcServiceMetaDataJWKSURI' => 'jwks',
'oidcServiceMetaDataRegistrationURI' => 'register',
'oidcServiceMetaDataTokenURI' => 'token',
'oidcServiceMetaDataUserInfoURI' => 'userinfo',
'openIdAuthnLevel' => 1,
'openIdExportedVars' => {},
'openIdIDPList' => '0;',
'openIdSPList' => '0;',
'openIdSreg_email' => 'mail',
'openIdSreg_fullname' => 'cn',
'openIdSreg_nickname' => 'uid',
'openIdSreg_timezone' => '_timezone',
'pamAuthnLevel' => 2,
'pamService' => 'login',
'passwordDB' => 'Demo',
'passwordResetAllowedRetries' => 3,
'port' => -1,
'portal' => 'http://auth.example.com/',
'portalAntiFrame' => 1,
'portalCheckLogins' => 1,
'portalDisplayAppslist' => 1,
'portalDisplayChangePassword' => '$_auth =~ /^(LDAP|DBI|Demo)$/',
'portalDisplayFavApps' => 1,
'portalDisplayLoginHistory' => 1,
'portalDisplayLogout' => 1,
'portalDisplayOidcConsents' => '$_oidcConnectedRP',
'portalDisplayRegister' => 1,
'portalErrorOnExpiredSession' => 1,
'portalForceAuthnInterval' => 5,
'portalMainLogo' => 'common/logos/logo_llng_400px.png',
'portalPingInterval' => 60000,
'portalRequireOldPassword' => 1,
'portalSkin' => 'bootstrap',
'portalUserAttr' => '_user',
'proxyAuthnLevel' => 2,
'radius2fActivation' => 0,
'radius2fTimeout' => 20,
'radiusAuthnLevel' => 3,
'randomPasswordRegexp' => '[A-Z]{3}[a-z]{5}.\\d{2}',
'redirectFormMethod' => 'get',
'registerDB' => 'Null',
'registerTimeout' => 0,
'registerUrl' => 'http://auth.example.com/register',
'reloadTimeout' => 5,
'remoteGlobalStorage' => 'Lemonldap::NG::Common::Apache::Session::SOAP',
'remoteGlobalStorageOptions' => {
'ns' =>

View File

@ -67,6 +67,6 @@ our $issuerParameters = {
issuerDBSAML => [qw(issuerDBSAMLActivation issuerDBSAMLPath issuerDBSAMLRule)],
};
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 samlIdPResolveCookie samlMetadataForceUTF8 samlStorage samlStorageOptions samlRelayStateTimeout samlUseQueryStringSpecific samlCommonDomainCookieActivation samlCommonDomainCookieDomain samlCommonDomainCookieReader samlCommonDomainCookieWriter samlDiscoveryProtocolActivation samlDiscoveryProtocolURL samlDiscoveryProtocolPolicy samlDiscoveryProtocolIsPassive samlOverrideIDPEntityID)];
our $oidcServiceParameters = [qw(oidcServiceMetaDataIssuer oidcServiceMetaDataAuthorizeURI oidcServiceMetaDataTokenURI oidcServiceMetaDataUserInfoURI oidcServiceMetaDataJWKSURI oidcServiceMetaDataRegistrationURI oidcServiceMetaDataEndSessionURI oidcServiceMetaDataCheckSessionURI oidcServiceMetaDataFrontChannelURI oidcServiceMetaDataBackChannelURI oidcServiceMetaDataAuthnContext oidcServicePrivateKeySig oidcServicePublicKeySig oidcServiceKeyIdSig oidcServiceAllowDynamicRegistration oidcServiceAllowAuthorizationCodeFlow oidcServiceAllowImplicitFlow oidcServiceAllowHybridFlow oidcStorage oidcStorageOptions)];
our $oidcServiceParameters = [qw(oidcServiceMetaDataIssuer oidcServiceMetaDataAuthorizeURI oidcServiceMetaDataTokenURI oidcServiceMetaDataUserInfoURI oidcServiceMetaDataJWKSURI oidcServiceMetaDataRegistrationURI oidcServiceMetaDataIntrospectionURI oidcServiceMetaDataEndSessionURI oidcServiceMetaDataCheckSessionURI oidcServiceMetaDataFrontChannelURI oidcServiceMetaDataBackChannelURI oidcServiceMetaDataAuthnContext oidcServicePrivateKeySig oidcServicePublicKeySig oidcServiceKeyIdSig oidcServiceAllowDynamicRegistration oidcServiceAllowAuthorizationCodeFlow oidcServiceAllowImplicitFlow oidcServiceAllowHybridFlow oidcStorage oidcStorageOptions)];
1;

View File

@ -380,7 +380,8 @@ sub headersInit {
$class->tsv->{headerList}->{$vhost} = [ keys %headers ];
my $sub = '';
foreach ( keys %headers ) {
my $val = $class->substitute( $headers{$_} ) . " || ''";
$headers{$_} ||= "''";
my $val = $class->substitute( $headers{$_} ) . " // ''";
$sub .= "('$_' => $val),";
}

View File

@ -26,6 +26,8 @@ init(
exportedHeaders => {
'test2.example.com' => {
'Auth-User' => '$uid',
'empty' => undef,
'zero' => "'0'",
},
}
}
@ -114,6 +116,13 @@ ok(
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 { /service|^XFromVH$/ } @{ $res->[1] };
@values = grep { /\.example\.com|^$sessionId$/ } @{ $res->[1] };
ok( @headers == 2, 'Found 2 service headers' )

View File

@ -1111,6 +1111,10 @@ qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.]
qr/^(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][-a-zA-Z0-9]*[a-zA-Z0-9]|[a-zA-Z])[.]?))?$/,
'type' => 'text'
},
'dontCompactConf' => {
'default' => 0,
'type' => 'bool'
},
'exportedAttr' => {
'type' => 'text'
},
@ -2107,6 +2111,10 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
'default' => 'flogout',
'type' => 'text'
},
'oidcServiceMetaDataIntrospectionURI' => {
'default' => 'introspect',
'type' => 'text'
},
'oidcServiceMetaDataIssuer' => {
'type' => 'text'
},

View File

@ -385,6 +385,11 @@ sub attributes {
msgFail => '__badUrl__',
documentation => 'URL to call on reload',
},
dontCompactConf => {
type => 'bool',
default => 0,
documentation => 'Don t compact configuration',
},
portalMainLogo => {
type => 'text',
default => 'common/logos/logo_llng_400px.png',
@ -3483,6 +3488,11 @@ m{^(?:ldapi://[^/]*/?|\w[\w\-\.]*(?::\d{1,5})?|ldap(?:s|\+tls)?://\w[\w\-\.]*(?:
default => 'register',
documentation => 'OpenID Connect registration endpoint',
},
oidcServiceMetaDataIntrospectionURI => {
type => 'text',
default => 'introspect',
documentation => 'OpenID Connect introspection endpoint',
},
oidcServiceMetaDataEndSessionURI => {
type => 'text',
default => 'logout',

View File

@ -511,7 +511,8 @@ sub tree {
title => 'logParams',
help => 'logs.html',
form => 'simpleInputContainer',
nodes => [ 'whatToTrace', 'customToTrace', 'hiddenAttributes' ]
nodes =>
[ 'whatToTrace', 'customToTrace', 'hiddenAttributes' ]
},
{
title => 'cookieParams',
@ -564,7 +565,7 @@ sub tree {
{
title => 'reloadParams',
help => 'configlocation.html#configuration_reload',
nodes => [ 'reloadUrls', 'reloadTimeout', ]
nodes => [ 'reloadUrls', 'reloadTimeout', 'dontCompactConf' ]
},
{
title => 'plugins',
@ -1125,6 +1126,7 @@ sub tree {
'oidcServiceMetaDataUserInfoURI',
'oidcServiceMetaDataJWKSURI',
'oidcServiceMetaDataRegistrationURI',
'oidcServiceMetaDataIntrospectionURI',
'oidcServiceMetaDataEndSessionURI',
'oidcServiceMetaDataCheckSessionURI',
'oidcServiceMetaDataFrontChannelURI',

View File

@ -103,13 +103,30 @@ sub check {
hdebug(" testNewConf() failed");
return 0;
}
my $separator = $self->newConf->{multiValuesSeparator} || '; ';
hdebug(" tests succeed");
$self->compactConf( $self->newConf );
my %conf = %{ $self->newConf() };
my %compactedConf = %{ $self->compactConf( $self->newConf ) };
my @removedKeys = ();
unless ( $self->confChanged ) {
hdebug(" no change detected");
$self->message('__confNotChanged__');
return 0;
}
unless ( $self->newConf->{dontCompactConf} ) {
foreach ( sort keys %conf ) {
push @removedKeys, $_ unless exists $compactedConf{$_};
}
}
push @{ $self->changes },
(
$self->{newConf}->{dontCompactConf}
? { confCompacted => '0' }
: {
confCompacted => '1',
removedKeys => join( $separator, @removedKeys )
}
);
return 1;
}

View File

@ -252,6 +252,7 @@
"dateTitle":"تاريخ",
"dn":"دي أن",
"domain":"نطاق",
"dontCompactConf":"Don't compact configuration file",
"download":"تحميل",
"downloadIt":"نزله",
"duplicate":"مكررة",
@ -570,6 +571,7 @@
"oidcServiceMetaDataJWKSURI":"JWKS",
"oidcServiceMetaDataKeys":"المفاتيح",
"oidcServiceMetaDataRegistrationURI":"التسجيل",
"oidcServiceMetaDataIntrospectionURI":"Introspection",
"oidcServiceMetaDataSecurity":"الحماية",
"oidcServiceMetaDataEndSessionURI":"نهاية الجلسة",
"oidcServiceMetaDataAuthnContext":"سياق إثبات الهوية",
@ -1051,4 +1053,4 @@
"samlRelayStateTimeout":"تناوب حالة مهلة الجلسة ",
"samlUseQueryStringSpecific":"استخدام أسلوب query_string المعين",
"samlOverrideIDPEntityID":"Override Entity ID when acting as IDP"
}
}

View File

@ -251,6 +251,7 @@
"dateTitle":"Dates",
"dn":"DN",
"domain":"Domain",
"dontCompactConf":"Don't compact configuration file",
"download":"Download",
"downloadIt":"Download it",
"duplicate":"Duplicate",
@ -569,6 +570,7 @@
"oidcServiceMetaDataJWKSURI":"JWKS",
"oidcServiceMetaDataKeys":"Keys",
"oidcServiceMetaDataRegistrationURI":"Registration",
"oidcServiceMetaDataIntrospectionURI":"Introspection",
"oidcServiceMetaDataSecurity":"Security",
"oidcServiceMetaDataEndSessionURI":"End of session",
"oidcServiceMetaDataAuthnContext":"Authentication context",
@ -1050,4 +1052,4 @@
"samlRelayStateTimeout":"RelayState session timeout",
"samlUseQueryStringSpecific":"Use specific query_string method",
"samlOverrideIDPEntityID":"Override Entity ID when acting as IDP"
}
}

View File

@ -251,6 +251,7 @@
"dateTitle":"Dates",
"dn":"DN",
"domain":"Domain",
"dontCompactConf":"Don't compact configuration file",
"download":"Download",
"downloadIt":"Download it",
"duplicate":"Duplicate",
@ -569,6 +570,7 @@
"oidcServiceMetaDataJWKSURI":"JWKS",
"oidcServiceMetaDataKeys":"Keys",
"oidcServiceMetaDataRegistrationURI":"Registration",
"oidcServiceMetaDataIntrospectionURI":"Introspection",
"oidcServiceMetaDataSecurity":"Security",
"oidcServiceMetaDataEndSessionURI":"End of session",
"oidcServiceMetaDataAuthnContext":"Authentication context",

View File

@ -251,6 +251,7 @@
"dateTitle":"Dates",
"dn":"DN",
"domain":"Domaine",
"dontCompactConf":"Ne pas compacter le fichier de configuration",
"download":"Télécharger",
"downloadIt":"Télécharger",
"duplicate":"Dupliquer",
@ -569,6 +570,7 @@
"oidcServiceMetaDataJWKSURI":"JWKS",
"oidcServiceMetaDataKeys":"Clefs",
"oidcServiceMetaDataRegistrationURI":"Enregistrement",
"oidcServiceMetaDataIntrospectionURI":"Introspection",
"oidcServiceMetaDataSecurity":"Sécurité",
"oidcServiceMetaDataEndSessionURI":"Fin de session",
"oidcServiceMetaDataAuthnContext":"Contexte d'authentification",

View File

@ -251,6 +251,7 @@
"dateTitle":"Date",
"dn":"DN",
"domain":"Dominio",
"dontCompactConf":"Don't compact configuration file",
"download":"Scarica",
"downloadIt":"Scaricalo",
"duplicate":"Duplicato",
@ -569,6 +570,7 @@
"oidcServiceMetaDataJWKSURI":"JWKS",
"oidcServiceMetaDataKeys":"Chiavi",
"oidcServiceMetaDataRegistrationURI":"Registrazione",
"oidcServiceMetaDataIntrospectionURI":"Introspection",
"oidcServiceMetaDataSecurity":"Sicurezza",
"oidcServiceMetaDataEndSessionURI":"Fine sessione",
"oidcServiceMetaDataAuthnContext":"Contesto di autenticazione",
@ -1050,4 +1052,4 @@
"samlRelayStateTimeout":"Timeout di sessione di RelayState",
"samlUseQueryStringSpecific":"Utilizza il metodo specifico query_string",
"samlOverrideIDPEntityID":"Sostituisci l'ID entità quando agisce come IDP"
}
}

View File

@ -251,6 +251,7 @@
"dateTitle":"Ngày",
"dn":"DN",
"domain":"Tên miền",
"dontCompactConf":"Don't compact configuration file",
"download":"Tải xuống",
"downloadIt":"Tải xuống",
"duplicate":"Sao y",
@ -569,6 +570,7 @@
"oidcServiceMetaDataJWKSURI":"JWKS",
"oidcServiceMetaDataKeys":"Khóa",
"oidcServiceMetaDataRegistrationURI":"Đăng ký",
"oidcServiceMetaDataIntrospectionURI":"Introspection",
"oidcServiceMetaDataSecurity":"Bảo mật",
"oidcServiceMetaDataEndSessionURI":"Kết thúc phiên",
"oidcServiceMetaDataAuthnContext":"Ngữ cảnh xác thực",
@ -1050,4 +1052,4 @@
"samlRelayStateTimeout":"Thời gian hết hạn phiên RelayState ",
"samlUseQueryStringSpecific":"Sử dụng phương pháp query_string cụ thể",
"samlOverrideIDPEntityID":"Override Entity ID when acting as IDP"
}
}

View File

@ -251,6 +251,7 @@
"dateTitle":"日期",
"dn":"LDAP 唯一名称",
"domain":"域",
"dontCompactConf":"Don't compact configuration file",
"download":"下载",
"downloadIt":"下载它",
"duplicate":"Duplicate",
@ -569,6 +570,7 @@
"oidcServiceMetaDataJWKSURI":"JWKS",
"oidcServiceMetaDataKeys":"键值",
"oidcServiceMetaDataRegistrationURI":"Registration",
"oidcServiceMetaDataIntrospectionURI":"Introspection",
"oidcServiceMetaDataSecurity":"Security",
"oidcServiceMetaDataEndSessionURI":"End of session",
"oidcServiceMetaDataAuthnContext":"Authentication context",
@ -1050,4 +1052,4 @@
"samlRelayStateTimeout":"RelayState session timeout",
"samlUseQueryStringSpecific":"Use specific query_string method",
"samlOverrideIDPEntityID":"Override Entity ID when acting as IDP"
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -38,7 +38,7 @@ while ( my $body = &body() ) {
#print STDERR Dumper($resBody);
ok( $resBody->{result} == 1, "$desc: JSON response contains \"result:1\"" );
ok( @{ $resBody->{details}->{__changes__} } eq 1,
ok( @{ $resBody->{details}->{__changes__} } eq 2,
"$desc: conf has changed" )
or print STDERR Dumper($resBody);
ok(

View File

@ -54,8 +54,8 @@ ok(
) or print STDERR Dumper($resBody);
ok(
@{ $resBody->{details}->{__changes__} } == 22,
'JSON response contains 24 changes'
@{ $resBody->{details}->{__changes__} } == 23,
'JSON response contains 23 changes'
) or print STDERR Dumper($resBody);
#print STDERR Dumper($resBody);
@ -113,11 +113,10 @@ ok( $res = &client->jsonResponse('/diff/1/2'), 'Diff called' );
my ( @c1, @c2 );
ok( ( @c1 = sort keys %{ $res->[0] } ), 'diff() detects changes in conf 1' );
ok( ( @c2 = sort keys %{ $res->[1] } ), 'diff() detects changes in conf 2' );
ok( @c1 == 12, '11 keys changed in conf 1' )
ok( @c1 == 12, '12 keys changed in conf 1' )
or print STDERR "Expect: 12 keys, get: " . join( ', ', @c1 ) . "\n";
ok( @c2 == 16, '14 keys changed or created in conf 2' )
ok( @c2 == 16, '16 keys changed or created in conf 2' )
or print STDERR "Expect: 16 keys, get: " . join( ',', @c2 ) . "\n";
count(5);
unlink $confFiles->[1];
@ -246,6 +245,10 @@ sub changes {
'new' => 0,
'key' => 'captcha_mail_enabled',
'old' => '1'
},
{
'confCompacted' => '1',
'removedKeys' => 'some; keys'
}
];
}

View File

@ -38,7 +38,7 @@ foreach my $i ( 0 .. 1 ) {
}
ok(
@{ $resBody->{details}->{__changes__} } == 22,
@{ $resBody->{details}->{__changes__} } == 23,
'JSON response contains 22 changes'
) or print STDERR Dumper($resBody);
@ -224,6 +224,10 @@ sub changes {
{
'key' => 'virtualHosts',
'old' => 'test2.example.com'
},
{
'confCompacted' => '1',
'removedKeys' => 'some; keys'
}
];
}

File diff suppressed because it is too large Load Diff

View File

@ -34,7 +34,7 @@ sub authenticate {
{ user => $req->user, password => $req->data->{password} } );
};
if ($@) {
$self->logger("Auth error: $@");
$self->logger->error("Auth error: $@");
$self->setSecurity($req);
return PE_ERROR;
}

View File

@ -57,6 +57,7 @@ has iss => (
# - userinfo : => userInfo() for unauth users (RP)
# - jwks : => jwks() for unauth users (RP)
# - register : => registration() for unauth users (RP)
# - introspect : => introspection() for unauth users (RP)
#
# Other paths will be handle by run() and return PE_ERROR
#
@ -86,22 +87,24 @@ sub init {
# Manage RP requests
$self->addRouteFromConf(
'Unauth',
oidcServiceMetaDataEndSessionURI => 'endSessionDone',
oidcServiceMetaDataCheckSessionURI => 'checkSession',
oidcServiceMetaDataTokenURI => 'token',
oidcServiceMetaDataUserInfoURI => 'userInfo',
oidcServiceMetaDataJWKSURI => 'jwks',
oidcServiceMetaDataRegistrationURI => 'registration',
oidcServiceMetaDataEndSessionURI => 'endSessionDone',
oidcServiceMetaDataCheckSessionURI => 'checkSession',
oidcServiceMetaDataTokenURI => 'token',
oidcServiceMetaDataUserInfoURI => 'userInfo',
oidcServiceMetaDataJWKSURI => 'jwks',
oidcServiceMetaDataRegistrationURI => 'registration',
oidcServiceMetaDataIntrospectionURI => 'introspection',
);
# Manage user requests
$self->addRouteFromConf(
'Auth',
oidcServiceMetaDataCheckSessionURI => 'checkSession',
oidcServiceMetaDataTokenURI => 'badAuthRequest',
oidcServiceMetaDataUserInfoURI => 'badAuthRequest',
oidcServiceMetaDataJWKSURI => 'badAuthRequest',
oidcServiceMetaDataRegistrationURI => 'badAuthRequest',
oidcServiceMetaDataCheckSessionURI => 'checkSession',
oidcServiceMetaDataTokenURI => 'badAuthRequest',
oidcServiceMetaDataUserInfoURI => 'badAuthRequest',
oidcServiceMetaDataJWKSURI => 'badAuthRequest',
oidcServiceMetaDataRegistrationURI => 'badAuthRequest',
oidcServiceMetaDataIntrospectionURI => 'badAuthRequest',
);
# Metadata (.well-known/openid-configuration)
@ -997,50 +1000,13 @@ sub token {
my ( $self, $req ) = @_;
$self->logger->debug("URL detected as an OpenID Connect TOKEN URL");
# Check authentication
my ( $client_id, $client_secret ) =
$self->getEndPointAuthenticationCredentials($req);
unless ($client_id) {
$self->logger->error(
"No authentication provided to get token, or authentication type not supported"
);
return $self->p->sendError( $req, 'invalid_request', 400 );
}
# Verify that client_id is registered in configuration
my $rp = $self->getRP($client_id);
my $rp = $self->checkEndPointAuthenticationCredentials($req);
unless ($rp) {
$self->userLogger->error(
"No registered Relying Party found with client_id $client_id");
return $self->p->sendError( $req, 'invalid_request', 400 );
}
else {
$self->logger->debug("Client id $client_id match Relying Party $rp");
}
# Check client_secret
if ( $self->conf->{oidcRPMetaDataOptions}->{$rp}
->{oidcRPMetaDataOptionsPublic} )
{
$self->logger->debug(
"Relying Party $rp is public, do not check client secret");
}
else {
unless ($client_secret) {
$self->logger->error(
"Relying Party $rp is confidential but no client secret was provided to authenticate on token endpoint"
);
return $self->p->sendError( $req, 'invalid_request', 400 );
}
unless ( $client_secret eq $self->conf->{oidcRPMetaDataOptions}->{$rp}
->{oidcRPMetaDataOptionsClientSecret} )
{
$self->logger->error("Wrong credentials for $rp");
return $self->p->sendError( $req, 'invalid_request', 400 );
}
}
my $client_id = $self->oidcRPList->{$rp}->{oidcRPMetaDataOptionsClientID};
# Get code session
my $code = $req->param('code');
@ -1190,7 +1156,7 @@ sub token {
return $self->p->sendJSONresponse( $req, $token_response );
}
# Handle uerinfo endpoint
# Handle userinfo endpoint
sub userInfo {
my ( $self, $req ) = @_;
$self->logger->debug("URL detected as an OpenID Connect USERINFO URL");
@ -1247,6 +1213,66 @@ sub userInfo {
}
}
sub introspection {
my ( $self, $req ) = @_;
$self->logger->debug("URL detected as an OpenID Connect INTROSPECTION URL");
my $rp = $self->checkEndPointAuthenticationCredentials($req);
unless ($rp) {
return $self->p->sendError( $req, 'invalid_request', 400 );
}
if ( $self->conf->{oidcRPMetaDataOptions}->{$rp}
->{oidcRPMetaDataOptionsPublic} )
{
$self->logger->error(
"Public clients are not allowed to acces the introspection endpoint"
);
return $self->p->sendError( $req, 'unauthorized_client', 401 );
}
my $token = $req->param('token');
unless ($token) {
return $self->p->sendError( $req, 'invalid_request', 400 );
}
my $response = { active => JSON::false };
my $oidcSession = $self->getOpenIDConnectSession($token);
if ($oidcSession) {
if ( my $user_session_id = $oidcSession->{data}->{user_session_id} ) {
# Get user identifier
my $apacheSession = $self->p->getApacheSession($user_session_id);
if ($apacheSession) {
$response->{active} = JSON::true;
# The ID attribute we choose is the one of the calling webservice,
# which might be different from the OIDC client the token was issued to.
my $user_id_attribute =
$self->conf->{oidcRPMetaDataOptions}->{$rp}
->{oidcRPMetaDataOptionsUserIDAttr}
|| $self->conf->{whatToTrace};
$response->{sub} = $apacheSession->data->{$user_id_attribute};
$response->{scope} = $oidcSession->{data}->{scope}
if $oidcSession->{data}->{scope};
$response->{client_id} =
$self->oidcRPList->{ $oidcSession->{data}->{rp} }
->{oidcRPMetaDataOptionsClientID}
if $oidcSession->{data}->{rp};
$response->{exp} =
$oidcSession->{data}->{_utime} + $self->conf->{timeout};
}
}
else {
$self->logger->error(
"Could not find user session ID in access token object");
}
}
return $self->p->sendJSONresponse( $req, $response );
}
# Handle jwks endpoint
sub jwks {
my ( $self, $req ) = @_;
@ -1485,13 +1511,14 @@ sub logout {
sub metadata {
my ( $self, $req ) = @_;
my $issuerDBOpenIDConnectPath = $self->conf->{issuerDBOpenIDConnectPath};
my $authorize_uri = $self->conf->{oidcServiceMetaDataAuthorizeURI};
my $token_uri = $self->conf->{oidcServiceMetaDataTokenURI};
my $userinfo_uri = $self->conf->{oidcServiceMetaDataUserInfoURI};
my $jwks_uri = $self->conf->{oidcServiceMetaDataJWKSURI};
my $registration_uri = $self->conf->{oidcServiceMetaDataRegistrationURI};
my $endsession_uri = $self->conf->{oidcServiceMetaDataEndSessionURI};
my $checksession_uri = $self->conf->{oidcServiceMetaDataCheckSessionURI};
my $authorize_uri = $self->conf->{oidcServiceMetaDataAuthorizeURI};
my $token_uri = $self->conf->{oidcServiceMetaDataTokenURI};
my $userinfo_uri = $self->conf->{oidcServiceMetaDataUserInfoURI};
my $jwks_uri = $self->conf->{oidcServiceMetaDataJWKSURI};
my $registration_uri = $self->conf->{oidcServiceMetaDataRegistrationURI};
my $endsession_uri = $self->conf->{oidcServiceMetaDataEndSessionURI};
my $checksession_uri = $self->conf->{oidcServiceMetaDataCheckSessionURI};
my $introspection_uri = $self->conf->{oidcServiceMetaDataIntrospectionURI};
my $path = $self->path . '/';
my $issuer = $self->iss;
@ -1531,6 +1558,7 @@ sub metadata {
authorization_endpoint => $baseUrl . $authorize_uri,
end_session_endpoint => $baseUrl . $endsession_uri,
check_session_iframe => $baseUrl . $checksession_uri,
introspection_endpoint => $baseUrl . $introspection_uri,
# Logout capabilities
backchannel_logout_supported => JSON::true,
@ -1551,6 +1579,8 @@ sub metadata {
subject_types_supported => ["public"],
token_endpoint_auth_methods_supported =>
[qw/client_secret_post client_secret_basic/],
introspection_endpoint_auth_methods_supported =>
[qw/client_secret_post client_secret_basic/],
claims_supported => [qw/sub iss auth_time acr/],
request_parameter_supported => JSON::true,
request_uri_parameter_supported => JSON::true,

View File

@ -728,7 +728,7 @@ sub getOpenIDConnectSession {
return undef;
}
if ($id) {
if ( $id and $type ) {
my $storedType = $oidcSession->{data}->{_type};
# Only check if a type is set in DB, for backward compatibility
@ -1111,6 +1111,56 @@ sub returnBearerError {
];
}
sub checkEndPointAuthenticationCredentials {
my ( $self, $req ) = @_;
# Check authentication
my ( $client_id, $client_secret ) =
$self->getEndPointAuthenticationCredentials($req);
unless ($client_id) {
$self->logger->error(
"No authentication provided to get token, or authentication type not supported"
);
return undef;
}
# Verify that client_id is registered in configuration
my $rp = $self->getRP($client_id);
unless ($rp) {
$self->userLogger->error(
"No registered Relying Party found with client_id $client_id");
return undef;
}
else {
$self->logger->debug("Client id $client_id match Relying Party $rp");
}
# Check client_secret
if ( $self->conf->{oidcRPMetaDataOptions}->{$rp}
->{oidcRPMetaDataOptionsPublic} )
{
$self->logger->debug(
"Relying Party $rp is public, do not check client secret");
}
else {
unless ($client_secret) {
$self->logger->error(
"Relying Party $rp is confidential but no client secret was provided to authenticate on token endpoint"
);
return undef;
}
unless ( $client_secret eq $self->conf->{oidcRPMetaDataOptions}->{$rp}
->{oidcRPMetaDataOptionsClientSecret} )
{
$self->logger->error("Wrong credentials for $rp");
return undef;
}
}
return $rp;
}
# Get Client ID and Client Secret
# @return array (client_id, client_secret)
sub getEndPointAuthenticationCredentials {

View File

@ -189,6 +189,7 @@ sub display {
elsif ( $req->{error} == PE_REDIRECT ) {
$skinfile = "redirect";
%templateParams = (
MAIN_LOGO => $self->conf->{portalMainLogo},
URL => $req->{urldc},
HIDDEN_INPUTS => $self->buildHiddenForm($req),
FORM_METHOD => $req->data->{redirectFormMethod} || 'get',

View File

@ -1 +0,0 @@
html,body{height:100%;background:radial-gradient(circle at 50% 0,#fff 0,#ddd 100%) no-repeat scroll 0 0 #ddd}#wrap{min-height:100%;height:auto;margin:0 auto -80px;padding:20px 0 80px}#footer{height:80px;background-color:#fff;background-color:rgba(255,255,255,0.9);text-align:center;padding-top:10px;overflow:hidden}#header img{background-color:#fff;background-color:rgba(255,255,255,0.8);margin-bottom:20px}.card,.navbar-light{background-color:#fff;background-color:rgba(255,255,255,0.9);background-image:none}.login,.password{text-align:center;padding:20px}div.form{margin:0 auto;max-width:330px}div.actions{margin:10px 0 0 0}div.actions a{margin-top:10px}.buttons{text-align:center;margin:10px 0 0 0;cursor:pointer}.btn{white-space:normal}.btn span.fa{padding-right:8px}li.ui-state-active{background-color:#fafafa;background-color:rgba(250,250,250,0.9)}#appslist,#password,#loginHistory,#logout,#oidcConsents{margin-top:20px}div.category{margin:10px 0;cursor:grab}div.application{margin:5px 0;overflow:hidden}div.application a,div.application a:hover{text-decoration:none}p.notifCheck label{margin-left:5px;margin-top:3px;display:inline-block}img.langicon{cursor:pointer}button.idploop{max-width:300px}button.idploop img{max-height:30px}div.oidc_consent_message>ul{text-align:left;list-style:circle}@media(min-width:768px){div.application{height:80px}div.application h4.appname{margin:0}#wrap{margin:0 auto -60px}#footer{height:60px}}.hiddenFrame{border:0;display:hidden;margin:0}.noborder{border:0}.max{width:100%}.link{cursor:pointer}.nodecor:hover,.nodecor:active.nodecor:focus{text-decoration:none}.fa.icon-blue{color:blue}.progress-bar-animated{width:100%}

View File

@ -0,0 +1,212 @@
use lib 'inc';
use Test::More;
use strict;
use IO::String;
use LWP::UserAgent;
use LWP::Protocol::PSGI;
use MIME::Base64;
use JSON;
BEGIN {
require 't/test-lib.pm';
}
my $debug = 'error';
# Initialization
my $op = LLNG::Manager::Test->new( {
ini => {
logLevel => $debug,
domain => 'idp.com',
portal => 'http://auth.op.com',
authentication => 'Demo',
userDB => 'Same',
issuerDBOpenIDConnectActivation => 1,
issuerDBOpenIDConnectRule => '$uid eq "french"',
oidcRPMetaDataExportedVars => {
rp => {
email => "mail",
family_name => "cn",
name => "cn"
},
rp2 => {
email => "mail",
family_name => "cn",
name => "cn"
}
},
oidcServiceMetaDataIssuer => "http://auth.op.com",
oidcServiceMetaDataAuthorizeURI => "authorize",
oidcServiceMetaDataCheckSessionURI => "checksession.html",
oidcServiceMetaDataJWKSURI => "jwks",
oidcServiceMetaDataEndSessionURI => "logout",
oidcServiceMetaDataRegistrationURI => "register",
oidcServiceMetaDataTokenURI => "token",
oidcServiceMetaDataUserInfoURI => "userinfo",
oidcServiceAllowHybridFlow => 1,
oidcServiceAllowImplicitFlow => 1,
oidcServiceAllowDynamicRegistration => 1,
oidcServiceAllowAuthorizationCodeFlow => 1,
oidcRPMetaDataOptions => {
rp => {
oidcRPMetaDataOptionsDisplayName => "RP",
oidcRPMetaDataOptionsIDTokenExpiration => 3600,
oidcRPMetaDataOptionsClientID => "rpid",
oidcRPMetaDataOptionsIDTokenSignAlg => "HS512",
oidcRPMetaDataOptionsClientSecret => "rpsecret",
oidcRPMetaDataOptionsUserIDAttr => "",
oidcRPMetaDataOptionsAccessTokenExpiration => 1,
oidcRPMetaDataOptionsBypassConsent => 1,
},
oauth => {
oidcRPMetaDataOptionsDisplayName => "oauth",
oidcRPMetaDataOptionsClientID => "oauth",
oidcRPMetaDataOptionsClientSecret => "service",
oidcRPMetaDataOptionsUserIDAttr => "",
}
},
oidcOPMetaDataOptions => {},
oidcOPMetaDataJSON => {},
oidcOPMetaDataJWKS => {},
oidcServiceMetaDataAuthnContext => {
'loa-4' => 4,
'loa-1' => 1,
'loa-5' => 5,
'loa-2' => 2,
'loa-3' => 3
},
oidcServicePrivateKeySig => "-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAs2jsmIoFuWzMkilJaA8//5/T30cnuzX9GImXUrFR2k9EKTMt
GMHCdKlWOl3BV+BTAU9TLz7Jzd/iJ5GJ6B8TrH1PHFmHpy8/qE/S5OhinIpIi7eb
ABqnoVcwDdCa8ugzq8k8SWxhRNXfVIlwz4NH1caJ8lmiERFj7IvNKqEhzAk0pyDr
8hubveTC39xREujKlsqutpPAFPJ3f2ybVsdykX5rx0h5SslG3jVWYhZ/SOb2aIzO
r0RMjhQmsYRwbpt3anjlBZ98aOzg7GAkbO8093X5VVk9vaPRg0zxJQ0Do0YLyzkR
isSAIFb0tdKuDnjRGK6y/N2j6At2HjkxntbtGQIDAQABAoIBADYq6LxJd977LWy3
0HT9nboFPIf+SM2qSEc/S5Po+6ipJBA4ZlZCMf7dHa6znet1TDpqA9iQ4YcqIHMH
6xZNQ7hhgSAzG9TrXBHqP+djDlrrGWotvjuy0IfS9ixFnnLWjrtAH9afRWLuG+a/
NHNC1M6DiiTE0TzL/lpt/zzut3CNmWzH+t19X6UsxUg95AzooEeewEYkv25eumWD
mfQZfCtSlIw1sp/QwxeJa/6LJw7KcPZ1wXUm1BN0b9eiKt9Cmni1MS7elgpZlgGt
xtfGTZtNLQ7bgDiM8MHzUfPBhbceNSIx2BeCuOCs/7eaqgpyYHBbAbuBQex2H61l
Lcc3Tz0CgYEA4Kx/avpCPxnvsJ+nHVQm5d/WERuDxk4vH1DNuCYBvXTdVCGADf6a
F5No1JcTH3nPTyPWazOyGdT9LcsEJicLyD8vCM6hBFstG4XjqcAuqG/9DRsElpHQ
yi1zc5DNP7Vxmiz9wII0Mjy0abYKtxnXh9YK4a9g6wrcTpvShhIcIb8CgYEAzGzG
lorVCfX9jXULIznnR/uuP5aSnTEsn0xJeqTlbW0RFWLdj8aIL1peirh1X89HroB9
GeTNqEJXD+3CVL2cx+BRggMDUmEz4hR59meZCDGUyT5fex4LIsceb/ESUl2jo6Sw
HXwWbN67rQ55N4oiOcOppsGxzOHkl5HdExKidycCgYEAr5Qev2tz+fw65LzfzHvH
Kj4S/KuT/5V6He731cFd+sEpdmX3vPgLVAFPG1Q1DZQT/rTzDDQKK0XX1cGiLG63
NnaqOye/jbfzOF8Z277kt51NFMDYhRLPKDD82IOA4xjY/rPKWndmcxwdob8yAIWh
efY76sMz6ntCT+xWSZA9i+ECgYBWMZM2TIlxLsBfEbfFfZewOUWKWEGvd9l5vV/K
D5cRIYivfMUw5yPq2267jPUolayCvniBH4E7beVpuPVUZ7KgcEvNxtlytbt7muil
5Z6X3tf+VodJ0Swe2NhTmNEB26uwxzLe68BE3VFCsbSYn2y48HAq+MawPZr18bHG
ZfgMxwKBgHHRg6HYqF5Pegzk1746uH2G+OoCovk5ylGGYzcH2ghWTK4agCHfBcDt
EYqYAev/l82wi+OZ5O8U+qjFUpT1CVeUJdDs0o5u19v0UJjunU1cwh9jsxBZAWLy
PAGd6SWf4S3uQCTw6dLeMna25YIlPh5qPA6I/pAahe8e3nSu2ckl
-----END RSA PRIVATE KEY-----
",
oidcServicePublicKeySig => "-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs2jsmIoFuWzMkilJaA8/
/5/T30cnuzX9GImXUrFR2k9EKTMtGMHCdKlWOl3BV+BTAU9TLz7Jzd/iJ5GJ6B8T
rH1PHFmHpy8/qE/S5OhinIpIi7ebABqnoVcwDdCa8ugzq8k8SWxhRNXfVIlwz4NH
1caJ8lmiERFj7IvNKqEhzAk0pyDr8hubveTC39xREujKlsqutpPAFPJ3f2ybVsdy
kX5rx0h5SslG3jVWYhZ/SOb2aIzOr0RMjhQmsYRwbpt3anjlBZ98aOzg7GAkbO80
93X5VVk9vaPRg0zxJQ0Do0YLyzkRisSAIFb0tdKuDnjRGK6y/N2j6At2Hjkxntbt
GQIDAQAB
-----END PUBLIC KEY-----
",
}
}
);
my $res;
# Authenticate to LLNG
my $url = "/";
my $query = "user=french&password=french";
ok(
$res = $op->_post(
"/",
IO::String->new($query),
accept => 'text/html',
length => length($query),
),
"Post authentication"
);
my $idpId = expectCookie($res);
# Get code for RP1
my $query =
"response_type=code&scope=openid%20profile%20email&client_id=rpid&state=af0ifjsldkj&redirect_uri=http%3A%2F%2Frp2.com%2F";
ok(
$res = $op->_get(
"/oauth2/authorize",
query => "$query",
accept => 'text/html',
cookie => "lemonldap=$idpId",
),
"Get authorization code"
);
my ($code) = expectRedirection( $res, qr#http://rp2\.com/.*code=([^\&]*)# );
# Exchange code for AT
$query =
"grant_type=authorization_code&code=$code&redirect_uri=http%3A%2F%2Frp2.com%2F";
ok(
$res = $op->_post(
"/oauth2/token",
IO::String->new($query),
accept => 'text/html',
length => length($query),
custom => {
HTTP_AUTHORIZATION => "Basic " . encode_base64("rpid:rpsecret"),
},
),
"Post token"
);
my $json = from_json( $res->[2]->[0] );
my $token = $json->{access_token};
ok( $token, 'Access token present' );
my $query = "token=$token";
ok(
$res = $op->_post(
"/oauth2/introspect",
IO::String->new($query),
accept => 'text/html',
length => length $query,
custom => {
HTTP_AUTHORIZATION => "Basic " . encode_base64("oauth:service"),
},
),
"Post introspection"
);
expectOK($res);
my $json = from_json( $res->[2]->[0] );
ok( $json->{active}, "Token is valid" );
is( $json->{sub}, "french", "Response contains the correct sub" );
# Check status after expiration
sleep(2);
$query = "token=$token";
ok(
$res = $op->_post(
"/oauth2/introspect",
IO::String->new($query),
accept => 'text/html',
length => length $query,
custom => {
HTTP_AUTHORIZATION => "Basic " . encode_base64("oauth:service"),
},
),
"Post introspection"
);
expectOK($res);
$json = from_json( $res->[2]->[0] );
ok( !$json->{active}, "Token is no longer valid" );
clean_sessions();
done_testing();

View File

@ -26,7 +26,7 @@ SKIP: {
$client = iniCmb('[Dm] and [DB]');
expectCookie( try('rtyler') );
expectReject( try('dwho'), 5 );
expectReject( try('dwho'), 401, 5 );
$client = iniCmb('if($env->{HTTP_X} eq "dwho") then [Dm] else [DB]');
expectCookie( try('dwho') );
@ -37,7 +37,7 @@ SKIP: {
);
expectCookie( try('rtyler') );
expectCookie( try('dvador') );
expectReject( try('dwho'), 5 );
expectReject( try('dwho'), 401, 5 );
}
count($maintests);
clean_sessions();

View File

@ -344,14 +344,14 @@ Note that it works only for Ajax request (see below).
=cut
sub expectReject {
my ( $res, $code ) = @_;
ok( $res->[0] == 401, ' Response is 401' ) or explain( $res->[0], 401 );
my ( $res, $status, $code ) = @_;
$status ||= 401;
cmp_ok( $res->[0], '==', $status, " Response status is $status" );
eval { $res = JSON::from_json( $res->[2]->[0] ) };
ok( not($@), 'Content is JSON' )
ok( not($@), ' Content is JSON' )
or explain( $res->[2]->[0], 'JSON content' );
if ( defined $code ) {
ok( $res->{error} == $code, "Error code is $code" )
or explain( $res->{error}, $code );
is( $res->{error}, $code, " Error code is $code" );
}
else {
pass("Error code is $res->{error}");
@ -696,9 +696,8 @@ sub _get {
$args{remote_user} ? ( 'REMOTE_USER' => $args{remote_user} )
: ()
),
'REQUEST_METHOD' => $args{method}
|| 'GET',
'REQUEST_URI' => $path . ( $args{query} ? "?$args{query}" : '' ),
'REQUEST_METHOD' => $args{method} || 'GET',
'REQUEST_URI' => $path . ( $args{query} ? "?$args{query}" : '' ),
( $args{query} ? ( QUERY_STRING => $args{query} ) : () ),
'SCRIPT_NAME' => '',
'SERVER_NAME' => 'auth.example.com',