Merge branch 'v2.0'

This commit is contained in:
Xavier Guimard 2019-03-14 14:57:39 +01:00
commit 8941ee4dd5
45 changed files with 2295 additions and 108 deletions

View File

@ -29,6 +29,7 @@ sub defaultValues {
'casAuthnLevel' => 1,
'checkTime' => 600,
'checkUserHiddenAttributes' => '_2fDevices _loginHistory hGroups',
'checkUserIdRule' => 1,
'checkXSS' => 1,
'confirmFormMethod' => 'post',
'cookieName' => 'lemonldap',
@ -71,6 +72,7 @@ sub defaultValues {
'httpOnly' => 1,
'https' => -1,
'impersonationHiddenAttributes' => '_2fDevices _loginHistory',
'impersonationIdRule' => 1,
'impersonationPrefix' => 'real_',
'impersonationRule' => 0,
'impersonationSkipEmptyValues' => 1,
@ -245,6 +247,7 @@ sub defaultValues {
'samlOrganizationDisplayName' => 'Example',
'samlOrganizationName' => 'Example',
'samlOrganizationURL' => 'http://www.example.com',
'samlOverrideIDPEntityID' => '',
'samlRelayStateTimeout' => 600,
'samlServiceSignatureMethod' => 'RSA_SHA1',
'samlSPSSODescriptorArtifactResolutionServiceArtifact' =>

View File

@ -66,7 +66,7 @@ our $issuerParameters = {
issuerDBOpenIDConnect => [qw(issuerDBOpenIDConnectActivation issuerDBOpenIDConnectPath issuerDBOpenIDConnectRule)],
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)];
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)];
1;

View File

@ -53,6 +53,11 @@ sub serviceToXML {
$template->param( $_, $self->getValue( $_, $conf ) );
}
# When asked to provide only IDP metadata, take into account EntityID override
if ( $type eq "idp" and $conf->{samlOverrideIDPEntityID} ) {
$template->param( 'samlEntityID', $conf->{samlOverrideIDPEntityID} );
}
# Boolean parameters
my @param_boolean = qw(
samlSPSSODescriptorAuthnRequestsSigned

View File

@ -783,6 +783,21 @@ qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.]
'default' => '_2fDevices _loginHistory hGroups',
'type' => 'text'
},
'checkUserIdRule' => {
'default' => 1,
'test' => sub {
my ( $val, $conf ) = @_;
my $s = '';
'Safe'->new->reval("BEGIN { warnings->unimport; } $s $val");
my $err = join(
'',
grep( { $_ =~ /Undefined subroutine/ ? () : $_; }
split( /\n/, $@, 0 ) )
);
return $err ? ( 1, "__badExpression__: $err" ) : 1;
},
'type' => 'text'
},
'checkXSS' => {
'default' => 1,
'type' => 'bool'
@ -1199,6 +1214,21 @@ qr/^(?:\*\.)?(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.])*(?:[a-zA-Z][
'default' => '_2fDevices _loginHistory',
'type' => 'text'
},
'impersonationIdRule' => {
'default' => 1,
'test' => sub {
my ( $val, $conf ) = @_;
my $s = '';
'Safe'->new->reval("BEGIN { warnings->unimport; } $s $val");
my $err = join(
'',
grep( { $_ =~ /Undefined subroutine/ ? () : $_; }
split( /\n/, $@, 0 ) )
);
return $err ? ( 1, "__badExpression__: $err" ) : 1;
},
'type' => 'text'
},
'impersonationMergeSSOgroups' => {
'default' => 0,
'type' => 'bool'
@ -2885,6 +2915,10 @@ qr/(?:(?:https?):\/\/(?:(?:(?:(?:(?:(?:[a-zA-Z0-9][-a-zA-Z0-9]*)?[a-zA-Z0-9])[.]
'default' => 'http://www.example.com',
'type' => 'text'
},
'samlOverrideIDPEntityID' => {
'default' => '',
'type' => 'text'
},
'samlRelayStateTimeout' => {
'default' => 600,
'type' => 'int'

View File

@ -422,6 +422,12 @@ sub attributes {
documentation => 'Enable check user',
flags => 'p',
},
checkUserIdRule => {
type => 'text',
test => $perlExpr,
default => 1,
documentation => 'checkUser identities rule',
},
checkUserHiddenAttributes => {
type => 'text',
default => '_2fDevices _loginHistory hGroups',
@ -457,6 +463,12 @@ sub attributes {
default => 0,
documentation => 'Impersonation activation rule',
},
impersonationIdRule => {
type => 'text',
test => $perlExpr,
default => 1,
documentation => 'Impersonation identities rule',
},
impersonationHiddenAttributes => {
type => 'text',
default => '_2fDevices _loginHistory',
@ -2021,6 +2033,11 @@ sub attributes {
default => 600,
documentation => 'SAML timeout of relay state',
},
samlOverrideIDPEntityID => {
type => 'text',
documentation => 'Override SAML EntityID when acting as an IDP',
default => '',
},
samlUseQueryStringSpecific => {
default => 0,
type => 'bool',

View File

@ -642,6 +642,7 @@ sub tree {
form => 'simpleInputContainer',
nodes => [
'checkUser',
'checkUserIdRule',
'checkUserHiddenAttributes',
'checkUserDisplayPersistentInfo',
'checkUserDisplayEmptyValues',
@ -653,6 +654,7 @@ sub tree {
form => 'simpleInputContainer',
nodes => [
'impersonationRule',
'impersonationIdRule',
'impersonationPrefix',
'impersonationHiddenAttributes',
'impersonationSkipEmptyValues',
@ -1008,7 +1010,8 @@ sub tree {
'samlDiscoveryProtocolPolicy',
'samlDiscoveryProtocolIsPassive'
]
}
},
'samlOverrideIDPEntityID',
]
}
]

View File

@ -153,6 +153,7 @@
"checkStateSecret":"Shared secret",
"checkUsers":"SSO profile Check",
"checkUser":"Activation",
"checkUserIdRule":"Identities use rule",
"checkUserHiddenAttributes":"Hidden attributes",
"checkUserDisplayPersistentInfo":"Display persistent session",
"checkUserDisplayEmptyValues":"Display empty values",
@ -288,6 +289,7 @@
"https":"إتش تي تي بي س",
"impersonation":"Impersonation",
"impersonationRule":"Use rule",
"impersonationIdRule":"Identities use rule",
"impersonationHiddenAttributes":"Hidden attributes",
"impersonationMergeSSOgroups":"Merge spoofed and real SSO groups",
"impersonationPrefix":"Real attributes prefix",
@ -975,5 +977,6 @@
"samlCommonDomainCookieReader":"يو آر إل القارئ",
"samlCommonDomainCookieWriter":"يو آر إل الكاتب",
"samlRelayStateTimeout":"تناوب حالة مهلة الجلسة ",
"samlUseQueryStringSpecific":"استخدام أسلوب query_string المعين"
}
"samlUseQueryStringSpecific":"استخدام أسلوب query_string المعين",
"samlOverrideIDPEntityID": "Override Entity ID when acting as IDP"
}

View File

@ -152,11 +152,12 @@
"checkState":"Activation",
"checkStateSecret":"Shared secret",
"checkUsers":"SSO profile Check",
"choiceParams":"Choice parameters",
"checkUser":"Activation",
"checkUserIdRule":"Identities use rule",
"checkUserHiddenAttributes":"Hidden attributes",
"checkUserDisplayPersistentInfo":"Display persistent session",
"checkUserDisplayEmptyValues":"Display empty values",
"choiceParams":"Choice parameters",
"chooseLogo":"Choose logo",
"chooseSkin":"Choose skin",
"combination":"Combination",
@ -288,6 +289,7 @@
"https":"HTTPS",
"impersonation":"Impersonation",
"impersonationRule":"Use rule",
"impersonationIdRule":"Identities use rule",
"impersonationHiddenAttributes":"Hidden attributes",
"impersonationMergeSSOgroups":"Merge spoofed and real SSO groups",
"impersonationPrefix":"Real attributes prefix",
@ -975,5 +977,6 @@
"samlCommonDomainCookieReader":"Reader URL",
"samlCommonDomainCookieWriter":"Writer URL",
"samlRelayStateTimeout":"RelayState session timeout",
"samlUseQueryStringSpecific":"Use specific query_string method"
}
"samlUseQueryStringSpecific":"Use specific query_string method",
"samlOverrideIDPEntityID": "Override Entity ID when acting as IDP"
}

View File

@ -153,6 +153,7 @@
"checkStateSecret":"Shared secret",
"checkUsers":"SSO profile Check",
"checkUser":"Activation",
"checkUserIdRule":"Identities use rule",
"checkUserHiddenAttributes":"Hidden attributes",
"checkUserDisplayPersistentInfo":"Display persistent session",
"checkUserDisplayEmptyValues":"Display empty values",
@ -288,6 +289,7 @@
"https":"HTTPS",
"impersonation":"Impersonation",
"impersonationRule":"Use rule",
"impersonationIdRule":"Identities use rule",
"impersonationHiddenAttributes":"Hidden attributes",
"impersonationMergeSSOgroups":"Merge spoofed and real SSO groups",
"impersonationPrefix":"Real attributes prefix",
@ -975,5 +977,6 @@
"samlCommonDomainCookieReader":"Reader URL",
"samlCommonDomainCookieWriter":"Writer URL",
"samlRelayStateTimeout":"RelayState session timeout",
"samlUseQueryStringSpecific":"Use specific query_string method"
"samlUseQueryStringSpecific":"Use specific query_string method",
"samlOverrideIDPEntityID": "Override Entity ID when acting as IDP"
}

View File

@ -153,6 +153,7 @@
"checkStateSecret":"Secret partagé",
"checkUsers":"Vérification des profils SSO",
"checkUser":"Activation",
"checkUserIdRule":"Règle d'utilisation des identités",
"checkUserHiddenAttributes":"Attributs masqués",
"checkUserDisplayPersistentInfo":"Afficher les données de session persistante",
"checkUserDisplayEmptyValues":"Afficher les valeurs nulles",
@ -288,6 +289,7 @@
"https":"HTTPS",
"impersonation":"Usurpation d'identité",
"impersonationRule":"Règle d'utilisation",
"impersonationIdRule":"Règle d'utilisation des identités",
"impersonationHiddenAttributes":"Attributs masqués",
"impersonationMergeSSOgroups":"Fusionner les groupes SSO réels et usurpés",
"impersonationPrefix":"Préfix des vrais attributs",
@ -975,5 +977,6 @@
"samlCommonDomainCookieReader":"URL de lecture",
"samlCommonDomainCookieWriter":"URL d'écriture",
"samlRelayStateTimeout":"Durée de vie d'une session RelayState",
"samlUseQueryStringSpecific":"Utilisation d'une fonction spécifique pour query_string"
"samlUseQueryStringSpecific":"Utilisation d'une fonction spécifique pour query_string",
"samlOverrideIDPEntityID": "Valeur de l'Entity ID en mode IDP"
}

View File

@ -153,6 +153,7 @@
"checkStateSecret":"Segreto condiviso",
"checkUsers":"SSO profile Check",
"checkUser":"Activation",
"checkUserIdRule":"Identities use rule",
"checkUserHiddenAttributes":"Hidden attributes",
"checkUserDisplayPersistentInfo":"Display persistent session",
"checkUserDisplayEmptyValues":"Display empty values",
@ -288,6 +289,7 @@
"https":"HTTPS",
"impersonation":"Impersonation",
"impersonationRule":"Use rule",
"impersonationIdRule":"Identities use rule",
"impersonationHiddenAttributes":"Hidden attributes",
"impersonationMergeSSOgroups":"Merge spoofed and real SSO groups",
"impersonationPrefix":"Real attributes prefix",
@ -975,5 +977,6 @@
"samlCommonDomainCookieReader":"URL del lettore",
"samlCommonDomainCookieWriter":"URL dell'autore",
"samlRelayStateTimeout":"Timeout di sessione di RelayState",
"samlUseQueryStringSpecific":"Utilizza il metodo specifico query_string"
}
"samlUseQueryStringSpecific":"Utilizza il metodo specifico query_string",
"samlOverrideIDPEntityID": "Override Entity ID when acting as IDP"
}

View File

@ -151,8 +151,9 @@
"clickHereToForce":"Nhấp vào đây để bắt buộc",
"checkState":"Kích hoạt",
"checkStateSecret":"Shared secret",
"checkUsers":"Session Check",
"checkUsers":"SSO profile Check",
"checkUser":"Activation",
"checkUserIdRule":"Identities use rule",
"checkUserHiddenAttributes":"Hidden attributes",
"checkUserDisplayPersistentInfo":"Display persistent session",
"checkUserDisplayEmptyValues":"Display empty values",
@ -288,6 +289,7 @@
"https":"HTTPS",
"impersonation":"Impersonation",
"impersonationRule":"Use rule",
"impersonationIdRule":"Identities use rule",
"impersonationHiddenAttributes":"Hidden attributes",
"impersonationMergeSSOgroups":"Merge spoofed and real SSO groups",
"impersonationPrefix":"Real attributes prefix",
@ -975,5 +977,6 @@
"samlCommonDomainCookieReader":"Trình đọc URL",
"samlCommonDomainCookieWriter":"Trình viết URL",
"samlRelayStateTimeout":"Thời gian hết hạn phiên RelayState ",
"samlUseQueryStringSpecific":"Sử dụng phương pháp query_string cụ thể"
}
"samlUseQueryStringSpecific":"Sử dụng phương pháp query_string cụ thể",
"samlOverrideIDPEntityID": "Override Entity ID when acting as IDP"
}

View File

@ -153,6 +153,7 @@
"checkStateSecret":"Shared secret",
"checkUsers":"SSO profile Check",
"checkUser":"Activation",
"checkUserIdRule":"Identities use rule",
"checkUserHiddenAttributes":"Hidden attributes",
"checkUserDisplayPersistentInfo":"Display persistent session",
"checkUserDisplayEmptyValues":"Display empty values",
@ -288,6 +289,7 @@
"https":"HTTPS",
"impersonation":"Impersonation",
"impersonationRule":"Use rule",
"impersonationIdRule":"Identities use rule",
"impersonationHiddenAttributes":"Hidden attributes",
"impersonationMergeSSOgroups":"Merge spoofed and real SSO groups",
"impersonationPrefix":"Real attributes prefix",
@ -975,5 +977,6 @@
"samlCommonDomainCookieReader":"Reader URL",
"samlCommonDomainCookieWriter":"Writer URL",
"samlRelayStateTimeout":"RelayState session timeout",
"samlUseQueryStringSpecific":"Use specific query_string method"
}
"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

@ -114,6 +114,7 @@ sub extractFormInfo {
#$self->p->setHiddenFormValue( $req, kerberos => 0, '', 0 );
eval( $self->InitCmd );
die 'Unable to launch init commmand ' . $self->{InitCmd} if ($@);
$req->data->{waitingMessage} = 1;
return PE_FIRSTACCESS;
}
}

View File

