Unit test for #2799

This commit is contained in:
Maxime Besson 2022-09-20 18:42:03 +02:00
parent fe9980328b
commit de2f0b0731
2 changed files with 388 additions and 201 deletions

View File

@ -11,8 +11,7 @@ BEGIN {
require 't/saml-lib.pm'; require 't/saml-lib.pm';
} }
my $maintests = 27; my $debug = 'error';
my $debug = 'error';
my ( $issuer, $sp, $res ); my ( $issuer, $sp, $res );
# Redefine LWP methods for tests # Redefine LWP methods for tests
@ -28,234 +27,393 @@ LWP::Protocol::PSGI->register(
SKIP: { SKIP: {
eval "use Lasso"; eval "use Lasso";
if ($@) { if ($@) {
skip 'Lasso not found', $maintests; skip 'Lasso not found';
} }
# Initialization # Initialization
$issuer = register( 'issuer', \&issuer ); $issuer = register( 'issuer', \&issuer );
$sp = register( 'sp', \&sp ); $sp = register( 'sp', \&sp );
# Try to authenticate subtest "SP-initiated flow, unauthorized user" => sub {
# -------------------
switch ('issuer');
my $res;
ok(
$res = $issuer->_post(
'/', IO::String->new('user=french&password=french'),
length => 27
),
'Auth query'
);
expectOK($res);
my $id = expectCookie($res);
# Simple SP access # SP-initiated flow
switch ('sp'); my $res;
ok( switch ('sp');
$res = $sp->_get( ok(
'/', accept => 'text/html', $res = $sp->_get(
), '/', accept => 'text/html',
'Unauth SP request' ),
); 'Unauth SP request'
expectOK($res); );
my ( $host, $url, $s ) = expectOK($res);
expectAutoPost( $res, 'auth.idp.com', '/saml/singleSignOn', my ( $host, $url, $s ) =
'SAMLRequest' ); expectAutoPost( $res, 'auth.idp.com', '/saml/singleSignOn',
my $pdata_hash = expectPdata($res); 'SAMLRequest' );
is( $pdata_hash->{genRequestHookCalled}, my $pdata_hash = expectPdata($res);
1, 'samlGenerateRequestHook called' ); is( $pdata_hash->{genRequestHookCalled},
1, 'samlGenerateRequestHook called' );
# Push SAML request to IdP # Push SAML request to IdP
switch ('issuer'); switch ('issuer');
ok( ok(
$res = $issuer->_post( $res = $issuer->_post(
$url, $url,
IO::String->new($s), IO::String->new($s),
accept => 'text/html', accept => 'text/html',
length => length($s) length => length($s)
), ),
'Post SAML request to IdP' 'Post SAML request to IdP'
); );
expectOK($res); expectOK($res);
my $pdata = 'lemonldappdata=' . expectCookie( $res, 'lemonldappdata' ); my $pdata = 'lemonldappdata=' . expectCookie( $res, 'lemonldappdata' );
my $rawCookie = getHeader( $res, 'Set-Cookie' ); my $rawCookie = getHeader( $res, 'Set-Cookie' );
ok( $rawCookie =~ /;\s*SameSite=None/, 'Found SameSite=None' ); ok( $rawCookie =~ /;\s*SameSite=None/, 'Found SameSite=None' );
# Try to authenticate with an unauthorized user to IdP # Try to authenticate with an unauthorized user to IdP
$s = "user=dwho&password=dwho&$s"; $s = "user=dwho&password=dwho&$s";
ok( ok(
$res = $issuer->_post( $res = $issuer->_post(
$url, $url,
IO::String->new($s), IO::String->new($s),
accept => 'text/html', accept => 'text/html',
cookie => $pdata, cookie => $pdata,
length => length($s), length => length($s),
), ),
'Post authentication' 'Post authentication'
); );
ok( $res->[2]->[0] =~ /trmsg="89"/, 'Reject reason is 89' ) ok( $res->[2]->[0] =~ /trmsg="89"/, 'Reject reason is 89' )
or print STDERR Dumper( $res->[2]->[0] ); or print STDERR Dumper( $res->[2]->[0] );
};
# Simple SP access subtest "SP-initiated flow, authorized user" => sub {
ok(
$res = $sp->_get(
'/', accept => 'text/html',
),
'Unauth SP request'
);
expectOK($res);
( $host, $url, $s ) =
expectAutoPost( $res, 'auth.idp.com', '/saml/singleSignOn',
'SAMLRequest' );
# Push SAML request to IdP my $res;
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 # Simple SP access
$s = "user=french&password=french&$s"; ok(
ok( $res = $sp->_get(
$res = $issuer->_post( '/', accept => 'text/html',
$url, ),
IO::String->new($s), 'Unauth SP request'
accept => 'text/html', );
cookie => $pdata, expectOK($res);
length => length($s), my ( $host, $url, $s ) =
), expectAutoPost( $res, 'auth.idp.com', '/saml/singleSignOn',
'Post authentication' 'SAMLRequest' );
);
my $idpId = expectCookie($res);
# Expect pdata to be cleared # Push SAML request to IdP
$pdata = expectCookie( $res, 'lemonldappdata' ); ok(
ok( $pdata !~ 'issuerRequestsaml', 'SAML request cleared from pdata' ) $res = $issuer->_post(
or explain( $pdata, 'not issuerRequestsaml' ); $url,
IO::String->new($s),
accept => 'text/html',
length => length($s)
),
'Post SAML request to IdP'
);
expectOK($res);
my $pdata = 'lemonldappdata=' . expectCookie( $res, 'lemonldappdata' );
( $host, $url, $s ) = # Try to authenticate with an authorized user to IdP
expectAutoPost( $res, 'auth.sp.com', '/saml/proxySingleSignOnPost', $s = "user=french&password=french&$s";
'SAMLResponse' ); ok(
$res = $issuer->_post(
$url,
IO::String->new($s),
accept => 'text/html',
cookie => $pdata,
length => length($s),
),
'Post authentication'
);
my $idpId = expectCookie($res);
my $resp = expectSamlResponse($s); # Expect pdata to be cleared
like( $pdata = expectCookie( $res, 'lemonldappdata' );
$resp, ok( $pdata !~ 'issuerRequestsaml', 'SAML request cleared from pdata' )
qr/AuthnInstant="2000-01-01T00:00:01Z"/, or explain( $pdata, 'not issuerRequestsaml' );
"Found AuthnInstant modified by hook"
);
$pdata_hash = expectPdata($res); ( $host, $url, $s ) =
is( $pdata_hash->{gotRequestHookCalled}, expectAutoPost( $res, 'auth.sp.com', '/saml/proxySingleSignOnPost',
1, 'samlGotRequestHookCalled called' ); 'SAMLResponse' );
# Post SAML response to SP my $resp = expectSamlResponse($s);
switch ('sp'); like(
ok( $resp,
$res = $sp->_post( qr/AuthnInstant="2000-01-01T00:00:01Z"/,
$url, IO::String->new($s), "Found AuthnInstant modified by hook"
accept => 'text/html', );
length => length($s),
),
'Post SAML response to SP'
);
# Verify authentication on SP my $pdata_hash = expectPdata($res);
expectRedirection( $res, 'http://auth.sp.com' ); is( $pdata_hash->{gotRequestHookCalled},
my $spId = expectCookie($res); 1, 'samlGotRequestHookCalled called' );
$rawCookie = getHeader( $res, 'Set-Cookie' );
ok( $rawCookie =~ /;\s*SameSite=None/, 'Found SameSite=None' );
ok( $res = $sp->_get( '/', cookie => "lemonldap=$spId" ), 'Get / on SP' ); # Post SAML response to SP
expectOK($res); switch ('sp');
expectAuthenticatedAs( $res, 'fa@badwolf.org@idp' ); ok(
$res = $sp->_post(
$url, IO::String->new($s),
accept => 'text/html',
length => length($s),
),
'Post SAML response to SP'
);
# Verify UTF-8 # Verify authentication on SP
ok( $res = $sp->_get("/sessions/global/$spId"), 'Get UTF-8' ); expectRedirection( $res, 'http://auth.sp.com' );
expectOK($res); my $spId = expectCookie($res);
ok( $res = eval { JSON::from_json( $res->[2]->[0] ) }, ' GET JSON' ) my $rawCookie = getHeader( $res, 'Set-Cookie' );
or print STDERR $@; ok( $rawCookie =~ /;\s*SameSite=None/, 'Found SameSite=None' );
is( $res->{gotResponseHookCalled}, 1, 'samlGotResponseHook called' );
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( '/', cookie => "lemonldap=$spId" ),
ok( 'Get / on SP' );
$res = $sp->_get( expectOK($res);
'/', expectAuthenticatedAs( $res, 'fa@badwolf.org@idp' );
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 # Verify UTF-8
switch ('issuer'); ok( $res = $sp->_get("/sessions/global/$spId"), 'Get UTF-8' );
ok( expectOK($res);
$res = $issuer->_post( ok( $res = eval { JSON::from_json( $res->[2]->[0] ) }, ' GET JSON' )
$url, or print STDERR $@;
IO::String->new($s), is( $res->{gotResponseHookCalled}, 1, 'samlGotResponseHook called' );
accept => 'text/html', ok( $res->{cn} eq 'Frédéric Accents', 'UTF-8 values' )
cookie => "lemonldap=$idpId", or explain( $res, 'cn => Frédéric Accents' );
length => length($s)
),
'Post SAML logout request to IdP'
);
( $host, $url, $s ) =
expectAutoPost( $res, 'auth.sp.com', '/saml/proxySingleLogoutReturn',
'SAMLResponse' );
my $removedCookie = expectCookie($res); # Logout initiated by SP
is( $removedCookie, 0, "IDP Cookie removed" ); 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' );
# Post SAML response to SP # Push SAML logout request to IdP
switch ('sp'); switch ('issuer');
ok( ok(
$res = $sp->_post( $res = $issuer->_post(
$url, IO::String->new($s), $url,
accept => 'text/html', IO::String->new($s),
length => length($s), accept => 'text/html',
), cookie => "lemonldap=$idpId",
'Post SAML response to SP' length => length($s)
); ),
expectRedirection( $res, 'http://auth.sp.com' ); 'Post SAML logout request to IdP'
);
( $host, $url, $s ) =
expectAutoPost( $res, 'auth.sp.com', '/saml/proxySingleLogoutReturn',
'SAMLResponse' );
# Test if logout is done my $removedCookie = expectCookie($res);
switch ('issuer'); is( $removedCookie, 0, "IDP Cookie removed" );
ok(
$res = $issuer->_get(
'/', cookie => "lemonldap=$idpId",
),
'Test if user is reject on IdP'
);
expectReject($res);
switch ('sp'); # Post SAML response to SP
ok( switch ('sp');
$res = $sp->_get( ok(
'/', $res = $sp->_post(
accept => 'text/html', $url, IO::String->new($s),
cookie => "lemonldap=$spId" accept => 'text/html',
), length => length($s),
'Test if user is reject on SP' ),
); 'Post SAML response to SP'
expectOK($res); );
expectAutoPost( $res, 'auth.idp.com', '/saml/singleSignOn', 'SAMLRequest' ); expectRedirection( $res, 'http://auth.sp.com' );
# Test if logout is done
switch ('issuer');
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 => "lemonldap=$spId"
),
'Test if user is reject on SP'
);
expectOK($res);
expectAutoPost( $res, 'auth.idp.com', '/saml/singleSignOn',
'SAMLRequest' );
};
subtest "SP-initiated flow, authorized user" => sub {
my $res;
# Simple SP access
ok(
$res = $sp->_get(
'/', accept => 'text/html',
),
'Unauth SP request'
);
expectOK($res);
my ( $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);
my $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);
# Expect pdata to be cleared
$pdata = expectCookie( $res, 'lemonldappdata' );
ok( $pdata !~ 'issuerRequestsaml', 'SAML request cleared from pdata' )
or explain( $pdata, 'not issuerRequestsaml' );
( $host, $url, $s ) =
expectAutoPost( $res, 'auth.sp.com', '/saml/proxySingleSignOnPost',
'SAMLResponse' );
my $resp = expectSamlResponse($s);
like(
$resp,
qr/AuthnInstant="2000-01-01T00:00:01Z"/,
"Found AuthnInstant modified by hook"
);
my $pdata_hash = expectPdata($res);
is( $pdata_hash->{gotRequestHookCalled},
1, 'samlGotRequestHookCalled called' );
# Post SAML response to SP
switch ('sp');
ok(
$res = $sp->_post(
$url, IO::String->new($s),
accept => 'text/html',
length => length($s),
),
'Post SAML response to SP'
);
# Verify authentication on SP
expectRedirection( $res, 'http://auth.sp.com' );
my $spId = expectCookie($res);
my $rawCookie = getHeader( $res, 'Set-Cookie' );
ok( $rawCookie =~ /;\s*SameSite=None/, 'Found SameSite=None' );
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 $@;
is( $res->{gotResponseHookCalled}, 1, 'samlGotResponseHook called' );
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&url=' . encodeUrl("http://test1.example.com"),
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'
);
( $host, $url, $s ) =
expectAutoPost( $res, 'auth.sp.com', '/saml/proxySingleLogoutReturn',
'SAMLResponse' );
my $removedCookie = expectCookie($res);
is( $removedCookie, 0, "IDP Cookie removed" );
# Post SAML response to SP
switch ('sp');
ok(
$res = $sp->_post(
$url, IO::String->new($s),
accept => 'text/html',
length => length($s),
),
'Post SAML response to SP'
);
expectRedirection( $res, 'http://test1.example.com' );
# Test if logout is done
switch ('issuer');
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 => "lemonldap=$spId"
),
'Test if user is reject on SP'
);
expectOK($res);
expectAutoPost( $res, 'auth.idp.com', '/saml/singleSignOn',
'SAMLRequest' );
};
} }
count($maintests);
clean_sessions(); clean_sessions();
done_testing( count() ); done_testing();
sub issuer { sub issuer {
return LLNG::Manager::Test->new( { return LLNG::Manager::Test->new( {

View File

@ -883,6 +883,35 @@ has ini => (
=head3 Methods =head3 Methods
=head4 login($client, $uid, $getParams)
Do a simple login using the Demo backend
=cut
sub login {
local $Test::Builder::Level = $Test::Builder::Level + 1;
my ( $self, $uid, $getParams ) = @_;
my $res;
$getParams ||= {};
my $query = main::buildForm( {
user => $uid,
password => $uid,
%$getParams,
}
);
main::ok(
$res = $self->_post(
'/',
IO::String->new($query),
length => length($query),
),
'Auth query'
);
main::expectOK($res);
my $id = main::expectCookie($res);
return $id;
}
=head4 logout($id) =head4 logout($id)
Launch a C</?logout=1> request an test: Launch a C</?logout=1> request an test: