BEGIN {
require
't/test-lib.pm'
;
require
't/oidc-lib.pm'
;
require
't/saml-lib.pm'
;
}
my
$maintests
= 17;
my
$debug
=
'error'
;
my
(
$idp
,
$sp
,
$rp
,
$res
);
LWP::Protocol::PSGI->register(
sub
{
my
$req
= Plack::Request->new(
@_
);
ok(
$req
->uri =~ m
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
' 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
;
}
$idp
= register(
'idp'
, \
&idp
);
$sp
= register(
'sp'
, \
&sp
);
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];
$rp
= register(
'rp'
,
sub
{ rp(
$jwks
,
$metadata
) } );
ok(
$res
=
$rp
->_get(
'/'
,
accept
=>
'text/html'
),
'Unauth SP request'
);
my
(
$url
,
$query
) =
expectRedirection(
$res
,
switch (
'sp'
);
ok(
$res
=
$sp
->_get(
$url
,
query
=>
$query
,
accept
=>
'text/html'
,
),
"Push request to OP, endpoint $url"
);
my
$spPdata
=
'lemonldappdata='
. expectCookie(
$res
,
'lemonldappdata'
);
(
$url
,
$query
) =
ok(
$res
=
$sp
->_get(
"/"
,
accept
=>
'text/html'
,
cookie
=>
$spPdata
,
),
"Return from WAYF"
);
$spPdata
=
'lemonldappdata='
. expectCookie(
$res
,
'lemonldappdata'
);
my
(
$host
,
$tmp
);
(
$url
,
$query
) = expectRedirection(
$res
,
switch (
'idp'
);
ok(
$res
=
$idp
->_get(
$url
,
query
=>
$query
,
accept
=>
'text/html'
,
),
'Launch SAML request to IdP'
);
my
$idpPdata
=
'lemonldappdata='
. expectCookie(
$res
,
'lemonldappdata'
);
my
$body
=
$res
->[2]->[0];
$body
=~ s/^.*?<form.*?>//s;
$body
=~ s
my
%fields
=
(
$body
=~ /<input type=
"hidden"
.+?name=
"(.+?)"
.+?value=
"(.*?)"
/sg );
$fields
{user} =
$fields
{password} =
'french'
;
$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
);
switch (
'sp'
);
ok(
$res
=
$sp
->_post(
$url
, IO::String->new(
$query
),
length
=>
length
(
$query
),
accept
=>
'text/html'
,
cookie
=>
"$spPdata"
),
'POST SAML response'
);
my
$spId
= expectCookie(
$res
);
(
$url
,
$query
) = expectRedirection(
$res
,
ok(
$res
=
$sp
->_get(
$url
,
query
=>
$query
,
accept
=>
'text/html'
,
cookie
=>
"lemonldap=$spId;$spPdata"
),
'Follow internal redirection from SAML-SP to OIDC-OP'
);
$spPdata
= expectCookie(
$res
,
'lemonldappdata'
);
(
$host
,
$tmp
,
$query
) =
expectForm(
$res
,
undef
,
qr#^/oauth2/authorize#
,
'confirm'
);
ok(
$res
=
$sp
->_get(
'/oauth2/authorize'
,
query
=>
$query
,
accept
=>
'text/html'
,
cookie
=>
"lemonldap=$spId;$spPdata"
),
'Confirm OIDC sharing'
);
switch (
'rp'
);
ok(
$res
=
$rp
->_get(
'/'
,
query
=>
$query
,
accept
=>
'text/html'
),
'Follow redirection to RP'
);
my
$rpId
= expectCookie(
$res
);
ok(
$res
=
$rp
->_get(
'/'
,
query
=>
'logout'
,
cookie
=>
"lemonldap=$rpId"
,
accept
=>
'text/html'
),
'Query RP for logout'
);
(
$url
,
$query
) = expectRedirection(
$res
,
);
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
,
switch (
'idp'
);
ok(
$res
=
$idp
->_get(
$url
,
query
=>
$query
,
cookie
=>
"lemonldap=$idpId"
,
accept
=>
'text/html'
,
),
'Push logout to SAML IdP'
);
(
$url
,
$query
) = expectRedirection(
$res
,
my
$removedCookie
= expectCookie(
$res
);
is(
$removedCookie
, 0,
"SSO cookie removed"
);
switch (
'sp'
);
ok(
$res
=
$sp
->_get(
$url
,
query
=>
$query
,
cookie
=>
"lemonldap=$spId"
,
accept
=>
'text/html'
,
),
'Push logout to SAML IdP'
);
}
count(
$maintests
);
clean_sessions();
done_testing( count() );
sub
idp {
return
LLNG::Manager::Test->new(
{
ini
=> {
logLevel
=>
$debug
,
domain
=>
'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"
,
samlServicePrivateKeyEnc
=> saml_key_idp_private_enc,
samlServicePrivateKeySig
=> saml_key_idp_private_sig,
samlServicePublicKeyEnc
=> saml_key_idp_public_enc,
samlServicePublicKeySig
=> saml_key_idp_public_sig,
samlSPMetaDataXML
=> {
"sp.com"
=> {
samlSPMetaDataXML
=>
samlSPMetaDataXML(
'sp'
,
'HTTP-Redirect'
)
},
},
}
}
);
}
sub
sp {
return
LLNG::Manager::Test->new(
{
ini
=> {
logLevel
=>
$debug
,
domain
=>
'sp.com'
,
authentication
=>
'SAML'
,
userDB
=>
'Same'
,
issuerDBSAMLActivation
=> 0,
issuerDBOpenIDConnectActivation
=> 1,
samlDiscoveryProtocolActivation
=> 1,
oidcRPMetaDataExportedVars
=> {
rp
=> {
email
=>
"mail"
,
family_name
=>
"cn"
,
name
=>
"cn"
}
},
oidcRPMetaDataOptionsExtraClaims
=> {
rp
=> {
email
=>
'email'
,
},
},
oidcServiceAllowHybridFlow
=> 1,
oidcServiceAllowImplicitFlow
=> 1,
oidcServiceAllowAuthorizationCodeFlow
=> 1,
oidcRPMetaDataOptions
=> {
rp
=> {
oidcRPMetaDataOptionsDisplayName
=>
"RP"
,
oidcRPMetaDataOptionsIDTokenExpiration
=> 3600,
oidcRPMetaDataOptionsClientID
=>
"rpid"
,
oidcRPMetaDataOptionsIDTokenSignAlg
=>
"HS512"
,
oidcRPMetaDataOptionsBypassConsent
=> 0,
oidcRPMetaDataOptionsClientSecret
=>
"rpsecret"
,
oidcRPMetaDataOptionsUserIDAttr
=>
""
,
oidcRPMetaDataOptionsAccessTokenExpiration
=> 3600,
oidcRPMetaDataOptionsRedirectUris
=>
}
},
oidcOPMetaDataOptions
=> {},
oidcOPMetaDataJSON
=> {},
oidcOPMetaDataJWKS
=> {},
oidcServiceMetaDataAuthnContext
=> {
'loa-4'
=> 4,
'loa-1'
=> 1,
'loa-5'
=> 5,
'loa-2'
=> 2,
'loa-3'
=> 3
},
oidcServicePrivateKeySig
=> oidc_key_op_private_sig,
oidcServicePublicKeySig
=> oidc_cert_op_public_sig,
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"
,
samlServicePublicKeySig
=> saml_key_sp_public_sig,
samlServicePrivateKeyEnc
=> saml_key_sp_private_enc,
samlServicePrivateKeySig
=> saml_key_sp_private_sig,
samlServicePublicKeyEnc
=> saml_key_sp_public_enc,
samlSPSSODescriptorAuthnRequestsSigned
=> 1,
},
}
);
}
sub
rp {
my
(
$jwks
,
$metadata
) =
@_
;
return
LLNG::Manager::Test->new(
{
ini
=> {
logLevel
=>
$debug
,
domain
=>
'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
=>
}
},
oidcOPMetaDataJWKS
=> {
sp
=>
$jwks
,
},
oidcOPMetaDataJSON
=> {
sp
=>
$metadata
,
},
}
}
);
}