@ -893,6 +893,10 @@ sub extractFormInfo {
$self->logger->debug(
"Will try to use SAML Discovery Protocol for IDP resolution");
if ($req->urldc) {
$req->pdata->{_url} = encode_base64($req->urldc, '');
}
my $disco_url = $self->conf->{samlDiscoveryProtocolURL};
my $portal = $self->conf->{portal};

View File

@ -51,6 +51,7 @@ sub extractFormInfo {
$req->data->{customScript} .= $self->{AjaxInitScript};
$self->logger->debug(
"Send init/script -> " . $req->data->{customScript} );
$req->data->{waitingMessage} = 1;
return PE_FIRSTACCESS;
}
else {

View File

@ -91,6 +91,11 @@ qr/^($saml_sso_get_url|$saml_sso_get_url_ret|$saml_sso_post_url|$saml_sso_post_u
);
return 0 unless ($res);
if ( $self->conf->{samlOverrideIDPEntityID} ) {
$self->lassoServer->ProviderID(
$self->conf->{samlOverrideIDPEntityID} );
}
# Single logout routes
$self->addUnauthRouteFromMetaDataURL(
"samlIDPSSODescriptorSingleLogoutServiceSOAP",
@ -165,6 +170,8 @@ sub storeEnv {
$self->checkMessage( $req, $req->uri, $req->method, $req->content_type );
return PE_OK if ( $artifact or !$request );
my $login = $self->createLogin( $self->lassoServer );
$self->disableSignatureVerification($login);
$self->processAuthnRequestMsg( $login, $request );
if ( my $sp = $login->remote_providerID() ) {
$req->env->{llng_saml_sp} = $sp;
if ( my $spConfKey = $self->spList->{$sp}->{confKey} ) {
@ -1924,7 +1931,16 @@ sub imgnok {
sub sendImage {
my ( $self, $req,, $img ) = @_;
return $self->p->staticFile( $req, "common/$img", 'image/png' );
return [
302,
[
'Location' => $self->conf->{portal}
. $self->p->staticPrefix
. '/common/'
. $img,
],
[],
];
}
# Normalize url to be tolerant to SAML Path

View File

@ -186,7 +186,7 @@ sub loadService {
# Create Lasso server with service metadata
my $server = $self->createServer(
$service_metadata->serviceToXML( $self->conf ),
$service_metadata->serviceToXML( $self->conf, ''),
$self->conf->{samlServicePrivateKeySig},
$self->conf->{samlServicePrivateKeySigPwd},
@ -2385,6 +2385,18 @@ sub samldate2timestamp {
sub sendLogoutResponseToServiceProvider {
my ( $self, $req, $logout, $method ) = @_;
my $httpmethod = $self->getFirstHttpMethod(
$self->lassoServer,
$logout->remote_providerID,
Lasso::Constants::MD_PROTOCOL_TYPE_SINGLE_LOGOUT
);
if ( $httpmethod == Lasso::Constants::HTTP_METHOD_NONE ) {
$self->logger->warn( "Provider "
. $logout->remote_providerID
. " has no SingleLogoutService in metadata, staying on portal" );
return $self->p->do( $req, [] );
}
# Logout response
unless ( $self->buildLogoutResponseMsg($logout) ) {
return $self->p->sendError( $req, "Unable to build SLO response", 500 );
@ -3074,7 +3086,7 @@ sub importRealSession {
sub metadata {
my ( $self, $req ) = @_;
my $type = $req->param('type');
my $type = $req->param('type') || 'all';
require Lemonldap::NG::Common::Conf::SAML::Metadata;
if ( my $metadata = Lemonldap::NG::Common::Conf::SAML::Metadata->new() ) {
my $s = $metadata->serviceToXML( $self->conf, $type );

View File

@ -113,7 +113,7 @@ sub display {
&& $req->data->{login},
ASK_LOGINS => $req->param('checkLogins') || 0,
CONFIRMKEY => $self->stamp(),
LIST => $req->data->{list} || [],
LIST => $req->data->{list} || [],
REMEMBER => $req->data->{confirmRemember},
(
$req->data->{customScript}
@ -420,6 +420,10 @@ sub display {
}
if ( $req->data->{waitingMessage} ) {
$templateParams{WAITING_MESSAGE} = 1;
}
$self->logger->debug("Skin returned: $skinfile");
return ( $skinfile, \%templateParams );
}

View File

@ -351,7 +351,7 @@ sub reloadConf {
}
};
my $portal = $self->conf->{portal};
$portal =~ s#^https?://(.*?)(?:/|$)/#$1#;
$portal =~ s#^https?://(.*?)(?:[:/].*)?$#$1#;
HANDLER->tsv->{defaultCondition}->{$portal} ||= sub { 1 };
1;

View File

@ -25,6 +25,7 @@ has ott => (
return $ott;
}
);
has idRule => ( is => 'rw', default => sub { 1 } );
sub hAttr {
$_[0]->{conf}->{checkUserHiddenAttributes} . ' '
@ -33,8 +34,22 @@ sub hAttr {
sub init {
my ($self) = @_;
my $hd = $self->p->HANDLER;
$self->addAuthRoute( checkuser => 'check', ['POST'] );
$self->addAuthRoute( checkuser => 'display', ['GET'] );
# Parse identity rule
$self->logger->debug(
"checkUser identities rule -> " . $self->conf->{checkUserIdRule} );
my $rule =
$hd->buildSub( $hd->substitute( $self->conf->{checkUserIdRule} ) );
unless ($rule) {
$self->error(
"Bad checkUser identities rule -> " . $hd->tsv->{jail}->error );
return 0;
}
$self->{idRule} = $rule;
return 1;
}
@ -49,12 +64,12 @@ sub check {
if ( $self->conf->{requireToken} ) {
my $token = $req->param('token');
unless ($token) {
$self->userLogger->warn('CheckUser try without token');
$self->userLogger->warn('checkUser try without token');
$msg = PE_NOTOKEN;
$token = $self->ott->createToken( $req->userData );
}
unless ( $self->ott->getToken($token) ) {
$self->userLogger->warn('Checkuser try with expired/bad token');
$self->userLogger->warn('checkUser try with expired/bad token');
$msg = PE_TOKENEXPIRED;
$token = $self->ott->createToken( $req->userData );
}
@ -64,6 +79,7 @@ sub check {
LANGS => $self->conf->{showLanguages},
MSG => "PE$msg",
ALERTE => 'alert-warning',
LOGIN => '',
TOKEN => $token,
};
return $self->p->sendJSONresponse( $req, $params )
@ -91,7 +107,7 @@ sub check {
LANGS => $self->conf->{showLanguages},
MSG => 'PE' . PE_MALFORMEDUSER,
ALERTE => 'alert-warning',
LOGIN => $req->{user},
LOGIN => '',
TOKEN => (
$self->conf->{requireToken}
? $self->ott->createToken( $req->userData )
@ -108,7 +124,7 @@ sub check {
$attrs = $req->userData;
}
else {
$self->logger->debug("Check requested for $req->{user}");
$self->logger->debug("checkUser requested for $req->{user}");
$req->{user} = $user;
$self->userLogger->notice(
"Retrieve session from userDB and compute Groups & Macros");
@ -183,8 +199,8 @@ sub check {
MSG => $msg,
ALERTE => ( $msg eq 'checkUser' ? 'alert-info' : 'alert-warning' ),
LOGIN => (
$self->p->checkXSSAttack( 'LOGIN', $req->{user} ) ? ""
: $req->{user}
$self->p->checkXSSAttack( 'LOGIN', $req->{userData}->{uid} ) ? ""
: $req->{userData}->{uid}
),
URL => (
$self->p->checkXSSAttack( 'URL', $url ) ? ""
@ -210,19 +226,47 @@ sub check {
sub display {
my ( $self, $req ) = @_;
my ( $attrs, $array_attrs ) = ( {}, [] );
$self->userLogger->notice("Retrieve session from Sessions database");
$self->userLogger->warn("Using spoofed SSO groups if exist!!!")
if ( $self->conf->{impersonationRule} );
$attrs = $req->userData;
# Create an array of hashes for template loop
$self->logger->debug("Delete hidden or empty attributes");
if ( $self->conf->{checkUserDisplayEmptyValues} ) {
foreach my $k ( sort keys %$attrs ) {
# Ignore hidden attributes
push @$array_attrs, { key => $k, value => $attrs->{$k} }
unless ( $self->hAttr =~ /\b$k\b/ );
}
}
else {
foreach my $k ( sort keys %$attrs ) {
# Ignore hidden attributes and empty values
push @$array_attrs, { key => $k, value => $attrs->{$k} }
unless ( $self->hAttr =~ /\b$k\b/ or !$attrs->{$k} );
}
}
# ARRAY_REF = [ A_REF GROUPS, A_REF MACROS, A_REF OTHERS ]
$array_attrs = $self->_splitAttributes($array_attrs);
# Display form
my $params = {
PORTAL => $self->conf->{portal},
MAIN_LOGO => $self->conf->{portalMainLogo},
LANGS => $self->conf->{showLanguages},
MSG => 'checkUser',
ALERTE => 'alert-info',
LOGIN => (
$self->p->checkXSSAttack( 'LOGIN', $req->{user} ) ? ""
: $req->{user}
),
TOKEN => (
PORTAL => $self->conf->{portal},
MAIN_LOGO => $self->conf->{portalMainLogo},
LANGS => $self->conf->{showLanguages},
MSG => 'checkUser',
ALERTE => 'alert-info',
LOGIN => $req->{userData}->{uid},
ATTRIBUTES => $array_attrs->[2],
MACROS => $array_attrs->[1],
GROUPS => $array_attrs->[0],
TOKEN => (
$self->conf->{requireToken}
? $self->ott->createToken( $req->userData )
: ''
@ -244,11 +288,24 @@ sub _userDatas {
if ( my $error = $self->p->process($req) ) {
if ( $error == PE_BADCREDENTIALS ) {
$self->userLogger->warn(
'Check requested for an unvalid user (' . $req->{user} . ")" );
'checkUser requested for an unvalid user ('
. $req->{user}
. ")" );
}
$self->logger->debug("Process returned error: $error");
return $req->error($error);
}
# Check identities rule
unless ( $self->idRule->( $req, $req->sessionInfo ) ) {
$self->userLogger->warn(
'checkUser requested for an unvalid user (' . $req->{user} . ")" );
$req->{sessionInfo} = {};
$self->logger->debug('Identity not authorized');
return $req->error(PE_BADCREDENTIALS);
}
$self->logger->debug("Return \"$req->{user}\" sessionInfo");
return $req->{sessionInfo};
}
@ -266,6 +323,8 @@ sub _authorization {
last;
}
}
$self->logger->debug("Return \"$req->{user}\" authorization");
return $exist
? $self->p->HANDLER->grant( $req, $req->{userData}, $appuri,
undef, $vhost )
@ -278,6 +337,8 @@ sub _headers {
$vhost =~ s/:\d+$//;
$req->{env}->{HTTP_HOST} = $vhost;
$self->p->HANDLER->headersInit( $self->{conf} );
$self->logger->debug("Return \"$req->{user}\" headers");
return $self->p->HANDLER->checkHeaders( $req, $req->{userData} );
}

View File

@ -13,7 +13,8 @@ extends 'Lemonldap::NG::Portal::Main::Plugin';
use constant endAuth => 'run';
has rule => ( is => 'rw', default => sub { 1 } );
has rule => ( is => 'rw', default => sub { 1 } );
has idRule => ( is => 'rw', default => sub { 1 } );
sub hAttr {
$_[0]->{conf}->{impersonationHiddenAttributes} . ' '
@ -22,11 +23,11 @@ sub hAttr {
sub init {
my ($self) = @_;
my $hd = $self->p->HANDLER;
# Parse activation rule
my $hd = $self->p->HANDLER;
$self->logger->debug(
"impersonation rule -> " . $self->conf->{impersonationRule} );
"Impersonation rule -> " . $self->conf->{impersonationRule} );
my $rule =
$hd->buildSub( $hd->substitute( $self->conf->{impersonationRule} ) );
unless ($rule) {
@ -34,6 +35,19 @@ sub init {
return 0;
}
$self->{rule} = $rule;
# Parse identity rule
$self->logger->debug( "Impersonation identities rule -> "
. $self->conf->{impersonationIdRule} );
$rule =
$hd->buildSub( $hd->substitute( $self->conf->{impersonationIdRule} ) );
unless ($rule) {
$self->error(
"Bad impersonation identities rule -> " . $hd->tsv->{jail}->error );
return 0;
}
$self->{idRule} = $rule;
return 1;
}
@ -41,26 +55,30 @@ sub init {
sub run {
my ( $self, $req ) = @_;
my $spoofId = $req->param('spoofId') || $req->{user};
$self->logger->debug("No impersonation required") if ( $spoofId eq $req->{user} );
my $statut = PE_OK;
my $spoofId = $req->param('spoofId') || '';
if ( $spoofId
and $req->param('spoofId') !~ /$self->{conf}->{userControl}/o )
{
return PE_MALFORMEDUSER;
if ( $spoofId !~ /$self->{conf}->{userControl}/o ) {
$self->userLogger->error('Malformed spoofed Id');
$self->logger->debug("Impersonation tried with spoofed Id: $spoofId");
$spoofId = $req->{user};
$statut = PE_MALFORMEDUSER;
}
# Skip if no submitted SpoofId
return PE_OK unless $spoofId;
# Check activation rule
unless ( $self->rule->( $req, $req->sessionInfo ) ) {
$self->userLogger->error('Impersonation service not authorized');
return PE_IMPERSONATION_SERVICE_NOT_ALLOWED;
if ( $spoofId ne $req->{user} ) {
$self->logger->debug("Spoofied Id: $spoofId / Real Id: $req->{user}");
unless ( $self->rule->( $req, $req->sessionInfo ) ) {
$self->userLogger->error('Impersonation service not authorized');
$spoofId = $req->{user};
$statut = PE_IMPERSONATION_SERVICE_NOT_ALLOWED;
}
}
# Fill spoof session
my ( $realSession, $spoofSession ) = ( {}, {} );
$self->logger->debug("Spoofing Id: $spoofId...");
$self->logger->debug("Rename real attributes...");
my $spk = '';
foreach my $k ( keys %{ $req->{sessionInfo} } ) {
if ( $self->{conf}->{impersonationSkipEmptyValues} ) {
@ -74,11 +92,27 @@ sub run {
$self->logger->debug("Delete $k");
delete $req->{sessionInfo}->{$k};
}
$req->{user} = $spoofId;
$spoofSession = $self->_userDatas($req);
$spoofSession->{groups} ||= '';
# Merging SSO groups and hGroups & Dedup
$spoofSession = $self->_userDatas( $req, $spoofId, $realSession );
if ( $req->error ) {
if ( $req->error == PE_BADCREDENTIALS ) {
$statut = PE_BADCREDENTIALS;
}
else {
return $req->error;
}
}
# Update spoofed session
$self->logger->debug("Populating spoofed session...");
foreach (qw (_auth _userDB)) {
$self->logger->debug("Processing $_...");
$spk = "$self->{conf}->{impersonationPrefix}$_";
$spoofSession->{$_} = $realSession->{$spk};
}
# Merging SSO Groups and hGroups & dedup
$spoofSession->{groups} ||= '';
if ( $self->{conf}->{impersonationMergeSSOgroups} ) {
$self->userLogger->warn("MERGING SSO groups and hGroups...");
my $spg = "$self->{conf}->{impersonationPrefix}groups";
@ -101,22 +135,19 @@ sub run {
{ %{ $spoofSession->{hGroups} }, %{ $realSession->{$sphg} } };
}
# Create spoofed session
foreach (qw (_auth _userDB)) {
$self->logger->debug("Processing $_...");
$spk = "$self->{conf}->{impersonationPrefix}$_";
$spoofSession->{$_} = $realSession->{$spk};
}
$spoofSession = { %$spoofSession, %$realSession };
# Main session
$self->p->updateSession( $req, $spoofSession );
return PE_OK;
return $statut;
}
sub _userDatas {
my ( $self, $req ) = @_;
$req->{sessionInfo} = {};
my ( $self, $req, $spoofId, $realSession ) = @_;
my $realId = $req->{user};
$req->{user} = $spoofId;
my $raz = 0;
# Compute Macros and Groups with real and spoofed sessions
$req->{sessionInfo} = {%$realSession};
# Search user in database
$req->steps( [
@ -133,9 +164,41 @@ sub _userDatas {
. ")" );
}
$self->logger->debug("Process returned error: $error");
return $req->error($error);
$req->error($error);
$raz = 1;
}
$self->logger->debug("Populating spoofed session...");
# Check identity rule if impersonation required
if ( $realId ne $spoofId ) {
unless ( $self->idRule->( $req, $req->sessionInfo ) ) {
$self->userLogger->warn(
'Impersonation requested for an unvalid user ('
. $req->{user}
. ")" );
$self->logger->debug('Identity not authorized');
$raz = 1;
}
}
# Same real and spoofed session - Compute Macros and Groups
if ($raz) {
$req->{sessionInfo} = {};
$req->{sessionInfo} = {%$realSession};
$req->{user} = $realId;
$req->steps( [
'getUser', 'setSessionInfo',
'setMacros', 'setGroups',
'setLocalGroups'
]
);
$self->logger->debug('Spoofed session equal real session');
$req->error(PE_BADCREDENTIALS);
if ( my $error = $self->p->process($req) ) {
$self->logger->debug("Process returned error: $error");
$req->error($error);
}
}
return $req->{sessionInfo};
}

View File

@ -242,6 +242,7 @@
"verify":"التحقق",
"VHnotFound":"Virtual Host not found",
"wait":"انتظر",
"waitingmessage":"Authentication in progress, please wait",
"warning":"تحذير",
"welcomeOnPortal":"مرحبا بك على بوابة إثبات الهوية الآمنة.",
"yesResendMail":"نعم، أعد إرسال البريد",
@ -258,4 +259,4 @@
"yourPhone":"رقم هاتفك",
"yourProfile":"ملفك الشخصي",
"yourTotpKey":"Your TOTP key"
}
}

View File

@ -242,6 +242,7 @@
"verify":"Verify",
"VHnotFound":"Virtual Host not found",
"wait":"Warten",
"waitingmessage":"Authentication in progress, please wait",
"warning":"Warnung",
"welcomeOnPortal":"Willkommen in Ihrem gesicherten Authentifizierungsportal.",
"yesResendMail":"Ja, Mail erneut senden.",
@ -258,4 +259,4 @@
"yourPhone":"Ihre Telefonnummer",
"yourProfile":"Ihr Profil",
"yourTotpKey":"Your TOTP key"
}
}

View File

@ -242,6 +242,7 @@
"verify": "Verify",
"VHnotFound":"Virtual Host not found",
"wait":"Wait",
"waitingmessage":"Authentication in progress, please wait",
"warning":"Warning",
"welcomeOnPortal":"Welcome on your secured authentication portal.",
"yesResendMail":"Yes, resend the mail",

View File

@ -242,6 +242,7 @@
"verify":"Verify",
"VHnotFound":"Virtual Host not found",
"wait":"Wait",
"waitingmessage":"Authentication in progress, please wait",
"warning":"Warning",
"welcomeOnPortal":"Welcome on your secured authentication portal.",
"yesResendMail":"Yes, resend the mail",
@ -258,4 +259,4 @@
"yourPhone":"Your phone number",
"yourProfile":"Your profile",
"yourTotpKey":"Your TOTP key"
}
}

View File

@ -242,6 +242,7 @@
"verify": "Vérifier",
"VHnotFound":"Hôte virtuel erroné ou inexistant",
"wait":"Attendre",
"waitingmessage":"Authentification en cours, merci de patienter",
"warning":"Attention",
"welcomeOnPortal":"Bienvenue sur votre portail d'authentification sécurisée.",
"yesResendMail":"Oui, renvoyer le mail",

View File

@ -242,6 +242,7 @@
"verify":"Verifica",
"VHnotFound":"Virtual Host not found",
"wait":"Attendere",
"waitingmessage":"Authentication in progress, please wait",
"warning":"Avvertimento",
"welcomeOnPortal":"Benvenuto sul tuo portale di autenticazione protetta.",
"yesResendMail":"Sì, rinvia e-mail",
@ -258,4 +259,4 @@
"yourPhone":"Numero di telefono",
"yourProfile":"Il tuo profilo",
"yourTotpKey":"La tua chiave TOTP"
}
}

View File

@ -242,6 +242,7 @@
"verify":"Verify",
"VHnotFound":"Virtual Host not found",
"wait":"Wait",
"waitingmessage":"Authentication in progress, please wait",
"warning":"Warning",
"welcomeOnPortal":"Welcome on your secured authentication portal.",
"yesResendMail":"Yes, resend the mail",
@ -258,4 +259,4 @@
"yourPhone":"Your phone number",
"yourProfile":"Your profile",
"yourTotpKey":"Your TOTP key"
}
}

View File

@ -242,6 +242,7 @@
"verify":"Verify",
"VHnotFound":"Virtual Host not found",
"wait":"Wait",
"waitingmessage":"Authentication in progress, please wait",
"warning":"Warning",
"welcomeOnPortal":"Welcome on your secured authentication portal.",
"yesResendMail":"Yes, resend the mail",
@ -258,4 +259,4 @@
"yourPhone":"Your phone number",
"yourProfile":"Your profile",
"yourTotpKey":"Your TOTP key"
}
}

View File

@ -242,6 +242,7 @@
"verify":"Verify",
"VHnotFound":"Virtual Host not found",
"wait":"Wait",
"waitingmessage":"Authentication in progress, please wait",
"warning":"Warning",
"welcomeOnPortal":"Welcome on your secured authentication portal.",
"yesResendMail":"Yes, resend the mail",
@ -258,4 +259,4 @@
"yourPhone":"Your phone number",
"yourProfile":"Your profile",
"yourTotpKey":"Your TOTP key"
}
}

View File

@ -242,6 +242,7 @@
"verify":"Xác minh",
"VHnotFound":"Virtual Host not found",
"wait":"Hãy đợi",
"waitingmessage":"Authentication in progress, please wait",
"warning":"Cảnh báo",
"welcomeOnPortal":"Chào mừng bạn đến với cổng thông tin xác thực được bảo mật của bạn.",
"yesResendMail":"Có, gửi lại thư",
@ -258,4 +259,4 @@
"yourPhone":"Số điện thoại của bạn",
"yourProfile":"Profile của bạn",
"yourTotpKey":"Your TOTP key"
}
}

View File

@ -242,6 +242,7 @@
"verify":"验证",
"VHnotFound":"Virtual Host not found",
"wait":"等待",
"waitingmessage":"Authentication in progress, please wait",
"warning":"警告",
"welcomeOnPortal":"欢迎来到您的加密认证 portal",
"yesResendMail":"好的,重新发送邮件",

View File

@ -1,3 +1,10 @@
<TMPL_IF NAME="TOKEN">
<input type="hidden" name="token" value="<TMPL_VAR NAME="TOKEN">" />
</TMPL_IF>
<TMPL_IF NAME="WAITING_MESSAGE">
<div class="alert alert-info"><span trspan="waitingmessage" /></div>
<TMPL_ELSE>
<div class="form">
<div class="input-group mb-3">
<div class="input-group-prepend">
@ -24,9 +31,6 @@
<input type="text" name="captcha" size="<TMPL_VAR NAME=CAPTCHA_SIZE>" class="form-control" trplaceholder="captcha" required aria-required="true" autocomplete="off" />
</div>
</TMPL_IF>
<TMPL_IF NAME="TOKEN">
<input type="hidden" name="token" value="<TMPL_VAR NAME="TOKEN">" />
</TMPL_IF>
<TMPL_INCLUDE NAME="impersonation.tpl">
<TMPL_INCLUDE NAME="checklogins.tpl">
@ -36,6 +40,7 @@
<span trspan="connect">Connect</span>
</button>
</div>
</TMPL_IF>
<div class="actions">
<TMPL_IF NAME="DISPLAY_RESETPASSWORD">

View File

@ -10,7 +10,7 @@ BEGIN {
require 't/test-lib.pm';
}
my $maintests = 15;
my $maintests = 14;
my $debug = 'error';
my ( $issuer, $sp, $res );
my %handlerOR = ( issuer => [], sp => [] );
@ -132,8 +132,7 @@ m#img src="http://auth.idp.com(/saml/relaySingleLogoutSOAP)\?(relay=.*?)"#s,
),
'Get image'
);
ok( getHeader( $res, 'Content-Type' ) eq 'image/png', 'Get an image' )
or explain( [ $res->[0], $res->[1] ], 'Content-Type => image/png' );
expectRedirection( $res, "http://auth.idp.com/static/common/icons/ok.png");
# Test if logout is done
switch ('issuer');

View File

@ -7,7 +7,7 @@ BEGIN {
require 't/test-lib.pm';
}
my $maintests = 3;
my $maintests = 10;
my $debug = 'error';
my ( $issuer, $res );
my %handlerOR = ( issuer => [], sp => [] );
@ -25,6 +25,15 @@ SKIP: {
ok( $res = $issuer->_get('/saml/metadata'), 'Get metadata' );
ok( $res->[2]->[0] =~ m#^<\?xml version="1.0"\?>#s, 'Metadata is XML' );
ok( $res = $issuer->_get('/saml/metadata/idp'), 'Get IDP metadata' );
ok( $res->[2]->[0] =~ m#^<\?xml version="1.0"\?>#s, 'Metadata is XML' );
ok( $res->[2]->[0] !~ m#<SPSSODescriptor#s, 'Metadata does not contain SP information' );
ok( $res->[2]->[0] =~ m#entityID="urn:example\.com"#s, 'IDP EntityID is overriden' );
ok( $res = $issuer->_get('/saml/metadata/sp'), 'Get SP metadata' );
ok( $res->[2]->[0] =~ m#^<\?xml version="1.0"\?>#s, 'Metadata is XML' );
ok( $res->[2]->[0] !~ m#<IDPSSODescriptor#s, 'Metadata does not contain IDP information' );
#print STDERR Dumper($res);
}
@ -41,6 +50,7 @@ sub issuer {
authentication => 'Demo',
userDB => 'Same',
issuerDBSAMLActivation => 1,
samlOverrideIDPEntityID => 'urn:example.com',
samlSPMetaDataOptions => {
'sp.com' => {
samlSPMetaDataOptionsEncryptionMode => 'none',

View File

@ -0,0 +1,543 @@
use lib 'inc';
use Test::More;
use strict;
use IO::String;
use LWP::UserAgent;
use LWP::Protocol::PSGI;
use MIME::Base64;
BEGIN {
require 't/test-lib.pm';
require 't/saml-lib.pm';
}
my $maintests = 21;
my $debug = 'error';
my ( $issuer, $sp, $res );
my %handlerOR = ( issuer => [], sp => [] );
# Redefine LWP methods for tests
LWP::Protocol::PSGI->register(
sub {
my $req = Plack::Request->new(@_);
fail('POST should not launch SOAP requests');
count(1);
return [ 500, [], [] ];
}
);
SKIP: {
eval "use Lasso";
if ($@) {
skip 'Lasso not found', $maintests;
}
# Initialization
ok( $issuer = issuer(), 'Issuer portal' );
$handlerOR{issuer} = \@Lemonldap::NG::Handler::Main::_onReload;
switch ('sp');
&Lemonldap::NG::Handler::Main::cfgNum( 0, 0 );
ok( $sp = sp(), 'SP portal' );
$handlerOR{sp} = \@Lemonldap::NG::Handler::Main::_onReload;
# Simple SP access
my $res;
ok(
$res = $sp->_get(
'/', accept => 'text/html',
),
'Unauth SP request'
);
expectOK($res);
ok( expectCookie( $res, 'lemonldapidp' ), 'IDP cookie defined' )
or explain(
$res->[1],
'Set-Cookie => lemonldapidp=http://auth.idp.com/saml/metadata; domain=.sp.com; path=/'
);
my ( $host, $url, $s ) =
expectAutoPost( $res, 'auth.idp.com', '/saml/singleSignOn',
'SAMLRequest' );
# Push SAML request to IdP
switch ('issuer');
ok(
$res = $issuer->_post(
$url,
IO::String->new($s),
accept => 'text/html',
length => length($s)
),
'Post SAML request to IdP'
);
expectOK($res);
my $pdata = 'lemonldappdata=' . expectCookie( $res, 'lemonldappdata' );
# Try to authenticate with an unauthorized user to IdP
$s = "user=dwho&password=dwho&$s";
ok(
$res = $issuer->_post(
$url,
IO::String->new($s),
accept => 'text/html',
cookie => $pdata,
length => length($s),
),
'Post authentication'
);
ok( $res->[2]->[0] =~ /trmsg="89"/, 'Reject reason is 89' )
or print STDERR Dumper( $res->[2]->[0] );
# Simple SP access
ok(
$res = $sp->_get(
'/', accept => 'text/html',
),
'Unauth SP request'
);
expectOK($res);
ok( expectCookie( $res, 'lemonldapidp' ), 'IDP cookie defined' )
or explain(
$res->[1],
'Set-Cookie => lemonldapidp=http://auth.idp.com/saml/metadata; domain=.sp.com; path=/'
);
( $host, $url, $s ) =
expectAutoPost( $res, 'auth.idp.com', '/saml/singleSignOn',
'SAMLRequest' );
# Push SAML request to IdP
ok(
$res = $issuer->_post(
$url,
IO::String->new($s),
accept => 'text/html',
length => length($s)
),
'Post SAML request to IdP'
);
expectOK($res);
$pdata = 'lemonldappdata=' . expectCookie( $res, 'lemonldappdata' );
# Try to authenticate with an authorized user to IdP
$s = "user=french&password=french&$s";
ok(
$res = $issuer->_post(
$url,
IO::String->new($s),
accept => 'text/html',
cookie => $pdata,
length => length($s),
),
'Post authentication'
);
my $idpId = expectCookie($res);
( $host, $url, $s ) =
expectAutoPost( $res, 'auth.sp.com', '/saml/proxySingleSignOnPost',
'SAMLResponse' );
# Post SAML response to SP
switch ('sp');
ok(
$res = $sp->_post(
$url, IO::String->new($s),
accept => 'text/html',
length => length($s),
cookie => 'lemonldapidp=http://auth.idp.com/saml/metadata',
),
'Post SAML response to SP'
);
# Verify authentication on SP
expectRedirection( $res, 'http://auth.sp.com' );
my $spId = expectCookie($res);
ok( $res = $sp->_get( '/', cookie => "lemonldap=$spId" ), 'Get / on SP' );
expectOK($res);
expectAuthenticatedAs( $res, 'fa@badwolf.org@idp' );
# Verify UTF-8
ok( $res = $sp->_get("/sessions/global/$spId"), 'Get UTF-8' );
expectOK($res);
ok( $res = eval { JSON::from_json( $res->[2]->[0] ) }, ' GET JSON' )
or print STDERR $@;
ok( $res->{cn} eq 'Frédéric Accents', 'UTF-8 values' )
or explain( $res, 'cn => Frédéric Accents' );
# Logout initiated by SP
ok(
$res = $sp->_get(
'/',
query => 'logout',
cookie => "lemonldap=$spId",
accept => 'text/html'
),
'Query SP for logout'
);
( $host, $url, $s ) =
expectAutoPost( $res, 'auth.idp.com', '/saml/singleLogout',
'SAMLRequest' );
# Push SAML logout request to IdP
switch ('issuer');
ok(
$res = $issuer->_post(
$url,
IO::String->new($s),
accept => 'text/html',
cookie => "lemonldap=$idpId",
length => length($s)
),
'Post SAML logout request to IdP'
);
# The SP doesn't have an SLO endpoint for its response (Hi Renater!) , in
# this case, the portal should display a nice user message instead of
# erroring.
expectOK($res);
ok( $res->[2]->[0] =~ /trmsg="47"/, 'Found logout message' );
ok(
$res = $issuer->_get(
'/', cookie => "lemonldap=$idpId",
),
'Test if user is reject on IdP'
);
expectReject($res);
switch ('sp');
ok(
$res = $sp->_get(
'/',
accept => 'text/html',
cookie =>
"lemonldapidp=http://auth.idp.com/saml/metadata; lemonldap=$spId"
),
'Test if user is reject on SP'
);
expectOK($res);
expectAutoPost( $res, 'auth.idp.com', '/saml/singleSignOn', 'SAMLRequest' );
}
count($maintests);
clean_sessions();
done_testing( count() );
sub switch {
my $type = shift;
@Lemonldap::NG::Handler::Main::_onReload = @{
$handlerOR{$type};
};
}
sub issuer {
return LLNG::Manager::Test->new( {
ini => {
logLevel => $debug,
domain => 'idp.com',
portal => 'http://auth.idp.com',
authentication => 'Demo',
userDB => 'Same',
issuerDBSAMLActivation => 1,
issuerDBSAMLRule => '$uid eq "french"',
samlSPMetaDataOptions => {
'sp.com' => {
samlSPMetaDataOptionsEncryptionMode => 'none',
samlSPMetaDataOptionsSignSSOMessage => 1,
samlSPMetaDataOptionsSignSLOMessage => 1,
samlSPMetaDataOptionsCheckSSOMessageSignature => 1,
samlSPMetaDataOptionsCheckSLOMessageSignature => 1,
}
},
samlSPMetaDataExportedAttributes => {
'sp.com' => {
cn =>
'1;cn;urn:oasis:names:tc:SAML:2.0:attrname-format:basic',
uid =>
'1;uid;urn:oasis:names:tc:SAML:2.0:attrname-format:basic',
}
},
samlOrganizationDisplayName => "IDP",
samlOrganizationName => "IDP",
samlOrganizationURL => "http://www.idp.com/",
samlServicePrivateKeyEnc => "-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAnfKBDG/K0TnGT7Xu8q1N45sNWvIK91SqNg8nvN2uVeKoHADT
csus5Xn3id5+8Q9TuMFsW9kIEeXiaPKXQa9ryfSNDhWDWloNkpGEeWif2BnHUu46
Abu1UBWb0mH6VwcG1PR4qHruLis1odjQ1qnVDNfSEASVIppEBYjDX203ypmURIzU
6h53GRRRlf1BLWkbVn9ysmDeR57Xw5Rsx/+tBlcnMrkv/40DSUkehQIl2JmlFrl2
Caik+gU4pd20apA/pNLjBZF0OmGoS08AIR5NMd0KFa6CwZUUSHJqH5GFy5Y2yl4l
g8K0klAS9q7L7aXI+eFQZhkwidjpxXnHPyxIGQIDAQABAoIBAHnfqjX3eO8SfnP5
NURp90Td2mNHirCn0qLd9NKl1ySMPR1GgeH9SQ7Umu32EcteAUL5dOw2PiTZVmeW
cKINgsWVftXUQcOQ4xIqWKb51QUBdy0FhxrZRSFjWxXt5iYK1PmzHfsax/g1/S9C
RnqtFyjOy1bywkSt9jiy+9YBR2B7BDhLHlILbijWn5zaecaV4YA+L1UK4M/mehdb
+0FVPavbGpnlqBRTY+7YXfZ/mRPCfn5DvO9lW1O0pJMmNdBh9kmm3DxHf6AkK47a
43gO/dRWiWo2rZ/+Jw7uyqOb23U0MydP7kia0p3tzCUBPsrlgnichYG5RNFp0wqy
3VT1TYECgYEA0Y9vENy1jJd+s7WbGrsRtSKxfZgtJr0yjSlQVYrIlwbZSGn+ndxq
V2vVlwIgLX3pz6T40BMfk6SNx08jjy0Sgn6OAM0ILrinno8yWcSAMCmfCU0S/3O1
55bqtcnk4XTHBHzJ5OrnrPaW5ourvJz0lcWEKMg3BXxLzaF6ZRy85nECgYEAwPMD
LNAKLCDrUMyYFOpPyPLe7wvszcFvPipGgerSgFP1c6N7xaMUdHDYqBfuis1khPGF
YcMHeNBYmzX6yEGbp3lrB4PHpUySmTU3mv3u9I05aahInK21gXum3uRkCWyyIF6V
T/qeszl9mVOCp0CC4eG3IMVpaD0UKDEHVhERYCkCgYAjuTPRyA4a3Wh38ilysRkf
q75eDqcDx5Tqg3RyYKo5NK2troP9HSnzpSpQB8i8eI53G0RfFCN5479XjqIdMi3J
mRFUCZ+vd0L7wKVwsBK6Ix49U6o9adhElnGEc9pUpLeYiD1SjMjZr1+iBYVNLeRz
86vH1/mpMbsqXrCis/dvwQKBgGttomHr/w3s0jftget7PirrFrbP0+wHfDGHhjRF
kyhCFtJovrwefYALaIXGtVjw3LusYZA570oT7pGUb2naJZkMYEwR0jG1vZWx7KDO
K6JbkxDB0pPxn7JVL2bAkPYyX8boAohCSOQO6WBZ/8+xem3bp4OGhpa0EyoBik0g
OaVpAoGATj4SyYsE10hGT676iie8zy3fi5IPC3E+x4QlVuusaLtuY8LJA50stjtx
gUa/JAKlZZL+gvzvOviQIxyfIChXOdTt5uiOYkdHJDbAF3NSrji7hrXq4v8UZv75
8hBrwJZIpy6y01dRlrriHmPRtEq1pk7JX2uUg0sP5g4BEcsaCbc=
-----END RSA PRIVATE KEY-----
",
samlServicePrivateKeySig => "-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAtR/wgDqWB4Maho5V6TjcL/NbNfjgIh7GcgkrB5RZcVT1GTej
JlMjUQdgBKBuZXQN+7/29P6UcGq1kYalURq6S8SpeJ1ofp5rBEoD/TIkvU0JOcid
65wp+fdzXGXsfiZvHraU74jSCgjP/wqfVGRyBIQzB0SIxSpnrsigqNsE1E94toDM
x4wovjHu/9ABAImREV7Sz83OeFF00/sghrjTEJOD/gHf04JCn9MgNOqvSTysr9LX
Wg/oUKQDEYeTq9ux6pq/oqv1MxwONbSZPtN5yD41mi+hT8Rh+W8Je8rsiML4VMxz
sb1l9303asw6suo5bLTISKNSbu1nt1NkpNxzywIDAQABAoIBAQCQkbvPPfP+bwC/
IeEk1IO7qkzFWa7czR+safD0jc6OjTdNN4F716Q6yt4zEzLKu8VliiW+C23EBQiD
7asKf4DvdTun0ExVtHDK7aEdeealSlXwz1ZtdypyILbtq1UGo/rR0v4x601rQPl0
IrBmFf6D6FkqleNtLJmxguXpoVfLdYKNwkxH2ux+GOA9r2o5pUCQmJGDap5YWRuQ
uB71ewJjVWujaL3e1ac/5cP7/tqWmgAiOaN8sYdD6+oWOR47bHj8JKcMBSl4y2QC
dL31cGmmf5KqBbtISki3RXfHHjT7E3Z85CbESkKTZlEb1ar3XmepY6Z7V5UO16oz
fFE5R6khAoGBAOl9Qb+qYVVO5ugE65ORjYVeuXykANhM9ssiY5a6zuAakWzw7Zv3
k6PXm9p7azlEXAlTnTXVwHYMyuuzZDvQ8LRV1iBOdPuIkUAmaQ5K9ASD7VcoHexh
k8DAKf9Ln7sTRaMdvgceRNczOmJOBIEpTZkssA/jVGXZsoyTWYl1en/ZAoGBAMaW
RnNbSNprEV2b8UeAJ6i77c4SXwu1I8X2NLtiLScb1ETBjfrdHmdlJglfyd/0gmhH
p/43Ku2iGUoY5KtuOI6QmahrJYQscRQhoj252VXadG6fNWWAlpgdCm9houhHb5BF
3zge/bTr0anUe9EA7Z/ymav12rEouoNjIlhI9C5DAoGATR85a2SMt8/TB0owwdJu
62GpZNkLCmcJkXkvaecUVAOSi2hdI4o4MwMRkK35cbX5rH74y4JqCtQY5pefgP53
sykzDAK+MyMdzxGg2764MRGegI5Yq+5jDmSquo+xF+q6srEtRk6iMG7UVwosBLmu
zuxqzySoiOfKSRKWnYe3SakCgYEAwWMkVkAmETXE4oDzFSsS8/mW2l//mPocTTK3
JWe1CunJ6+8FYbAlZJEW2ngismp8+CoXybNVpbZ+pC7buKoMf6EHUgCNt0pEEFO0
mCG9KSMk0XlPWXpArP9S4yaUq1itpzSz7QYZES+4rIcU0HLz9RgeWFyCTJWaFErc
7laVG9sCgYBKOtk5WlIOP4BxSd2y4cYzohgwTZIs1/2kTEn1u4eH73M1xvAlHHFB
wSF5QXgDKJ8pPAOhNWpdLO/PdtnQn91nOvTNc+ShJZzjdbneUdQVpWpoBf72uA+N
6rIVf1JBUL2p7HFHaGdUZC7KGQ+yv6ZHrE1+7202nuDvJdvGEEdFsQ==
-----END RSA PRIVATE KEY-----
",
samlServicePublicKeyEnc => "-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnfKBDG/K0TnGT7Xu8q1N
45sNWvIK91SqNg8nvN2uVeKoHADTcsus5Xn3id5+8Q9TuMFsW9kIEeXiaPKXQa9r
yfSNDhWDWloNkpGEeWif2BnHUu46Abu1UBWb0mH6VwcG1PR4qHruLis1odjQ1qnV
DNfSEASVIppEBYjDX203ypmURIzU6h53GRRRlf1BLWkbVn9ysmDeR57Xw5Rsx/+t
BlcnMrkv/40DSUkehQIl2JmlFrl2Caik+gU4pd20apA/pNLjBZF0OmGoS08AIR5N
Md0KFa6CwZUUSHJqH5GFy5Y2yl4lg8K0klAS9q7L7aXI+eFQZhkwidjpxXnHPyxI
GQIDAQAB
-----END PUBLIC KEY-----
",
samlServicePublicKeySig => "-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtR/wgDqWB4Maho5V6Tjc
L/NbNfjgIh7GcgkrB5RZcVT1GTejJlMjUQdgBKBuZXQN+7/29P6UcGq1kYalURq6
S8SpeJ1ofp5rBEoD/TIkvU0JOcid65wp+fdzXGXsfiZvHraU74jSCgjP/wqfVGRy
BIQzB0SIxSpnrsigqNsE1E94toDMx4wovjHu/9ABAImREV7Sz83OeFF00/sghrjT
EJOD/gHf04JCn9MgNOqvSTysr9LXWg/oUKQDEYeTq9ux6pq/oqv1MxwONbSZPtN5
yD41mi+hT8Rh+W8Je8rsiML4VMxzsb1l9303asw6suo5bLTISKNSbu1nt1NkpNxz
ywIDAQAB
-----END PUBLIC KEY-----
",
samlSPMetaDataXML => {
"sp.com" => {
samlSPMetaDataXML => <<EOF
<?xml version="1.0"?>
<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
entityID="http://auth.sp.com/saml/metadata">
<SPSSODescriptor AuthnRequestsSigned="true"
WantAssertionsSigned="true"
protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<KeyDescriptor use="signing">
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:KeyValue>
<RSAKeyValue xmlns="http://www.w3.org/2000/09/xmldsig#">
<Modulus>
u4iToYAEmWQxgZDihGVzMMql1elPn37domWcvXeU2E4yt2hh5jkQHiFjgodfOlNeRIw5QJVlUBwr
+CQvbaKRFXd7BrOhQIDC0TZPRVB0XHarUtsCuDekN4/2GKSzHsoToKUVPWq9thsuek3xkpsJGZNX
7bglfEc9+QQpYTqN1rkdN1PVU0epNMokFFGho5pLRqLUV5+I/QXAL49jfTjaSxsp4UndTI8/+mGS
RSq+nrT2zyQRM/vkj5vR9ZVz67HO/+Wk3Mx6RAwkVcMdgMAqCq8odmbI0yCRZiTL9ybKWRKqWJoK
J0p5+Q2fPEBPupQZR09Jt/JPuLVSsGfCxi9Nqw==</Modulus>
<Exponent>AQAB</Exponent>
</RSAKeyValue>
</ds:KeyValue>
</ds:KeyInfo>
</KeyDescriptor>
<KeyDescriptor use="encryption">
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:KeyValue>
<RSAKeyValue xmlns="http://www.w3.org/2000/09/xmldsig#">
<Modulus>
sRaod2RZ8hMFBl+VhsnhyPM8l/Fj1obnBxfQIaWuHFIFfXiGe/CYHuZ5QJQLnZxHMJX6LL3Sh+Us
og3p0jpijpcg0QgfBSEkfopKTgReYN8DiDIll0rV1XdTni7E85Nd1YyNy3ui/ZD+UShWwqu6jLVL
R+QUm+/1LIKYb3OCBTvOlY7xHoP6NSU1+Mr+YzGBUacdO2vnNxe/PQhxIeP1zO0njuqGHkwEpy8r
UWRZbbDn31TmKjqlhgtsz5HPhbRaYEExhyepKgBiNz+RyxtYXVhuG8OrWQDoS5gYHSjdw1CTJyix
eJwyoqA9RGYguG5nh9zndi3LWAh7Z0lx+tIz+w==</Modulus>
<Exponent>AQAB</Exponent>
</RSAKeyValue>
</ds:KeyValue>
</ds:KeyInfo>
</KeyDescriptor>
<ArtifactResolutionService isDefault="true" index="0"
Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP"
Location="http://auth.sp.com/saml/artifact" />
<NameIDFormat>
urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</NameIDFormat>
<NameIDFormat>
urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName</NameIDFormat>
<NameIDFormat>
urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName</NameIDFormat>
<NameIDFormat>
urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos</NameIDFormat>
<NameIDFormat>
urn:oasis:names:tc:SAML:2.0:nameid-format:entity</NameIDFormat>
<NameIDFormat>
urn:oasis:names:tc:SAML:2.0:nameid-format:transient</NameIDFormat>
<AssertionConsumerService isDefault="true" index="0"
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Location="http://auth.sp.com/saml/proxySingleSignOnPost" />
<AssertionConsumerService isDefault="false" index="1"
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"
Location="http://auth.sp.com/saml/proxySingleSignOnArtifact" />
</SPSSODescriptor>
<Organization>
<OrganizationName xml:lang="en">org</OrganizationName>
<OrganizationDisplayName xml:lang="en">
org</OrganizationDisplayName>
<OrganizationURL xml:lang="en">
http://www.org.com</OrganizationURL>
</Organization>
</EntityDescriptor>
EOF
},
},
}
}
);
}
sub sp {
return LLNG::Manager::Test->new( {
ini => {
logLevel => $debug,
domain => 'sp.com',
portal => 'http://auth.sp.com',
authentication => 'SAML',
userDB => 'Same',
issuerDBSAMLActivation => 0,
restSessionServer => 1,
samlIDPMetaDataExportedAttributes => {
idp => {
mail => "0;mail;;",
uid => "1;uid",
cn => "0;cn"
}
},
samlIDPMetaDataOptions => {
idp => {
samlIDPMetaDataOptionsEncryptionMode => 'none',
samlIDPMetaDataOptionsSSOBinding => 'post',
samlIDPMetaDataOptionsSLOBinding => 'post',
samlIDPMetaDataOptionsSignSSOMessage => 1,
samlIDPMetaDataOptionsSignSLOMessage => 1,
samlIDPMetaDataOptionsCheckSSOMessageSignature => 1,
samlIDPMetaDataOptionsCheckSLOMessageSignature => 1,
samlIDPMetaDataOptionsForceUTF8 => 1,
}
},
samlIDPMetaDataExportedAttributes => {
idp => {
"uid" => "0;uid;;",
"cn" => "1;cn;;",
},
},
samlIDPMetaDataXML => {
idp => {
samlIDPMetaDataXML =>
samlIDPMetaDataXML( 'idp', 'HTTP-POST' )
}
},
samlOrganizationDisplayName => "SP",
samlOrganizationName => "SP",
samlOrganizationURL => "http://www.sp.com",
samlServicePublicKeySig => "-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu4iToYAEmWQxgZDihGVz
MMql1elPn37domWcvXeU2E4yt2hh5jkQHiFjgodfOlNeRIw5QJVlUBwr+CQvbaKR
FXd7BrOhQIDC0TZPRVB0XHarUtsCuDekN4/2GKSzHsoToKUVPWq9thsuek3xkpsJ
GZNX7bglfEc9+QQpYTqN1rkdN1PVU0epNMokFFGho5pLRqLUV5+I/QXAL49jfTja
Sxsp4UndTI8/+mGSRSq+nrT2zyQRM/vkj5vR9ZVz67HO/+Wk3Mx6RAwkVcMdgMAq
Cq8odmbI0yCRZiTL9ybKWRKqWJoKJ0p5+Q2fPEBPupQZR09Jt/JPuLVSsGfCxi9N
qwIDAQAB
-----END PUBLIC KEY-----
",
samlServicePrivateKeyEnc => "-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAsRaod2RZ8hMFBl+VhsnhyPM8l/Fj1obnBxfQIaWuHFIFfXiG
e/CYHuZ5QJQLnZxHMJX6LL3Sh+Usog3p0jpijpcg0QgfBSEkfopKTgReYN8DiDIl
l0rV1XdTni7E85Nd1YyNy3ui/ZD+UShWwqu6jLVLR+QUm+/1LIKYb3OCBTvOlY7x
HoP6NSU1+Mr+YzGBUacdO2vnNxe/PQhxIeP1zO0njuqGHkwEpy8rUWRZbbDn31Tm
Kjqlhgtsz5HPhbRaYEExhyepKgBiNz+RyxtYXVhuG8OrWQDoS5gYHSjdw1CTJyix
eJwyoqA9RGYguG5nh9zndi3LWAh7Z0lx+tIz+wIDAQABAoIBAEkZrk8iiJKJ0WAx
IrsyKNbXuWKLTYgnxcRCyzKofrfID+YcU39j8JeI0fKbajQUZ7qhnlTLwtU//+2h
SqzyVu6/add/v7ZRWQw3L7cGzKK2THHzKVtLk/t7N3QroDdf1LMrQvkFP2HmcWS0
/yN62hXtXHb/qpY4Nn+6JQyUpM5dkv8S/QjDl2NTdyWrXKzWp+4I3QLQ20f4zym+
ir7RennziMc0HlQNcTjGAUbFULtdqEfSFWhNK7UjiRY+S0XV2xJIbGjnxUQH62fS
w1ZzYsF7sBtoSckvfL4WfGbylhOVnliU05RLU2c67PRjj1Gskoslq1Ow/3DHR7rI
BSBpV8ECgYEA1eHfcog7xQGDkW+cshJtFPFx+9MegB58gFW1rl0rn+tfbexvoSEA
7G7EOTyaU6OAI+8StiRT6AYTgEU7PMM9zDykdGIWj3h0OpHGA86xhEiiaaM2DDRv
/DEKRVlEdmRLLLY28pJVHOMYomia3mb2VKZGg2VfGtSfjg1GXD3I8OECgYEA0/X0
U55KjZ1JQTPUgFc1WK1NxX9MaH+NcpDaolEUy3Qf3QTbfws+a9K3vwCn7EpQhrfs
I6RVUtwFdCyfl/jzBY9Gykkg03sMgW7Qw2SCCsSt05M+jDtBbNJ7esP6PAeKFvXZ
ZWhdeiAa4kM/P6gtvZXQ4tY4LkSbcd6b0SzzFFsCgYBjMsusFzuBd95JyfZnMNye
5gzzu0teKMWd0CLfqB7foQ81sH9lwCTpg8ZGtbDuMdrwz6ViDR9NceQBjhqXaAZ1
f3rW79d+22Ms9wdcJLV4oSeSzzv2FSwLT8NvvqNeNc4YArshbnVDXKDEUrfhhueh
Ay2ZK58clpkaDVYg2hckgQKBgG3KuhtSI/YE4fwXN9yez7A2XNGPZem/IGqWo9lu
PGJCrXqT2IqPLW82gB083r6jo+CUhonTxqqb82tA7g4PUvqvQ5Dmnk1NMKYe255K
gp3HUO8GF2EWFIak5Hcr6oOLuDi6cjh3/euTk7ld8fYsTD0mzEOjiQhWW1p5X6bT
LLp/AoGAHvkxA1NM1HJ3myAREbwNXxRy/nhNt4mwMkZ6hPQsW/Eg/3r7j6MJOFrm
U8AJJjDGKe6nlXhhnMoQfJzAc0cYNgjktmJXW27fHGIwt/2QwYNFHPK3s7HTrfH6
7T4XKT3yGeeeyC2soKJQPlGB+ETdIUnXa7eo9KVWtMTgISyx1Qk=
-----END RSA PRIVATE KEY-----
",
samlServicePrivateKeySig => "-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAu4iToYAEmWQxgZDihGVzMMql1elPn37domWcvXeU2E4yt2hh
5jkQHiFjgodfOlNeRIw5QJVlUBwr+CQvbaKRFXd7BrOhQIDC0TZPRVB0XHarUtsC
uDekN4/2GKSzHsoToKUVPWq9thsuek3xkpsJGZNX7bglfEc9+QQpYTqN1rkdN1PV
U0epNMokFFGho5pLRqLUV5+I/QXAL49jfTjaSxsp4UndTI8/+mGSRSq+nrT2zyQR
M/vkj5vR9ZVz67HO/+Wk3Mx6RAwkVcMdgMAqCq8odmbI0yCRZiTL9ybKWRKqWJoK
J0p5+Q2fPEBPupQZR09Jt/JPuLVSsGfCxi9NqwIDAQABAoIBABE0Cjb6g3F+23vD
SsRSeiqzrFrfOEqtXK+VGrfWzHS7V7Ozg6eW/H+HGJXUzUuQcklfg7EFA3JB41a0
GxW3oA+UElkfCV/dcAG5NbRqGQKScEz9glZb5FikgDLqiPP+HabS/gvQSu71t2HI
3KxSRJdwCNTp26Z28pxxYUpmELTtxd9vlHjffit2Mnt2uc8hOtFHdNavfYwvYH7o
bmlckp7b/JVOy2Yy21O94ZWkE498jXyn71Gr+V1cnJ0RrmYbhQqIvFpFHj98Pf4O
if3c4YmBcZ4t7PUsZUYF3ooWt8k/mdigQC3D6p80OKe+wUTYKcCN0ZdFbiURv9pg
CsqLh+ECgYEA9vA+9QfzvXC7S5yXgTkuRiusPlNye/AiyA/0oGjmjFZ1YNsT7awH
6BjW6WE+rS4elKJu1GaefM/cDguH4ZmJc+eKgi4LDCqYw9rr9les3aneBc8demd3
O/Ej1Pud1QxXArBNfBYo08vEqwST9P89clJC5090U6bGK2E0rTVu1w0CgYEAwmpG
9LbOFeGCPmwX7Avuk7tQQfRSV6q9TFZo+HxDfKYvxec846l1vBenY2rrgYhtolYJ
YS795LYgbSWRxGfgr1GuIbP5GsjHy6/1o6bS8M++GJ7KHArb0QLAYyQweqqb164A
NvHJkveueWnxzeOlD9j2fcjEnBHwTnqjG+17CZcCgYEAqMXawa4FsNxzpmIISpHC
RsNindZ60Kp3mzUMhPYtXI1a/C+/lxmU7dTMTgXgyIxU6lF6XkEk4TlPtWm8HTzK
7SS7Te4aLt6OOo5N57hUtct7q4y7IQXGQHm3e8HdRdeBQJ0u2Dhs/xSt/hTK6w/n
91Kx11Y+s02w88UkM53pe6ECgYAF/UYwVc1liSv9BlF6WSfBb1zam09KGh1405Sq
SxG9LlV8cFJE5TyWTdg/TNTyiaRvAt2JG+yAdkfrdOPXvCeE3yxRJ30+IP9evA4C
O6p19sBxe7rYQFFjUAVjSIMh1E22yEqDZtGB8JV0chob8K5uHY4CdAPylu7jTA3o
V1maAwKBgQCSGQ3yzsk4EGN2xd/JdgGDzhKyTZTQKMWYqQcsYxRAQ7Paj7u+Wkgv
dBeKcI0HwgpLy5ZohSd2erqieIsW0pEbJWCmos4IcO8tgNfEOa5WXYdyLbj5tFwt
ctu4/BJdijqfpMAtG8pv6k09gYjfASVytXmydGcs/0rVKYCRQA8Tow==
-----END RSA PRIVATE KEY-----
",
samlServicePublicKeyEnc => "-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsRaod2RZ8hMFBl+Vhsnh
yPM8l/Fj1obnBxfQIaWuHFIFfXiGe/CYHuZ5QJQLnZxHMJX6LL3Sh+Usog3p0jpi
jpcg0QgfBSEkfopKTgReYN8DiDIll0rV1XdTni7E85Nd1YyNy3ui/ZD+UShWwqu6
jLVLR+QUm+/1LIKYb3OCBTvOlY7xHoP6NSU1+Mr+YzGBUacdO2vnNxe/PQhxIeP1
zO0njuqGHkwEpy8rUWRZbbDn31TmKjqlhgtsz5HPhbRaYEExhyepKgBiNz+RyxtY
XVhuG8OrWQDoS5gYHSjdw1CTJyixeJwyoqA9RGYguG5nh9zndi3LWAh7Z0lx+tIz
+wIDAQAB
-----END PUBLIC KEY-----
",
samlSPSSODescriptorAuthnRequestsSigned => 1,
},
}
);
}

View File

@ -0,0 +1,502 @@
use lib 'inc';
use Test::More;
use strict;
use IO::String;
use LWP::UserAgent;
use LWP::Protocol::PSGI;
use MIME::Base64;
BEGIN {
require 't/test-lib.pm';
require 't/saml-lib.pm';
}
my $maintests = 12;
my $debug = 'error';
my ( $idp, $proxy, $app, $res );
my %handlerOR = ( idp => [], proxy => [], app => [] );
# Redefine LWP methods for tests
LWP::Protocol::PSGI->register(
sub {
my $req = Plack::Request->new(@_);
ok( $req->uri =~ m#http://auth.(app|proxy).com([^\?]*)(?:\?(.*))?$#,
'SOAP request' );
my $host = $1;
my $url = $2;
my $query = $3;
my $res;
my $client = ( $host eq 'app' ? $app : $proxy );
if ( $req->method eq 'POST' ) {
my $s = $req->content;
ok(
$res = $client->_post(
$url, IO::String->new($s),
length => length($s),
query => $query,
type => 'application/xml',
),
"Execute POST request to $url"
);
}
else {
ok(
$res = $client->_get(
$url,
type => 'application/xml',
query => $query,
),
"Execute request to $url"
);
}
expectOK($res);
ok( getHeader( $res, 'Content-Type' ) =~ m#xml#, 'Content is XML' )
or explain( $res->[1], 'Content-Type => application/xml' );
count(3);
return $res;
}
);
SKIP: {
eval "use Lasso";
if ($@) {
skip 'Lasso not found', $maintests;
}
# Initialization
switch ('idp');
ok( $idp = idp(), 'IdP portal' );
$handlerOR{idp} = \@Lemonldap::NG::Handler::Main::_onReload;
switch ('proxy');
&Lemonldap::NG::Handler::Main::cfgNum( 0, 0 );
ok( $proxy = proxy(), 'Proxy portal' );
$handlerOR{proxy} = \@Lemonldap::NG::Handler::Main::_onReload;
switch ('app');
&Lemonldap::NG::Handler::Main::cfgNum( 0, 0 );
ok( $app = app(), 'CAS app portal' );
$handlerOR{app} = \@Lemonldap::NG::Handler::Main::_onReload;
# Query RP for auth
ok( $res = $app->_get( '/', accept => 'text/html' ),
'Unauth CAS app request' );
ok( expectCookie( $res, 'llngcasserver' ) eq 'proxy',
'Get CAS server cookie' );
my ( $url, $query ) =
expectRedirection( $res, qr#http://auth.proxy.com(/cas/login)\?(.*)$# );
# Push request to Proxy
switch ('proxy');
ok(
$res = $proxy->_get(
$url,
query => $query,
accept => 'text/html',
),
"Push request to proxy"
);
my $proxyPdata = 'lemonldappdata=' . expectCookie( $res, 'lemonldappdata' );
my ( $url, $query ) =
expectRedirection( $res, qr#^http://discovery.example.com/# );
# Return from WAYF
ok(
$res = $proxy->_get(
"/",
query => "idp=".uri_escape("http://auth.idp.com/saml/metadata"),
accept => 'text/html',
cookie => $proxyPdata,
),
"Return from WAYF"
);
$proxyPdata = 'lemonldappdata=' . expectCookie( $res, 'lemonldappdata' );
my ( $host, $tmp );
( $host, $url, $query ) =
expectAutoPost( $res, 'auth.idp.com', '/saml/singleSignOn',
'SAMLRequest' );
switch ('idp');
ok(
$res = $idp->_post(
$url,
IO::String->new($query),
length => length($query),
accept => 'text/html',
),
'Launch SAML request to IdP'
);
my $idpPdata = 'lemonldappdata=' . expectCookie( $res, 'lemonldappdata' );
# Try to authenticate to IdP
my $body = $res->[2]->[0];
$body =~ s/^.*?<form.*?>//s;
$body =~ s#</form>.*$##s;
my %fields =
( $body =~ /<input type="hidden".+?name="(.+?)".+?value="(.*?)"/sg );
$fields{user} = $fields{password} = 'french';
use URI::Escape;
$query =
join( '&', map { "$_=" . uri_escape( $fields{$_} ) } keys %fields );
ok(
$res = $idp->_post(
$url,
IO::String->new($query),
accept => 'text/html',
length => length($query),
cookie => $idpPdata,
),
'Post authentication'
);
( $host, $url, $query ) = expectAutoPost($res);
$query =~ s/\+/%2B/g;
my $idpId = expectCookie($res);
# Post SAML response
switch ('proxy');
ok(
$res = $proxy->_post(
$url, IO::String->new($query),
length => length($query),
accept => 'text/html',
cookie =>
"lemonldapidp=http://auth.idp.com/saml/metadata;$proxyPdata",
),
'POST SAML response'
);
my $spId = expectCookie($res);
($url) = expectRedirection( $res, qr#http://auth.proxy.com([^?]*)# );
ok(
$res = $proxy->_get(
$url,
accept => 'text/html',
cookie =>
"lemonldapidp=http://auth.idp.com/saml/metadata;lemonldap=$spId;$proxyPdata",
),
'Follow internal redirection'
);
($query) =
expectRedirection( $res, qr#^http://auth.app.com/\?(ticket.*)$# );
# Follow redirection to App
switch ('app');
ok( $res = $app->_get( '/', query => $query, accept => 'text/html' ),
'Follow redirection to RP' );
my $appId = expectCookie($res);
}
count($maintests);
clean_sessions();
done_testing( count() );
sub switch {
my $type = shift;
pass( '==> Switching to ' . uc($type) . ' <==' );
count(1);
@Lemonldap::NG::Handler::Main::_onReload = @{
$handlerOR{$type};
};
}
sub idp {
return LLNG::Manager::Test->new( {
ini => {
logLevel => $debug,
domain => 'idp.com',
portal => 'http://auth.idp.com',
authentication => 'Demo',
userDB => 'Same',
issuerDBSAMLActivation => 1,
samlSPMetaDataOptions => {
'proxy.com' => {
samlSPMetaDataOptionsEncryptionMode => 'none',
samlSPMetaDataOptionsSignSSOMessage => 1,
samlSPMetaDataOptionsSignSLOMessage => 1,
samlSPMetaDataOptionsCheckSSOMessageSignature => 1,
samlSPMetaDataOptionsCheckSLOMessageSignature => 1,
}
},
samlSPMetaDataExportedAttributes => {
'proxy.com' => {
cn =>
'1;cn;urn:oasis:names:tc:SAML:2.0:attrname-format:basic',
uid =>
'1;uid;urn:oasis:names:tc:SAML:2.0:attrname-format:basic',
}
},
samlOrganizationDisplayName => "IDP",
samlOrganizationName => "IDP",
samlOrganizationURL => "http://www.idp.com/",
samlServicePrivateKeyEnc => "-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAnfKBDG/K0TnGT7Xu8q1N45sNWvIK91SqNg8nvN2uVeKoHADT
csus5Xn3id5+8Q9TuMFsW9kIEeXiaPKXQa9ryfSNDhWDWloNkpGEeWif2BnHUu46
Abu1UBWb0mH6VwcG1PR4qHruLis1odjQ1qnVDNfSEASVIppEBYjDX203ypmURIzU
6h53GRRRlf1BLWkbVn9ysmDeR57Xw5Rsx/+tBlcnMrkv/40DSUkehQIl2JmlFrl2
Caik+gU4pd20apA/pNLjBZF0OmGoS08AIR5NMd0KFa6CwZUUSHJqH5GFy5Y2yl4l
g8K0klAS9q7L7aXI+eFQZhkwidjpxXnHPyxIGQIDAQABAoIBAHnfqjX3eO8SfnP5
NURp90Td2mNHirCn0qLd9NKl1ySMPR1GgeH9SQ7Umu32EcteAUL5dOw2PiTZVmeW
cKINgsWVftXUQcOQ4xIqWKb51QUBdy0FhxrZRSFjWxXt5iYK1PmzHfsax/g1/S9C
RnqtFyjOy1bywkSt9jiy+9YBR2B7BDhLHlILbijWn5zaecaV4YA+L1UK4M/mehdb
+0FVPavbGpnlqBRTY+7YXfZ/mRPCfn5DvO9lW1O0pJMmNdBh9kmm3DxHf6AkK47a
43gO/dRWiWo2rZ/+Jw7uyqOb23U0MydP7kia0p3tzCUBPsrlgnichYG5RNFp0wqy
3VT1TYECgYEA0Y9vENy1jJd+s7WbGrsRtSKxfZgtJr0yjSlQVYrIlwbZSGn+ndxq
V2vVlwIgLX3pz6T40BMfk6SNx08jjy0Sgn6OAM0ILrinno8yWcSAMCmfCU0S/3O1
55bqtcnk4XTHBHzJ5OrnrPaW5ourvJz0lcWEKMg3BXxLzaF6ZRy85nECgYEAwPMD
LNAKLCDrUMyYFOpPyPLe7wvszcFvPipGgerSgFP1c6N7xaMUdHDYqBfuis1khPGF
YcMHeNBYmzX6yEGbp3lrB4PHpUySmTU3mv3u9I05aahInK21gXum3uRkCWyyIF6V
T/qeszl9mVOCp0CC4eG3IMVpaD0UKDEHVhERYCkCgYAjuTPRyA4a3Wh38ilysRkf
q75eDqcDx5Tqg3RyYKo5NK2troP9HSnzpSpQB8i8eI53G0RfFCN5479XjqIdMi3J
mRFUCZ+vd0L7wKVwsBK6Ix49U6o9adhElnGEc9pUpLeYiD1SjMjZr1+iBYVNLeRz
86vH1/mpMbsqXrCis/dvwQKBgGttomHr/w3s0jftget7PirrFrbP0+wHfDGHhjRF
kyhCFtJovrwefYALaIXGtVjw3LusYZA570oT7pGUb2naJZkMYEwR0jG1vZWx7KDO
K6JbkxDB0pPxn7JVL2bAkPYyX8boAohCSOQO6WBZ/8+xem3bp4OGhpa0EyoBik0g
OaVpAoGATj4SyYsE10hGT676iie8zy3fi5IPC3E+x4QlVuusaLtuY8LJA50stjtx
gUa/JAKlZZL+gvzvOviQIxyfIChXOdTt5uiOYkdHJDbAF3NSrji7hrXq4v8UZv75
8hBrwJZIpy6y01dRlrriHmPRtEq1pk7JX2uUg0sP5g4BEcsaCbc=
-----END RSA PRIVATE KEY-----
",
samlServicePrivateKeySig => "-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAtR/wgDqWB4Maho5V6TjcL/NbNfjgIh7GcgkrB5RZcVT1GTej
JlMjUQdgBKBuZXQN+7/29P6UcGq1kYalURq6S8SpeJ1ofp5rBEoD/TIkvU0JOcid
65wp+fdzXGXsfiZvHraU74jSCgjP/wqfVGRyBIQzB0SIxSpnrsigqNsE1E94toDM
x4wovjHu/9ABAImREV7Sz83OeFF00/sghrjTEJOD/gHf04JCn9MgNOqvSTysr9LX
Wg/oUKQDEYeTq9ux6pq/oqv1MxwONbSZPtN5yD41mi+hT8Rh+W8Je8rsiML4VMxz
sb1l9303asw6suo5bLTISKNSbu1nt1NkpNxzywIDAQABAoIBAQCQkbvPPfP+bwC/
IeEk1IO7qkzFWa7czR+safD0jc6OjTdNN4F716Q6yt4zEzLKu8VliiW+C23EBQiD
7asKf4DvdTun0ExVtHDK7aEdeealSlXwz1ZtdypyILbtq1UGo/rR0v4x601rQPl0
IrBmFf6D6FkqleNtLJmxguXpoVfLdYKNwkxH2ux+GOA9r2o5pUCQmJGDap5YWRuQ
uB71ewJjVWujaL3e1ac/5cP7/tqWmgAiOaN8sYdD6+oWOR47bHj8JKcMBSl4y2QC
dL31cGmmf5KqBbtISki3RXfHHjT7E3Z85CbESkKTZlEb1ar3XmepY6Z7V5UO16oz
fFE5R6khAoGBAOl9Qb+qYVVO5ugE65ORjYVeuXykANhM9ssiY5a6zuAakWzw7Zv3
k6PXm9p7azlEXAlTnTXVwHYMyuuzZDvQ8LRV1iBOdPuIkUAmaQ5K9ASD7VcoHexh
k8DAKf9Ln7sTRaMdvgceRNczOmJOBIEpTZkssA/jVGXZsoyTWYl1en/ZAoGBAMaW
RnNbSNprEV2b8UeAJ6i77c4SXwu1I8X2NLtiLScb1ETBjfrdHmdlJglfyd/0gmhH
p/43Ku2iGUoY5KtuOI6QmahrJYQscRQhoj252VXadG6fNWWAlpgdCm9houhHb5BF
3zge/bTr0anUe9EA7Z/ymav12rEouoNjIlhI9C5DAoGATR85a2SMt8/TB0owwdJu
62GpZNkLCmcJkXkvaecUVAOSi2hdI4o4MwMRkK35cbX5rH74y4JqCtQY5pefgP53
sykzDAK+MyMdzxGg2764MRGegI5Yq+5jDmSquo+xF+q6srEtRk6iMG7UVwosBLmu
zuxqzySoiOfKSRKWnYe3SakCgYEAwWMkVkAmETXE4oDzFSsS8/mW2l//mPocTTK3
JWe1CunJ6+8FYbAlZJEW2ngismp8+CoXybNVpbZ+pC7buKoMf6EHUgCNt0pEEFO0
mCG9KSMk0XlPWXpArP9S4yaUq1itpzSz7QYZES+4rIcU0HLz9RgeWFyCTJWaFErc
7laVG9sCgYBKOtk5WlIOP4BxSd2y4cYzohgwTZIs1/2kTEn1u4eH73M1xvAlHHFB
wSF5QXgDKJ8pPAOhNWpdLO/PdtnQn91nOvTNc+ShJZzjdbneUdQVpWpoBf72uA+N
6rIVf1JBUL2p7HFHaGdUZC7KGQ+yv6ZHrE1+7202nuDvJdvGEEdFsQ==
-----END RSA PRIVATE KEY-----
",
samlServicePublicKeyEnc => "-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnfKBDG/K0TnGT7Xu8q1N
45sNWvIK91SqNg8nvN2uVeKoHADTcsus5Xn3id5+8Q9TuMFsW9kIEeXiaPKXQa9r
yfSNDhWDWloNkpGEeWif2BnHUu46Abu1UBWb0mH6VwcG1PR4qHruLis1odjQ1qnV
DNfSEASVIppEBYjDX203ypmURIzU6h53GRRRlf1BLWkbVn9ysmDeR57Xw5Rsx/+t
BlcnMrkv/40DSUkehQIl2JmlFrl2Caik+gU4pd20apA/pNLjBZF0OmGoS08AIR5N
Md0KFa6CwZUUSHJqH5GFy5Y2yl4lg8K0klAS9q7L7aXI+eFQZhkwidjpxXnHPyxI
GQIDAQAB
-----END PUBLIC KEY-----
",
samlServicePublicKeySig => "-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtR/wgDqWB4Maho5V6Tjc
L/NbNfjgIh7GcgkrB5RZcVT1GTejJlMjUQdgBKBuZXQN+7/29P6UcGq1kYalURq6
S8SpeJ1ofp5rBEoD/TIkvU0JOcid65wp+fdzXGXsfiZvHraU74jSCgjP/wqfVGRy
BIQzB0SIxSpnrsigqNsE1E94toDMx4wovjHu/9ABAImREV7Sz83OeFF00/sghrjT
EJOD/gHf04JCn9MgNOqvSTysr9LXWg/oUKQDEYeTq9ux6pq/oqv1MxwONbSZPtN5
yD41mi+hT8Rh+W8Je8rsiML4VMxzsb1l9303asw6suo5bLTISKNSbu1nt1NkpNxz
ywIDAQAB
-----END PUBLIC KEY-----
",
samlSPMetaDataXML => {
"proxy.com" => {
samlSPMetaDataXML =>
samlSPMetaDataXML( 'proxy', 'HTTP-POST' )
},
},
}
}
);
}
sub proxy {
return LLNG::Manager::Test->new( {
ini => {
logLevel => $debug,
domain => 'proxy.com',
portal => 'http://auth.proxy.com',
authentication => 'SAML',
userDB => 'Same',
issuerDBCASActivation => 1,
casAttr => 'uid',
casAttributes => { cn => 'cn', uid => 'uid', mail => 'mail', },
casAccessControlPolicy => 'none',
multiValuesSeparator => ';',
samlDiscoveryProtocolURL => 'http://discovery.example.com/',
samlDiscoveryProtocolActivation => 1,
samlIDPMetaDataExportedAttributes => {
idp => {
mail => "0;mail;;",
uid => "1;uid",
cn => "0;cn"
},
idp2 => {
mail => "0;mail;;",
uid => "1;uid",
cn => "0;cn"
}
},
samlIDPMetaDataOptions => {
idp => {
samlIDPMetaDataOptionsEncryptionMode => 'none',
samlIDPMetaDataOptionsSSOBinding => 'post',
samlIDPMetaDataOptionsSLOBinding => 'post',
samlIDPMetaDataOptionsSignSSOMessage => 1,
samlIDPMetaDataOptionsSignSLOMessage => 1,
samlIDPMetaDataOptionsCheckSSOMessageSignature => 1,
samlIDPMetaDataOptionsCheckSLOMessageSignature => 1,
samlIDPMetaDataOptionsForceUTF8 => 1,
},
idp2 => {
samlIDPMetaDataOptionsEncryptionMode => 'none',
samlIDPMetaDataOptionsSSOBinding => 'post',
samlIDPMetaDataOptionsSLOBinding => 'post',
samlIDPMetaDataOptionsSignSSOMessage => 1,
samlIDPMetaDataOptionsSignSLOMessage => 1,
samlIDPMetaDataOptionsCheckSSOMessageSignature => 1,
samlIDPMetaDataOptionsCheckSLOMessageSignature => 1,
samlIDPMetaDataOptionsForceUTF8 => 1,
}
},
samlIDPMetaDataExportedAttributes => {
idp => {
"uid" => "0;uid;;",
"cn" => "1;cn;;",
},
idp2 => {
"uid" => "0;uid;;",
"cn" => "1;cn;;",
},
},
samlIDPMetaDataXML => {
idp => {
samlIDPMetaDataXML =>
samlIDPMetaDataXML( 'idp', 'HTTP-POST' )
},
idp2 => {
samlIDPMetaDataXML =>
samlIDPMetaDataXML( 'idp2', 'HTTP-POST' )
}
},
samlOrganizationDisplayName => "SP",
samlOrganizationName => "SP",
samlOrganizationURL => "http://www.proxy.com",
samlServicePublicKeySig => "-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu4iToYAEmWQxgZDihGVz
MMql1elPn37domWcvXeU2E4yt2hh5jkQHiFjgodfOlNeRIw5QJVlUBwr+CQvbaKR
FXd7BrOhQIDC0TZPRVB0XHarUtsCuDekN4/2GKSzHsoToKUVPWq9thsuek3xkpsJ
GZNX7bglfEc9+QQpYTqN1rkdN1PVU0epNMokFFGho5pLRqLUV5+I/QXAL49jfTja
Sxsp4UndTI8/+mGSRSq+nrT2zyQRM/vkj5vR9ZVz67HO/+Wk3Mx6RAwkVcMdgMAq
Cq8odmbI0yCRZiTL9ybKWRKqWJoKJ0p5+Q2fPEBPupQZR09Jt/JPuLVSsGfCxi9N
qwIDAQAB
-----END PUBLIC KEY-----
",
samlServicePrivateKeyEnc => "-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAsRaod2RZ8hMFBl+VhsnhyPM8l/Fj1obnBxfQIaWuHFIFfXiG
e/CYHuZ5QJQLnZxHMJX6LL3Sh+Usog3p0jpijpcg0QgfBSEkfopKTgReYN8DiDIl
l0rV1XdTni7E85Nd1YyNy3ui/ZD+UShWwqu6jLVLR+QUm+/1LIKYb3OCBTvOlY7x
HoP6NSU1+Mr+YzGBUacdO2vnNxe/PQhxIeP1zO0njuqGHkwEpy8rUWRZbbDn31Tm
Kjqlhgtsz5HPhbRaYEExhyepKgBiNz+RyxtYXVhuG8OrWQDoS5gYHSjdw1CTJyix
eJwyoqA9RGYguG5nh9zndi3LWAh7Z0lx+tIz+wIDAQABAoIBAEkZrk8iiJKJ0WAx
IrsyKNbXuWKLTYgnxcRCyzKofrfID+YcU39j8JeI0fKbajQUZ7qhnlTLwtU//+2h
SqzyVu6/add/v7ZRWQw3L7cGzKK2THHzKVtLk/t7N3QroDdf1LMrQvkFP2HmcWS0
/yN62hXtXHb/qpY4Nn+6JQyUpM5dkv8S/QjDl2NTdyWrXKzWp+4I3QLQ20f4zym+
ir7RennziMc0HlQNcTjGAUbFULtdqEfSFWhNK7UjiRY+S0XV2xJIbGjnxUQH62fS
w1ZzYsF7sBtoSckvfL4WfGbylhOVnliU05RLU2c67PRjj1Gskoslq1Ow/3DHR7rI
BSBpV8ECgYEA1eHfcog7xQGDkW+cshJtFPFx+9MegB58gFW1rl0rn+tfbexvoSEA
7G7EOTyaU6OAI+8StiRT6AYTgEU7PMM9zDykdGIWj3h0OpHGA86xhEiiaaM2DDRv
/DEKRVlEdmRLLLY28pJVHOMYomia3mb2VKZGg2VfGtSfjg1GXD3I8OECgYEA0/X0
U55KjZ1JQTPUgFc1WK1NxX9MaH+NcpDaolEUy3Qf3QTbfws+a9K3vwCn7EpQhrfs
I6RVUtwFdCyfl/jzBY9Gykkg03sMgW7Qw2SCCsSt05M+jDtBbNJ7esP6PAeKFvXZ
ZWhdeiAa4kM/P6gtvZXQ4tY4LkSbcd6b0SzzFFsCgYBjMsusFzuBd95JyfZnMNye
5gzzu0teKMWd0CLfqB7foQ81sH9lwCTpg8ZGtbDuMdrwz6ViDR9NceQBjhqXaAZ1
f3rW79d+22Ms9wdcJLV4oSeSzzv2FSwLT8NvvqNeNc4YArshbnVDXKDEUrfhhueh
Ay2ZK58clpkaDVYg2hckgQKBgG3KuhtSI/YE4fwXN9yez7A2XNGPZem/IGqWo9lu
PGJCrXqT2IqPLW82gB083r6jo+CUhonTxqqb82tA7g4PUvqvQ5Dmnk1NMKYe255K
gp3HUO8GF2EWFIak5Hcr6oOLuDi6cjh3/euTk7ld8fYsTD0mzEOjiQhWW1p5X6bT
LLp/AoGAHvkxA1NM1HJ3myAREbwNXxRy/nhNt4mwMkZ6hPQsW/Eg/3r7j6MJOFrm
U8AJJjDGKe6nlXhhnMoQfJzAc0cYNgjktmJXW27fHGIwt/2QwYNFHPK3s7HTrfH6
7T4XKT3yGeeeyC2soKJQPlGB+ETdIUnXa7eo9KVWtMTgISyx1Qk=
-----END RSA PRIVATE KEY-----
",
samlServicePrivateKeySig => "-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAu4iToYAEmWQxgZDihGVzMMql1elPn37domWcvXeU2E4yt2hh
5jkQHiFjgodfOlNeRIw5QJVlUBwr+CQvbaKRFXd7BrOhQIDC0TZPRVB0XHarUtsC
uDekN4/2GKSzHsoToKUVPWq9thsuek3xkpsJGZNX7bglfEc9+QQpYTqN1rkdN1PV
U0epNMokFFGho5pLRqLUV5+I/QXAL49jfTjaSxsp4UndTI8/+mGSRSq+nrT2zyQR
M/vkj5vR9ZVz67HO/+Wk3Mx6RAwkVcMdgMAqCq8odmbI0yCRZiTL9ybKWRKqWJoK
J0p5+Q2fPEBPupQZR09Jt/JPuLVSsGfCxi9NqwIDAQABAoIBABE0Cjb6g3F+23vD
SsRSeiqzrFrfOEqtXK+VGrfWzHS7V7Ozg6eW/H+HGJXUzUuQcklfg7EFA3JB41a0
GxW3oA+UElkfCV/dcAG5NbRqGQKScEz9glZb5FikgDLqiPP+HabS/gvQSu71t2HI
3KxSRJdwCNTp26Z28pxxYUpmELTtxd9vlHjffit2Mnt2uc8hOtFHdNavfYwvYH7o
bmlckp7b/JVOy2Yy21O94ZWkE498jXyn71Gr+V1cnJ0RrmYbhQqIvFpFHj98Pf4O
if3c4YmBcZ4t7PUsZUYF3ooWt8k/mdigQC3D6p80OKe+wUTYKcCN0ZdFbiURv9pg
CsqLh+ECgYEA9vA+9QfzvXC7S5yXgTkuRiusPlNye/AiyA/0oGjmjFZ1YNsT7awH
6BjW6WE+rS4elKJu1GaefM/cDguH4ZmJc+eKgi4LDCqYw9rr9les3aneBc8demd3
O/Ej1Pud1QxXArBNfBYo08vEqwST9P89clJC5090U6bGK2E0rTVu1w0CgYEAwmpG
9LbOFeGCPmwX7Avuk7tQQfRSV6q9TFZo+HxDfKYvxec846l1vBenY2rrgYhtolYJ
YS795LYgbSWRxGfgr1GuIbP5GsjHy6/1o6bS8M++GJ7KHArb0QLAYyQweqqb164A
NvHJkveueWnxzeOlD9j2fcjEnBHwTnqjG+17CZcCgYEAqMXawa4FsNxzpmIISpHC
RsNindZ60Kp3mzUMhPYtXI1a/C+/lxmU7dTMTgXgyIxU6lF6XkEk4TlPtWm8HTzK
7SS7Te4aLt6OOo5N57hUtct7q4y7IQXGQHm3e8HdRdeBQJ0u2Dhs/xSt/hTK6w/n
91Kx11Y+s02w88UkM53pe6ECgYAF/UYwVc1liSv9BlF6WSfBb1zam09KGh1405Sq
SxG9LlV8cFJE5TyWTdg/TNTyiaRvAt2JG+yAdkfrdOPXvCeE3yxRJ30+IP9evA4C
O6p19sBxe7rYQFFjUAVjSIMh1E22yEqDZtGB8JV0chob8K5uHY4CdAPylu7jTA3o
V1maAwKBgQCSGQ3yzsk4EGN2xd/JdgGDzhKyTZTQKMWYqQcsYxRAQ7Paj7u+Wkgv
dBeKcI0HwgpLy5ZohSd2erqieIsW0pEbJWCmos4IcO8tgNfEOa5WXYdyLbj5tFwt
ctu4/BJdijqfpMAtG8pv6k09gYjfASVytXmydGcs/0rVKYCRQA8Tow==
-----END RSA PRIVATE KEY-----
",
samlServicePublicKeyEnc => "-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsRaod2RZ8hMFBl+Vhsnh
yPM8l/Fj1obnBxfQIaWuHFIFfXiGe/CYHuZ5QJQLnZxHMJX6LL3Sh+Usog3p0jpi
jpcg0QgfBSEkfopKTgReYN8DiDIll0rV1XdTni7E85Nd1YyNy3ui/ZD+UShWwqu6
jLVLR+QUm+/1LIKYb3OCBTvOlY7xHoP6NSU1+Mr+YzGBUacdO2vnNxe/PQhxIeP1
zO0njuqGHkwEpy8rUWRZbbDn31TmKjqlhgtsz5HPhbRaYEExhyepKgBiNz+RyxtY
XVhuG8OrWQDoS5gYHSjdw1CTJyixeJwyoqA9RGYguG5nh9zndi3LWAh7Z0lx+tIz
+wIDAQAB
-----END PUBLIC KEY-----
",
samlSPSSODescriptorAuthnRequestsSigned => 1,
},
}
);
}
sub app {
my ( $jwks, $metadata ) = @_;
return LLNG::Manager::Test->new( {
ini => {
logLevel => $debug,
domain => 'app.com',
portal => 'http://auth.app.com',
authentication => 'CAS',
userDB => 'Same',
multiValuesSeparator => ';',
casSrvMetaDataExportedVars => {
proxy => {
cn => 'cn',
mail => 'mail',
uid => 'uid',
}
},
casSrvMetaDataOptions => {
proxy => {
casSrvMetaDataOptionsUrl => 'http://auth.proxy.com/cas',
casSrvMetaDataOptionsGateway => 0,
}
},
}
}
);
}

View File

@ -0,0 +1,701 @@
use lib 'inc';
use Test::More;
use strict;
use IO::String;
use LWP::UserAgent;
use LWP::Protocol::PSGI;
use MIME::Base64;
BEGIN {
require 't/test-lib.pm';
require 't/saml-lib.pm';
}
my $maintests = 19;
my $debug = 'error';
my ( $idp, $sp, $rp, $res );
my %handlerOR = ( idp => [], sp => [], rp => [] );
LWP::Protocol::PSGI->register(
sub {
my $req = Plack::Request->new(@_);
ok( $req->uri =~ m#http://auth.(rp|sp).com(.*)#, ' REST request' );
my $host = $1;
my $url = $2;
my ( $res, $client );
count(1);
if ( $host eq 'sp' ) {
pass(" Request from RP to OP(sp), endpoint $url");
$client = $sp;
}
elsif ( $host eq 'rp' ) {
pass(' Request from OP to RP(proxy)');
$client = $rp;
}
else {
fail(' Aborting REST request (external)');
return HTTP::Response->new(500);
}
if ( $req->method =~ /^post$/i ) {
my $s = $req->content;
ok(
$res = $client->_post(
$url, IO::String->new($s),
length => length($s),
type => $req->header('Content-Type'),
),
' Execute request'
);
}
else {
ok(
$res = $client->_get(
$url,
custom => {
HTTP_AUTHORIZATION => $req->header('Authorization'),
}
),
' Execute request'
);
}
ok( $res->[0] == 200, ' Response is 200' );
ok( getHeader( $res, 'Content-Type' ) =~ m#^application/json#,
' Content is JSON' )
or explain( $res->[1], 'Content-Type => application/json' );
count(4);
return $res;
}
);
SKIP: {
eval "use Lasso";
if ($@) {
skip 'Lasso not found', $maintests;
}
# Initialization
switch ('idp');
ok( $idp = idp(), 'IdP portal' );
$handlerOR{idp} = \@Lemonldap::NG::Handler::Main::_onReload;
switch ('sp');
&Lemonldap::NG::Handler::Main::cfgNum( 0, 0 );
ok( $sp = sp(), 'Proxy portal' );
$handlerOR{sp} = \@Lemonldap::NG::Handler::Main::_onReload;
ok(
$res = $sp->_get('/oauth2/jwks'),
'Get JWKS, endpoint /oauth2/jwks'
);
expectOK($res);
my $jwks = $res->[2]->[0];
ok(
$res = $sp->_get('/.well-known/openid-configuration'),
'Get metadata, endpoint /.well-known/openid-configuration'
);
expectOK($res);
my $metadata = $res->[2]->[0];
switch ('rp');
&Lemonldap::NG::Handler::Main::cfgNum( 0, 0 );
ok( $rp = rp( $jwks, $metadata ), 'RP portal' );
$handlerOR{rp} = \@Lemonldap::NG::Handler::Main::_onReload;
# Query RP for auth
ok( $res = $rp->_get( '/', accept => 'text/html' ), 'Unauth SP request' );
my ( $url, $query ) =
expectRedirection( $res,
qr#http://auth.sp.com(/oauth2/authorize)\?(.*)$# );
# Push request to Proxy
switch ('sp');
ok(
$res = $sp->_get(
$url,
query => $query,
accept => 'text/html',
# cookie => 'lemonldapidp=http://auth.idp.com/saml/metadata'
),
"Push request to OP, endpoint $url"
);
my $spPdata = 'lemonldappdata=' . expectCookie( $res, 'lemonldappdata' );
my ( $url, $query ) =
expectRedirection( $res, qr#^http://discovery.example.com/# );
# Return from WAYF
ok(
$res = $sp->_get(
"/",
query => "idp=".uri_escape("http://auth.idp.com/saml/metadata"),
accept => 'text/html',
cookie => $spPdata,
),
"Return from WAYF"
);
$spPdata = 'lemonldappdata=' . expectCookie( $res, 'lemonldappdata' );
my ( $host, $tmp );
( $url, $query ) = expectRedirection( $res,
qr#^http://auth.idp.com(/saml/singleSignOn)\?(SAMLRequest=.+)# );
# Push SAML request to IdP
switch ('idp');
ok(
$res = $idp->_get(
$url,
query => $query,
accept => 'text/html',
),
'Launch SAML request to IdP'
);
my $idpPdata = 'lemonldappdata=' . expectCookie( $res, 'lemonldappdata' );
# Try to authenticate to IdP
my $body = $res->[2]->[0];
$body =~ s/^.*?<form.*?>//s;
$body =~ s#</form>.*$##s;
my %fields =
( $body =~ /<input type="hidden".+?name="(.+?)".+?value="(.*?)"/sg );
$fields{user} = $fields{password} = 'french';
use URI::Escape;
$query =
join( '&', map { "$_=" . uri_escape( $fields{$_} ) } keys %fields );
ok(
$res = $idp->_post(
$url,
IO::String->new($query),
accept => 'text/html',
length => length($query),
cookie => $idpPdata,
),
'Post authentication'
);
( $host, $url, $query ) = expectAutoPost( $res, 'auth.sp.com' );
$query =~ s/\+/%2B/g;
my $idpId = expectCookie($res);
# Post SAML response
switch ('sp');
ok(
$res = $sp->_post(
$url, IO::String->new($query),
length => length($query),
accept => 'text/html',
cookie => "lemonldapidp=http://auth.idp.com/saml/metadata;$spPdata"
),
'POST SAML response'
);
my $spId = expectCookie($res);
( $url, $query ) = expectRedirection( $res,
qr#http://auth.sp.com/*(/oauth2[^\?]*)(?:\?(.*))?$# );
# Follow internal redirection
ok(
$res = $sp->_get(
$url,
query => $query,
accept => 'text/html',
cookie =>
"lemonldap=$spId;lemonldapidp=http://auth.idp.com/saml/metadata;$spPdata"
),
'Follow internal redirection from SAML-SP to OIDC-OP'
);
( $host, $tmp, $query ) = expectForm( $res, '#', undef, 'confirm' );
ok(
$res = $sp->_get(
$url,
query => $query,
accept => 'text/html',
cookie =>
"lemonldap=$spId;lemonldapidp=http://auth.idp.com/saml/metadata;$spPdata"
),
'Confirm OIDC sharing'
);
($query) = expectRedirection( $res, qr#http://auth.rp.com/*\?(.*)$# );
# Follow redirection to RP
switch ('rp');
ok( $res = $rp->_get( '/', query => $query, accept => 'text/html' ),
'Follow redirection to RP' );
my $rpId = expectCookie($res);
# Logout initiated by RP
ok(
$res = $rp->_get(
'/',
query => 'logout',
cookie => "lemonldap=$rpId",
accept => 'text/html'
),
'Query RP for logout'
);
( $url, $query ) = expectRedirection( $res,
qr#http://auth.sp.com(/oauth2/logout)\?(post_logout_redirect_uri=.+)$#
);
# Push logout request to proxy
switch ('sp');
ok(
$res = $sp->_get(
$url,
query => $query,
cookie => "lemonldap=$spId",
accept => 'text/html'
),
"Push logout request to OP/SP, endpoint $url"
);
( $host, $tmp, $query ) = expectForm( $res, '#', undef, 'confirm' );
ok(
$res = $sp->_post(
$url, IO::String->new($query),
length => length($query),
accept => 'text/html',
cookie => "lemonldap=$spId",
),
"Confirm logout, endpoint $url"
);
( $url, $query ) = expectRedirection( $res,
qr#http://auth.idp.com/*(/saml/singleLogout)\?(.*)# );
# Push logout to SAML IdP
switch ('idp');
ok(
$res = $idp->_get(
$url,
query => $query,
cookie => "lemonldap=$idpId",
accept => 'text/html',
),
'Push logout to SAML IdP'
);
( $url, $query ) = expectRedirection( $res,
qr#http://auth.sp.com/*(/saml/proxySingleLogoutReturn)\?(.*)$# );
# Push logout to SAML SP
switch ('sp');
ok(
$res = $sp->_get(
$url,
query => $query,
cookie => "lemonldap=$spId",
accept => 'text/html',
),
'Push logout to SAML IdP'
);
#print STDERR Dumper($res);
}
count($maintests);
clean_sessions();
done_testing( count() );
sub switch {
my $type = shift;
pass( '==> Switching to ' . uc($type) . ' <==' );
count(1);
@Lemonldap::NG::Handler::Main::_onReload = @{
$handlerOR{$type};
};
}
sub idp {
return LLNG::Manager::Test->new( {
ini => {
logLevel => $debug,
domain => 'idp.com',
portal => 'http://auth.idp.com',
authentication => 'Demo',
userDB => 'Same',
issuerDBSAMLActivation => 1,
samlSPMetaDataOptions => {
'sp.com' => {
samlSPMetaDataOptionsEncryptionMode => 'none',
samlSPMetaDataOptionsSignSSOMessage => 1,
samlSPMetaDataOptionsSignSLOMessage => 1,
samlSPMetaDataOptionsCheckSSOMessageSignature => 1,
samlSPMetaDataOptionsCheckSLOMessageSignature => 1,
}
},
samlSPMetaDataExportedAttributes => {
'sp.com' => {
cn =>
'1;cn;urn:oasis:names:tc:SAML:2.0:attrname-format:basic',
uid =>
'1;uid;urn:oasis:names:tc:SAML:2.0:attrname-format:basic',
}
},
samlOrganizationDisplayName => "IDP",
samlOrganizationName => "IDP",
samlOrganizationURL => "http://www.idp.com/",
samlServicePrivateKeyEnc => "-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAnfKBDG/K0TnGT7Xu8q1N45sNWvIK91SqNg8nvN2uVeKoHADT
csus5Xn3id5+8Q9TuMFsW9kIEeXiaPKXQa9ryfSNDhWDWloNkpGEeWif2BnHUu46
Abu1UBWb0mH6VwcG1PR4qHruLis1odjQ1qnVDNfSEASVIppEBYjDX203ypmURIzU
6h53GRRRlf1BLWkbVn9ysmDeR57Xw5Rsx/+tBlcnMrkv/40DSUkehQIl2JmlFrl2
Caik+gU4pd20apA/pNLjBZF0OmGoS08AIR5NMd0KFa6CwZUUSHJqH5GFy5Y2yl4l
g8K0klAS9q7L7aXI+eFQZhkwidjpxXnHPyxIGQIDAQABAoIBAHnfqjX3eO8SfnP5
NURp90Td2mNHirCn0qLd9NKl1ySMPR1GgeH9SQ7Umu32EcteAUL5dOw2PiTZVmeW
cKINgsWVftXUQcOQ4xIqWKb51QUBdy0FhxrZRSFjWxXt5iYK1PmzHfsax/g1/S9C
RnqtFyjOy1bywkSt9jiy+9YBR2B7BDhLHlILbijWn5zaecaV4YA+L1UK4M/mehdb
+0FVPavbGpnlqBRTY+7YXfZ/mRPCfn5DvO9lW1O0pJMmNdBh9kmm3DxHf6AkK47a
43gO/dRWiWo2rZ/+Jw7uyqOb23U0MydP7kia0p3tzCUBPsrlgnichYG5RNFp0wqy
3VT1TYECgYEA0Y9vENy1jJd+s7WbGrsRtSKxfZgtJr0yjSlQVYrIlwbZSGn+ndxq
V2vVlwIgLX3pz6T40BMfk6SNx08jjy0Sgn6OAM0ILrinno8yWcSAMCmfCU0S/3O1
55bqtcnk4XTHBHzJ5OrnrPaW5ourvJz0lcWEKMg3BXxLzaF6ZRy85nECgYEAwPMD
LNAKLCDrUMyYFOpPyPLe7wvszcFvPipGgerSgFP1c6N7xaMUdHDYqBfuis1khPGF
YcMHeNBYmzX6yEGbp3lrB4PHpUySmTU3mv3u9I05aahInK21gXum3uRkCWyyIF6V
T/qeszl9mVOCp0CC4eG3IMVpaD0UKDEHVhERYCkCgYAjuTPRyA4a3Wh38ilysRkf
q75eDqcDx5Tqg3RyYKo5NK2troP9HSnzpSpQB8i8eI53G0RfFCN5479XjqIdMi3J
mRFUCZ+vd0L7wKVwsBK6Ix49U6o9adhElnGEc9pUpLeYiD1SjMjZr1+iBYVNLeRz
86vH1/mpMbsqXrCis/dvwQKBgGttomHr/w3s0jftget7PirrFrbP0+wHfDGHhjRF
kyhCFtJovrwefYALaIXGtVjw3LusYZA570oT7pGUb2naJZkMYEwR0jG1vZWx7KDO
K6JbkxDB0pPxn7JVL2bAkPYyX8boAohCSOQO6WBZ/8+xem3bp4OGhpa0EyoBik0g
OaVpAoGATj4SyYsE10hGT676iie8zy3fi5IPC3E+x4QlVuusaLtuY8LJA50stjtx
gUa/JAKlZZL+gvzvOviQIxyfIChXOdTt5uiOYkdHJDbAF3NSrji7hrXq4v8UZv75
8hBrwJZIpy6y01dRlrriHmPRtEq1pk7JX2uUg0sP5g4BEcsaCbc=
-----END RSA PRIVATE KEY-----
",
samlServicePrivateKeySig => "-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAtR/wgDqWB4Maho5V6TjcL/NbNfjgIh7GcgkrB5RZcVT1GTej
JlMjUQdgBKBuZXQN+7/29P6UcGq1kYalURq6S8SpeJ1ofp5rBEoD/TIkvU0JOcid
65wp+fdzXGXsfiZvHraU74jSCgjP/wqfVGRyBIQzB0SIxSpnrsigqNsE1E94toDM
x4wovjHu/9ABAImREV7Sz83OeFF00/sghrjTEJOD/gHf04JCn9MgNOqvSTysr9LX
Wg/oUKQDEYeTq9ux6pq/oqv1MxwONbSZPtN5yD41mi+hT8Rh+W8Je8rsiML4VMxz
sb1l9303asw6suo5bLTISKNSbu1nt1NkpNxzywIDAQABAoIBAQCQkbvPPfP+bwC/
IeEk1IO7qkzFWa7czR+safD0jc6OjTdNN4F716Q6yt4zEzLKu8VliiW+C23EBQiD
7asKf4DvdTun0ExVtHDK7aEdeealSlXwz1ZtdypyILbtq1UGo/rR0v4x601rQPl0
IrBmFf6D6FkqleNtLJmxguXpoVfLdYKNwkxH2ux+GOA9r2o5pUCQmJGDap5YWRuQ
uB71ewJjVWujaL3e1ac/5cP7/tqWmgAiOaN8sYdD6+oWOR47bHj8JKcMBSl4y2QC
dL31cGmmf5KqBbtISki3RXfHHjT7E3Z85CbESkKTZlEb1ar3XmepY6Z7V5UO16oz
fFE5R6khAoGBAOl9Qb+qYVVO5ugE65ORjYVeuXykANhM9ssiY5a6zuAakWzw7Zv3
k6PXm9p7azlEXAlTnTXVwHYMyuuzZDvQ8LRV1iBOdPuIkUAmaQ5K9ASD7VcoHexh
k8DAKf9Ln7sTRaMdvgceRNczOmJOBIEpTZkssA/jVGXZsoyTWYl1en/ZAoGBAMaW
RnNbSNprEV2b8UeAJ6i77c4SXwu1I8X2NLtiLScb1ETBjfrdHmdlJglfyd/0gmhH
p/43Ku2iGUoY5KtuOI6QmahrJYQscRQhoj252VXadG6fNWWAlpgdCm9houhHb5BF
3zge/bTr0anUe9EA7Z/ymav12rEouoNjIlhI9C5DAoGATR85a2SMt8/TB0owwdJu
62GpZNkLCmcJkXkvaecUVAOSi2hdI4o4MwMRkK35cbX5rH74y4JqCtQY5pefgP53
sykzDAK+MyMdzxGg2764MRGegI5Yq+5jDmSquo+xF+q6srEtRk6iMG7UVwosBLmu
zuxqzySoiOfKSRKWnYe3SakCgYEAwWMkVkAmETXE4oDzFSsS8/mW2l//mPocTTK3
JWe1CunJ6+8FYbAlZJEW2ngismp8+CoXybNVpbZ+pC7buKoMf6EHUgCNt0pEEFO0
mCG9KSMk0XlPWXpArP9S4yaUq1itpzSz7QYZES+4rIcU0HLz9RgeWFyCTJWaFErc
7laVG9sCgYBKOtk5WlIOP4BxSd2y4cYzohgwTZIs1/2kTEn1u4eH73M1xvAlHHFB
wSF5QXgDKJ8pPAOhNWpdLO/PdtnQn91nOvTNc+ShJZzjdbneUdQVpWpoBf72uA+N
6rIVf1JBUL2p7HFHaGdUZC7KGQ+yv6ZHrE1+7202nuDvJdvGEEdFsQ==
-----END RSA PRIVATE KEY-----
",
samlServicePublicKeyEnc => "-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnfKBDG/K0TnGT7Xu8q1N
45sNWvIK91SqNg8nvN2uVeKoHADTcsus5Xn3id5+8Q9TuMFsW9kIEeXiaPKXQa9r
yfSNDhWDWloNkpGEeWif2BnHUu46Abu1UBWb0mH6VwcG1PR4qHruLis1odjQ1qnV
DNfSEASVIppEBYjDX203ypmURIzU6h53GRRRlf1BLWkbVn9ysmDeR57Xw5Rsx/+t
BlcnMrkv/40DSUkehQIl2JmlFrl2Caik+gU4pd20apA/pNLjBZF0OmGoS08AIR5N
Md0KFa6CwZUUSHJqH5GFy5Y2yl4lg8K0klAS9q7L7aXI+eFQZhkwidjpxXnHPyxI
GQIDAQAB
-----END PUBLIC KEY-----
",
samlServicePublicKeySig => "-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtR/wgDqWB4Maho5V6Tjc
L/NbNfjgIh7GcgkrB5RZcVT1GTejJlMjUQdgBKBuZXQN+7/29P6UcGq1kYalURq6
S8SpeJ1ofp5rBEoD/TIkvU0JOcid65wp+fdzXGXsfiZvHraU74jSCgjP/wqfVGRy
BIQzB0SIxSpnrsigqNsE1E94toDMx4wovjHu/9ABAImREV7Sz83OeFF00/sghrjT
EJOD/gHf04JCn9MgNOqvSTysr9LXWg/oUKQDEYeTq9ux6pq/oqv1MxwONbSZPtN5
yD41mi+hT8Rh+W8Je8rsiML4VMxzsb1l9303asw6suo5bLTISKNSbu1nt1NkpNxz
ywIDAQAB
-----END PUBLIC KEY-----
",
samlSPMetaDataXML => {
"sp.com" => {
samlSPMetaDataXML =>
samlSPMetaDataXML( 'sp', 'HTTP-Redirect' )
},
},
}
}
);
}
sub sp {
return LLNG::Manager::Test->new( {
ini => {
logLevel => $debug,
domain => 'sp.com',
portal => 'http://auth.sp.com',
authentication => 'SAML',
userDB => 'Same',
issuerDBSAMLActivation => 0,
issuerDBOpenIDConnectActivation => 1,
samlDiscoveryProtocolURL => 'http://discovery.example.com/',
samlDiscoveryProtocolActivation => 1,
oidcRPMetaDataExportedVars => {
rp => {
email => "mail",
family_name => "cn",
name => "cn"
}
},
oidcRPMetaDataOptionsExtraClaims => {
rp => {
email => 'email',
},
},
oidcServiceMetaDataIssuer => "http://auth.sp.com",
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",
oidcRPMetaDataOptionsBypassConsent => 0,
oidcRPMetaDataOptionsClientSecret => "rpsecret",
oidcRPMetaDataOptionsUserIDAttr => "",
oidcRPMetaDataOptionsAccessTokenExpiration => 3600
}
},
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-----
",
samlIDPMetaDataExportedAttributes => {
idp => {
mail => "0;mail;;",
uid => "1;uid",
cn => "0;cn"
},
idp2 => {
mail => "0;mail;;",
uid => "1;uid",
cn => "0;cn"
}
},
samlIDPMetaDataOptions => {
idp => {
samlIDPMetaDataOptionsEncryptionMode => 'none',
samlIDPMetaDataOptionsSSOBinding => 'redirect',
samlIDPMetaDataOptionsSLOBinding => 'redirect',
samlIDPMetaDataOptionsSignSSOMessage => 1,
samlIDPMetaDataOptionsSignSLOMessage => 1,
samlIDPMetaDataOptionsCheckSSOMessageSignature => 1,
samlIDPMetaDataOptionsCheckSLOMessageSignature => 1,
samlIDPMetaDataOptionsForceUTF8 => 1,
},
idp2 => {
samlIDPMetaDataOptionsEncryptionMode => 'none',
samlIDPMetaDataOptionsSSOBinding => 'redirect',
samlIDPMetaDataOptionsSLOBinding => 'redirect',
samlIDPMetaDataOptionsSignSSOMessage => 1,
samlIDPMetaDataOptionsSignSLOMessage => 1,
samlIDPMetaDataOptionsCheckSSOMessageSignature => 1,
samlIDPMetaDataOptionsCheckSLOMessageSignature => 1,
samlIDPMetaDataOptionsForceUTF8 => 1,
}
},
samlIDPMetaDataExportedAttributes => {
idp => {
"uid" => "0;uid;;",
"cn" => "1;cn;;",
},
idp2 => {
"uid" => "0;uid;;",
"cn" => "1;cn;;",
},
},
samlIDPMetaDataXML => {
idp => {
samlIDPMetaDataXML =>
samlIDPMetaDataXML( 'idp', 'HTTP-Redirect' )
},
idp2 => {
samlIDPMetaDataXML =>
samlIDPMetaDataXML( 'idp2', 'HTTP-Redirect' )
}
},
samlOrganizationDisplayName => "SP",
samlOrganizationName => "SP",
samlOrganizationURL => "http://www.sp.com",
samlServicePublicKeySig => "-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu4iToYAEmWQxgZDihGVz
MMql1elPn37domWcvXeU2E4yt2hh5jkQHiFjgodfOlNeRIw5QJVlUBwr+CQvbaKR
FXd7BrOhQIDC0TZPRVB0XHarUtsCuDekN4/2GKSzHsoToKUVPWq9thsuek3xkpsJ
GZNX7bglfEc9+QQpYTqN1rkdN1PVU0epNMokFFGho5pLRqLUV5+I/QXAL49jfTja
Sxsp4UndTI8/+mGSRSq+nrT2zyQRM/vkj5vR9ZVz67HO/+Wk3Mx6RAwkVcMdgMAq
Cq8odmbI0yCRZiTL9ybKWRKqWJoKJ0p5+Q2fPEBPupQZR09Jt/JPuLVSsGfCxi9N
qwIDAQAB
-----END PUBLIC KEY-----
",
samlServicePrivateKeyEnc => "-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAsRaod2RZ8hMFBl+VhsnhyPM8l/Fj1obnBxfQIaWuHFIFfXiG
e/CYHuZ5QJQLnZxHMJX6LL3Sh+Usog3p0jpijpcg0QgfBSEkfopKTgReYN8DiDIl
l0rV1XdTni7E85Nd1YyNy3ui/ZD+UShWwqu6jLVLR+QUm+/1LIKYb3OCBTvOlY7x
HoP6NSU1+Mr+YzGBUacdO2vnNxe/PQhxIeP1zO0njuqGHkwEpy8rUWRZbbDn31Tm
Kjqlhgtsz5HPhbRaYEExhyepKgBiNz+RyxtYXVhuG8OrWQDoS5gYHSjdw1CTJyix
eJwyoqA9RGYguG5nh9zndi3LWAh7Z0lx+tIz+wIDAQABAoIBAEkZrk8iiJKJ0WAx
IrsyKNbXuWKLTYgnxcRCyzKofrfID+YcU39j8JeI0fKbajQUZ7qhnlTLwtU//+2h
SqzyVu6/add/v7ZRWQw3L7cGzKK2THHzKVtLk/t7N3QroDdf1LMrQvkFP2HmcWS0
/yN62hXtXHb/qpY4Nn+6JQyUpM5dkv8S/QjDl2NTdyWrXKzWp+4I3QLQ20f4zym+
ir7RennziMc0HlQNcTjGAUbFULtdqEfSFWhNK7UjiRY+S0XV2xJIbGjnxUQH62fS
w1ZzYsF7sBtoSckvfL4WfGbylhOVnliU05RLU2c67PRjj1Gskoslq1Ow/3DHR7rI
BSBpV8ECgYEA1eHfcog7xQGDkW+cshJtFPFx+9MegB58gFW1rl0rn+tfbexvoSEA
7G7EOTyaU6OAI+8StiRT6AYTgEU7PMM9zDykdGIWj3h0OpHGA86xhEiiaaM2DDRv
/DEKRVlEdmRLLLY28pJVHOMYomia3mb2VKZGg2VfGtSfjg1GXD3I8OECgYEA0/X0
U55KjZ1JQTPUgFc1WK1NxX9MaH+NcpDaolEUy3Qf3QTbfws+a9K3vwCn7EpQhrfs
I6RVUtwFdCyfl/jzBY9Gykkg03sMgW7Qw2SCCsSt05M+jDtBbNJ7esP6PAeKFvXZ
ZWhdeiAa4kM/P6gtvZXQ4tY4LkSbcd6b0SzzFFsCgYBjMsusFzuBd95JyfZnMNye
5gzzu0teKMWd0CLfqB7foQ81sH9lwCTpg8ZGtbDuMdrwz6ViDR9NceQBjhqXaAZ1
f3rW79d+22Ms9wdcJLV4oSeSzzv2FSwLT8NvvqNeNc4YArshbnVDXKDEUrfhhueh
Ay2ZK58clpkaDVYg2hckgQKBgG3KuhtSI/YE4fwXN9yez7A2XNGPZem/IGqWo9lu
PGJCrXqT2IqPLW82gB083r6jo+CUhonTxqqb82tA7g4PUvqvQ5Dmnk1NMKYe255K
gp3HUO8GF2EWFIak5Hcr6oOLuDi6cjh3/euTk7ld8fYsTD0mzEOjiQhWW1p5X6bT
LLp/AoGAHvkxA1NM1HJ3myAREbwNXxRy/nhNt4mwMkZ6hPQsW/Eg/3r7j6MJOFrm
U8AJJjDGKe6nlXhhnMoQfJzAc0cYNgjktmJXW27fHGIwt/2QwYNFHPK3s7HTrfH6
7T4XKT3yGeeeyC2soKJQPlGB+ETdIUnXa7eo9KVWtMTgISyx1Qk=
-----END RSA PRIVATE KEY-----
",
samlServicePrivateKeySig => "-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAu4iToYAEmWQxgZDihGVzMMql1elPn37domWcvXeU2E4yt2hh
5jkQHiFjgodfOlNeRIw5QJVlUBwr+CQvbaKRFXd7BrOhQIDC0TZPRVB0XHarUtsC
uDekN4/2GKSzHsoToKUVPWq9thsuek3xkpsJGZNX7bglfEc9+QQpYTqN1rkdN1PV
U0epNMokFFGho5pLRqLUV5+I/QXAL49jfTjaSxsp4UndTI8/+mGSRSq+nrT2zyQR
M/vkj5vR9ZVz67HO/+Wk3Mx6RAwkVcMdgMAqCq8odmbI0yCRZiTL9ybKWRKqWJoK
J0p5+Q2fPEBPupQZR09Jt/JPuLVSsGfCxi9NqwIDAQABAoIBABE0Cjb6g3F+23vD
SsRSeiqzrFrfOEqtXK+VGrfWzHS7V7Ozg6eW/H+HGJXUzUuQcklfg7EFA3JB41a0
GxW3oA+UElkfCV/dcAG5NbRqGQKScEz9glZb5FikgDLqiPP+HabS/gvQSu71t2HI
3KxSRJdwCNTp26Z28pxxYUpmELTtxd9vlHjffit2Mnt2uc8hOtFHdNavfYwvYH7o
bmlckp7b/JVOy2Yy21O94ZWkE498jXyn71Gr+V1cnJ0RrmYbhQqIvFpFHj98Pf4O
if3c4YmBcZ4t7PUsZUYF3ooWt8k/mdigQC3D6p80OKe+wUTYKcCN0ZdFbiURv9pg
CsqLh+ECgYEA9vA+9QfzvXC7S5yXgTkuRiusPlNye/AiyA/0oGjmjFZ1YNsT7awH
6BjW6WE+rS4elKJu1GaefM/cDguH4ZmJc+eKgi4LDCqYw9rr9les3aneBc8demd3
O/Ej1Pud1QxXArBNfBYo08vEqwST9P89clJC5090U6bGK2E0rTVu1w0CgYEAwmpG
9LbOFeGCPmwX7Avuk7tQQfRSV6q9TFZo+HxDfKYvxec846l1vBenY2rrgYhtolYJ
YS795LYgbSWRxGfgr1GuIbP5GsjHy6/1o6bS8M++GJ7KHArb0QLAYyQweqqb164A
NvHJkveueWnxzeOlD9j2fcjEnBHwTnqjG+17CZcCgYEAqMXawa4FsNxzpmIISpHC
RsNindZ60Kp3mzUMhPYtXI1a/C+/lxmU7dTMTgXgyIxU6lF6XkEk4TlPtWm8HTzK
7SS7Te4aLt6OOo5N57hUtct7q4y7IQXGQHm3e8HdRdeBQJ0u2Dhs/xSt/hTK6w/n
91Kx11Y+s02w88UkM53pe6ECgYAF/UYwVc1liSv9BlF6WSfBb1zam09KGh1405Sq
SxG9LlV8cFJE5TyWTdg/TNTyiaRvAt2JG+yAdkfrdOPXvCeE3yxRJ30+IP9evA4C
O6p19sBxe7rYQFFjUAVjSIMh1E22yEqDZtGB8JV0chob8K5uHY4CdAPylu7jTA3o
V1maAwKBgQCSGQ3yzsk4EGN2xd/JdgGDzhKyTZTQKMWYqQcsYxRAQ7Paj7u+Wkgv
dBeKcI0HwgpLy5ZohSd2erqieIsW0pEbJWCmos4IcO8tgNfEOa5WXYdyLbj5tFwt
ctu4/BJdijqfpMAtG8pv6k09gYjfASVytXmydGcs/0rVKYCRQA8Tow==
-----END RSA PRIVATE KEY-----
",
samlServicePublicKeyEnc => "-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsRaod2RZ8hMFBl+Vhsnh
yPM8l/Fj1obnBxfQIaWuHFIFfXiGe/CYHuZ5QJQLnZxHMJX6LL3Sh+Usog3p0jpi
jpcg0QgfBSEkfopKTgReYN8DiDIll0rV1XdTni7E85Nd1YyNy3ui/ZD+UShWwqu6
jLVLR+QUm+/1LIKYb3OCBTvOlY7xHoP6NSU1+Mr+YzGBUacdO2vnNxe/PQhxIeP1
zO0njuqGHkwEpy8rUWRZbbDn31TmKjqlhgtsz5HPhbRaYEExhyepKgBiNz+RyxtY
XVhuG8OrWQDoS5gYHSjdw1CTJyixeJwyoqA9RGYguG5nh9zndi3LWAh7Z0lx+tIz
+wIDAQAB
-----END PUBLIC KEY-----
",
samlSPSSODescriptorAuthnRequestsSigned => 1,
},
}
);
}
sub rp {
my ( $jwks, $metadata ) = @_;
return LLNG::Manager::Test->new( {
ini => {
logLevel => $debug,
domain => 'rp.com',
portal => 'http://auth.rp.com',
authentication => 'OpenIDConnect',
userDB => 'Same',
oidcOPMetaDataExportedVars => {
sp => {
cn => "name",
uid => "sub",
sn => "family_name",
mail => "email"
}
},
oidcOPMetaDataOptions => {
sp => {
oidcOPMetaDataOptionsJWKSTimeout => 0,
oidcOPMetaDataOptionsClientSecret => "rpsecret",
oidcOPMetaDataOptionsScope => "openid profile email",
oidcOPMetaDataOptionsStoreIDToken => 0,
oidcOPMetaDataOptionsDisplay => "",
oidcOPMetaDataOptionsClientID => "rpid",
oidcOPMetaDataOptionsConfigurationURI =>
"https://auth.sp.com/.well-known/openid-configuration"
}
},
oidcOPMetaDataJWKS => {
sp => $jwks,
},
oidcOPMetaDataJSON => {
sp => $metadata,
},
}
}
);
}

View File

@ -66,7 +66,7 @@ count(1);
# Expired token
sleep 3;
$query =~ s/user=dwho/user=rtyler/;
$query =~ s/user=/user=rtyler/;
$query =~ s/url=/url=http%3A%2F%2Ftest1.example.com/;
ok(
@ -87,7 +87,7 @@ count(2);
# Valid token
$query =~ s/user=/user=rtyler/;
$query =~ s/url=/url=test1.example.com/;
$query =~ s/url=/url=http%3A%2F%2Ftest1.example.com/;
ok(
$res = $client->_post(

View File

@ -18,6 +18,7 @@ my $client = LLNG::Manager::Test->new( {
portalMainLogo => 'common/logos/logo_llng_old.png',
checkUser => 1,
requireToken => 0,
checkUserIdRule => '$uid ne "msmith"',
checkUserDisplayPersistentInfo => 1,
checkUserDisplayEmptyValues => 1,
}
@ -28,7 +29,7 @@ my $client = LLNG::Manager::Test->new( {
ok(
$res = $client->_post(
'/',
IO::String->new('user=msmith&password=msmith'),
IO::String->new('user=rtyler&password=rtyler'),
length => 27,
accept => 'text/html',
),
@ -73,6 +74,7 @@ expectRedirection( $res, 'http://auth.example.com/' );
# CheckUser form -> granted
# ------------------------
ok(
$res = $client->_get(
'/checkuser',
@ -90,6 +92,14 @@ ok( $res->[2]->[0] =~ m%<span trspan="checkUser">%, 'Found trspan="checkUser"' )
or explain( $res->[2]->[0], 'trspan="checkUser"' );
count(1);
ok( $res->[2]->[0] =~ m%<td class="text-left">_user</td>%,
'Found attribute _user' )
or explain( $res->[2]->[0], 'Attribute _user' );
ok( $res->[2]->[0] =~ m%<td class="text-left">dwho</td>%,
'Found value dwho' )
or explain( $res->[2]->[0], 'Value dwho' );
count(2);
$query =~ s/user=dwho/user=rtyler/;
$query =~ s/url=/url=http%3A%2F%2Ftry.example.com/;
ok(
@ -111,12 +121,7 @@ ok( $res->[2]->[0] =~ m%<span trspan="VHnotFound">%,
or explain( $res->[2]->[0], 'trspan="VHnotFound"' );
count(1);
( $host, $url, $query ) =
expectForm( $res, undef, '/checkuser', 'user', 'url' );
ok( $res->[2]->[0] =~ m%<span trspan="checkUser">%, 'Found trspan="checkUser"' )
or explain( $res->[2]->[0], 'trspan="checkUser"' );
count(1);
# Request with good VH & user
$query =~
s/url=http%3A%2F%2Ftry.example.com/url=http%3A%2F%2Ftest1.example.com/;
ok(
@ -168,9 +173,8 @@ ok( $res->[2]->[0] =~ m%<td class="text-left">uid</td>%, 'Found uid' )
or explain( $res->[2]->[0], 'Attribute Value uid' );
count(11);
$query =~ s/user=dwho/user=msmith/;
$query =~
s/url=http%3A%2F%2Ftest1.example.com/url=http%3A%2F%2Fmanager.example.com%2Fmanager.html/;
# Request a forbidden identity
$query =~ s/user=rtyler/user=msmith/;
ok(
$res = $client->_post(
'/checkuser',
@ -183,9 +187,47 @@ ok(
);
ok(
$res->[2]->[0] =~
m%<div class="alert alert-danger"><b><span trspan="forbidden"></span></b></div>%,
'Found trspan="forbidden"'
) or explain( $res->[2]->[0], 'trspan="forbidden"' );
m%<div class="message message-positive alert"><span trspan="PE5"></span></div>%,
' PE5 found'
) or explain( $res->[2]->[0], 'PE5 - Forbidden identity' );
count(2);
# Request an unknown identity
$query =~ s/user=msmith/user=davros/;
ok(
$res = $client->_post(
'/checkuser',
IO::String->new($query),
cookie => "lemonldap=$id",
length => length($query),
accept => 'text/html',
),
'POST checkuser'
);
ok(
$res->[2]->[0] =~
m%<div class="message message-positive alert"><span trspan="PE5"></span></div>%,
' PE5 found'
) or explain( $res->[2]->[0], 'PE5 - Unknown identity' );
count(2);
# Request an unvalid identity
$query =~ s/user=dwho/user=%*'/;
ok(
$res = $client->_post(
'/checkuser',
IO::String->new($query),
cookie => "lemonldap=$id",
length => length($query),
accept => 'text/html',
),
'POST checkuser'
);
ok(
$res->[2]->[0] =~
m%<div class="message message-positive alert"><span trspan="PE5"></span></div>%,
' PE5 found'
) or explain( $res->[2]->[0], 'PE5 - Unvalid identity' );
count(2);
$client->logout($id);

View File

@ -18,15 +18,21 @@ my $client = LLNG::Manager::Test->new( {
portalMainLogo => 'common/logos/logo_llng_old.png',
requireToken => 0,
checkUser => 1,
impersonationRule => 1,
impersonationRule => '$uid ne "msmith"',
impersonationIdRule => '$uid ne "msmith"',
checkUserDisplayPersistentInfo => 0,
checkUserDisplayEmptyValues => 0,
impersonationMergeSSOgroups => 0,
macros => {
test_impersonation => '"$real__user/$_user"',
_whatToTrace =>
'$_auth eq "SAML" ? "$_user@$_idpConfKey" : $_user',
},
}
}
);
## Try to authenticate with bad spoofed user
## Try to impersonate with a bad spoofed user
ok( $res = $client->_get( '/', accept => 'text/html' ), 'Get Menu', );
count(1);
my ( $host, $url, $query ) =
@ -45,16 +51,133 @@ ok(
'Auth query'
);
ok( $res->[2]->[0] =~ m%<span trmsg="40"></span>%, ' PE40 found' )
or print STDERR Dumper( $res->[2]->[0] );
or explain( $res->[2]->[0], "PE40 - Bad formed user" );
count(2);
my $id = expectCookie($res);
$client->logout($id);
## Try to impersonate with a forbidden identity
ok( $res = $client->_get( '/', accept => 'text/html' ), 'Get Menu', );
count(1);
( $host, $url, $query ) =
expectForm( $res, '#', undef, 'user', 'password', 'spoofId' );
$query =~ s/user=/user=rtyler/;
$query =~ s/password=/password=rtyler/;
$query =~ s/spoofId=/spoofId=msmith/;
ok(
$res = $client->_post(
'/',
IO::String->new($query),
length => length($query),
accept => 'text/html',
),
'Auth query'
);
ok(
$res->[2]->[0] =~
m%<div class="message message-negative alert"><span trmsg="5"></span></div>%,
' PE5 found'
) or explain( $res->[2]->[0], "PE5 - Forbidden identity" );
count(2);
$id = expectCookie($res);
$client->logout($id);
## An unauthorized user try to impersonate
ok( $res = $client->_get( '/', accept => 'text/html' ), 'Get Menu', );
count(1);
( $host, $url, $query ) =
expectForm( $res, '#', undef, 'user', 'password', 'spoofId' );
$query =~ s/user=/user=msmith/;
$query =~ s/password=/password=msmith/;
$query =~ s/spoofId=/spoofId=rtyler/;
ok(
$res = $client->_post(
'/',
IO::String->new($query),
length => length($query),
accept => 'text/html',
),
'Auth query'
);
ok(
$res->[2]->[0] =~
m%<div class="message message-negative alert"><span trmsg="93"></span></div>%,
' PE93 found'
) or explain( $res->[2]->[0], "PE93 - Impersonation service not allowed" );
count(2);
$id = expectCookie($res);
$client->logout($id);
## An unauthorized user to impersonate tries to authenticate
ok( $res = $client->_get( '/', accept => 'text/html' ), 'Get Menu', );
count(1);
( $host, $url, $query ) =
expectForm( $res, '#', undef, 'user', 'password', 'spoofId' );
$query =~ s/user=/user=msmith/;
$query =~ s/password=/password=msmith/;
ok(
$res = $client->_post(
'/',
IO::String->new($query),
length => length($query),
accept => 'text/html',
),
'Auth query'
);
count(1);
$id = expectCookie($res);
expectRedirection( $res, 'http://auth.example.com/' );
# CheckUser form
# ------------------------
ok(
$res = $client->_get(
'/checkuser',
cookie => "lemonldap=$id",
accept => 'text/html'
),
'CheckUser form',
);
count(1);
( $host, $url, $query ) =
expectForm( $res, undef, '/checkuser', 'user', 'url' );
ok( $res->[2]->[0] =~ m%<span trspan="checkUser">%, 'Found trspan="checkUser"' )
or explain( $res->[2]->[0], 'trspan="checkUser"' );
count(1);
ok(
$res = $client->_post(
'/checkuser',
IO::String->new($query),
cookie => "lemonldap=$id",
length => length($query),
accept => 'text/html',
),
'POST checkuser'
);
count(1);
ok( $res->[2]->[0] =~ m%<td class="align-middle">test_impersonation</td>%,
'Found macro test_impersonation' )
or explain( $res->[2]->[0], 'test_impersonation' );
ok( $res->[2]->[0] =~ m%<td class="align-middle">msmith/msmith</td>%,
'Found msmith/msmith' )
or explain( $res->[2]->[0], 'Found msmith/msmith' );
count(2);
$client->logout($id);
## Try to authenticate
ok( $res = $client->_get( '/', accept => 'text/html' ), 'Get Menu', );
count(1);
my ( $host, $url, $query ) =
( $host, $url, $query ) =
expectForm( $res, '#', undef, 'user', 'password', 'spoofId' );
$query =~ s/user=/user=rtyler/;
@ -146,7 +269,13 @@ ok( $res->[2]->[0] =~ m%<td class="text-left">real_uid</td>%, 'Found real_uid' )
or explain( $res->[2]->[0], 'real_groups' );
ok( $res->[2]->[0] =~ m%<td class="text-left">rtyler</td>%, 'Found rtyler' )
or explain( $res->[2]->[0], 'su' );
count(14);
ok( $res->[2]->[0] =~ m%<td class="align-middle">test_impersonation</td>%,
'Found macro test_impersonation' )
or explain( $res->[2]->[0], 'test_impersonation' );
ok( $res->[2]->[0] =~ m%<td class="align-middle">rtyler/dwho</td>%,
'Found rtyler/dwo' )
or explain( $res->[2]->[0], 'Found rtyler/dwo' );
count(16);
$client->logout($id);
clean_sessions();

View File

@ -35,7 +35,7 @@
"key": "qwertyui",
"locationRules": {
"auth.example.com" : {
"(?#checkUser)^/checkuser" : "$uid eq \"dwho\" or $uid eq \"rtyler\"",
"(?#checkUser)^/checkuser" : "$uid eq \"dwho\" or $uid eq \"msmith\"",
"(?#errors)^/lmerror/": "accept",
"default" : "accept"
},