Add OIDC/Choice test (#595)
parent
80db34a4f2
commit
8e458c6a0d
@ -0,0 +1,406 @@ |
||||
use Test::More; |
||||
use strict; |
||||
use IO::String; |
||||
use MIME::Base64; |
||||
|
||||
BEGIN { |
||||
require 't/test-lib.pm'; |
||||
} |
||||
|
||||
my $debug = 'error'; |
||||
my $mainTests = 18; |
||||
my ( $op, $rp, $res ); |
||||
my %handlerOR = ( op => [], rp => [] ); |
||||
|
||||
SKIP: { |
||||
eval { require DBI; require DBD::SQLite; }; |
||||
if ($@) { |
||||
skip 'DBD::SQLite not found', $mainTests; |
||||
} |
||||
my $dbh = DBI->connect("dbi:SQLite:dbname=t/userdb.db"); |
||||
$dbh->do('CREATE TABLE users (user text,password text,name text)'); |
||||
$dbh->do("INSERT INTO users VALUES ('dwho','dwho','Doctor who')"); |
||||
|
||||
# Initialization |
||||
ok( $op = op(), 'OP portal' ); |
||||
|
||||
ok( |
||||
$res = $op->_get('/oauth2/jwks'), |
||||
'Get JWKS, endpoint /oauth2/jwks' |
||||
); |
||||
expectOK($res); |
||||
my $jwks = $res->[2]->[0]; |
||||
|
||||
ok( |
||||
$res = $op->_get('/.well-known/openid-configuration'), |
||||
'Get metadata, endpoint /.well-known/openid-configuration' |
||||
); |
||||
expectOK($res); |
||||
my $metadata = $res->[2]->[0]; |
||||
|
||||
switch ('rp'); |
||||
ok( $rp = rp( $jwks, $metadata ), 'RP portal' ); |
||||
|
||||
# Query RP for auth |
||||
ok( $res = $rp->_get( '/', accept => 'text/html' ), 'Unauth SP request' ); |
||||
my ( $url, $query ) = |
||||
expectRedirection( $res, |
||||
qr#http://auth.op.com(/oauth2/authorize)\?(.*)$# ); |
||||
|
||||
# Push request to OP |
||||
switch ('op'); |
||||
ok( $res = $op->_get( $url, query => $query, accept => 'text/html' ), |
||||
"Push request to OP, endpoint $url" ); |
||||
my ( $host, $tmp ); |
||||
( $host, $tmp, $query ) = expectForm($res); |
||||
|
||||
# Try to authenticate to OP |
||||
$query =~ s/user=&?//; |
||||
$query = "user=dwho&password=dwho&$query"; |
||||
ok( |
||||
$res = $op->_post( |
||||
$url, |
||||
IO::String->new($query), |
||||
accept => 'text/html', |
||||
length => length($query), |
||||
), |
||||
"Post authentication, endpoint $url" |
||||
); |
||||
my $idpId = expectCookie($res); |
||||
( $host, $tmp, $query ) = expectForm( $res, '#', undef, 'confirm' ); |
||||
|
||||
ok( |
||||
$res = $op->_post( |
||||
$url, |
||||
IO::String->new($query), |
||||
accept => 'text/html', |
||||
cookie => "lemonldap=$idpId", |
||||
length => length($query), |
||||
), |
||||
"Post confirmation, endpoint $url" |
||||
); |
||||
|
||||
($query) = expectRedirection( $res, qr#^http://auth.rp.com/?\?(.*)$# ); |
||||
|
||||
# Push OP response to RP |
||||
switch ('rp'); |
||||
|
||||
ok( $res = $rp->_get( '/', query => $query, accept => 'text/html' ), |
||||
'Call openidconnectcallback on RP' ); |
||||
my $spId = expectCookie($res); |
||||
|
||||
switch ('op'); |
||||
ok( |
||||
$res = $op->_get( '/oauth2/checksession.html', accept => 'text.html' ), |
||||
'Check session, endpoint /oauth2/checksession.html' |
||||
); |
||||
expectOK($res); |
||||
ok( getHeader( $res, 'Content-Security-Policy' ) !~ /frame-ancestors/, |
||||
' Frame can be embedded' ) |
||||
or explain( $res->[1], |
||||
'Content-Security-Policy does not contain a frame-ancestors' ); |
||||
|
||||
# Logout initiated by RP |
||||
ok( |
||||
$res = $rp->_get( |
||||
'/', |
||||
query => 'logout', |
||||
cookie => "lemonldap=$spId", |
||||
accept => 'text/html' |
||||
), |
||||
'Query RP for logout' |
||||
); |
||||
( $url, $query ) = expectRedirection( $res, |
||||
qr#http://auth.op.com(/oauth2/logout)\?(post_logout_redirect_uri=.+)$# |
||||
); |
||||
|
||||
# Push logout to OP |
||||
switch ('op'); |
||||
|
||||
ok( |
||||
$res = $op->_get( |
||||
$url, |
||||
query => $query, |
||||
cookie => "lemonldap=$idpId", |
||||
accept => 'text/html' |
||||
), |
||||
"Push logout request to OP, endpoint $url" |
||||
); |
||||
|
||||
( $host, $tmp, $query ) = expectForm( $res, '#', undef, 'confirm' ); |
||||
|
||||
ok( |
||||
$res = $op->_post( |
||||
$url, IO::String->new($query), |
||||
length => length($query), |
||||
cookie => "lemonldap=$idpId", |
||||
accept => 'text/html', |
||||
), |
||||
"Confirm logout, endpoint $url" |
||||
); |
||||
|
||||
( $url, $query ) = expectRedirection( $res, qr#.# ); |
||||
|
||||
# Test logout endpoint without session |
||||
ok( |
||||
$res = $op->_get( |
||||
'/oauth2/logout', |
||||
accept => 'text/html', |
||||
query => 'post_logout_redirect_uri=http://auth.rp.com/?logout=1' |
||||
), |
||||
'logout endpoint with redirect, endpoint /oauth2/logout' |
||||
); |
||||
expectRedirection( $res, 'http://auth.rp.com/?logout=1' ); |
||||
|
||||
ok( $res = $op->_get('/oauth2/logout'), |
||||
'logout endpoint, endpoint /oauth2/logout' ); |
||||
expectReject($res); |
||||
|
||||
# Test if logout is done |
||||
ok( |
||||
$res = $op->_get( |
||||
'/', cookie => "lemonldap=$idpId", |
||||
), |
||||
'Test if user is reject on IdP' |
||||
); |
||||
expectReject($res); |
||||
|
||||
switch ('rp'); |
||||
ok( |
||||
$res = $rp->_get( |
||||
'/', |
||||
accept => 'text/html', |
||||
cookie => |
||||
"lemonldapidp=http://auth.idp.com/saml/metadata; lemonldap=$spId" |
||||
), |
||||
'Test if user is reject on SP' |
||||
); |
||||
expectRedirection( $res, qr#^http://auth.op.com/oauth2/authorize# ); |
||||
|
||||
clean_sessions(); |
||||
} |
||||
|
||||
#print STDERR Dumper($res); |
||||
|
||||
count($mainTests); |
||||
eval { unlink 't/userdb.db' }; |
||||
done_testing( count() ); |
||||
|
||||
no warnings 'redefine'; |
||||
|
||||
sub LWP::UserAgent::request { |
||||
my ( $self, $req ) = @_; |
||||
ok( $req->uri =~ m#http://auth.((?:o|r)p).com(.*)#, ' REST request' ); |
||||
my $host = $1; |
||||
my $url = $2; |
||||
my ( $res, $client ); |
||||
count(1); |
||||
if ( $host eq 'op' ) { |
||||
pass(" Request from RP to OP, endpoint $url"); |
||||
$client = $op; |
||||
} |
||||
elsif ( $host eq 'rp' ) { |
||||
pass(' Request from OP to RP'); |
||||
$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' ); |
||||
my $httpResp = HTTP::Response->new( $res->[0], 'OK' ); |
||||
|
||||
while ( my $name = shift @{ $res->[1] } ) { |
||||
$httpResp->header( $name, shift( @{ $res->[1] } ) ); |
||||
} |
||||
|
||||
#print STDERR Dumper($res->[2]); |
||||
$httpResp->content( join( '', @{ $res->[2] } ) ); |
||||
count(4); |
||||
return $httpResp; |
||||
} |
||||
|
||||
sub switch { |
||||
my $type = shift; |
||||
pass( '==> Switching to ' . uc($type) . ' <==' ); |
||||
count(1); |
||||
@Lemonldap::NG::Handler::Main::_onReload = @{ |
||||
$handlerOR{$type}; |
||||
}; |
||||
} |
||||
|
||||
sub op { |
||||
return LLNG::Manager::Test->new( |
||||
{ |
||||
ini => { |
||||
logLevel => $debug, |
||||
domain => 'idp.com', |
||||
portal => 'http://auth.op.com/', |
||||
authentication => 'Choice', |
||||
userDB => 'Same', |
||||
authChoiceParam => 'test', |
||||
authChoiceModules => { |
||||
demo => 'Demo;Demo;Demo', |
||||
sql => 'DBI;DBI;DBI', |
||||
}, |
||||
dbiAuthChain => 'dbi:SQLite:dbname=t/userdb.db', |
||||
dbiAuthUser => '', |
||||
dbiAuthPassword => '', |
||||
dbiAuthTable => 'users', |
||||
dbiAuthLoginCol => 'user', |
||||
dbiAuthPasswordCol => 'password', |
||||
dbiAuthPasswordHash => '', |
||||
issuerDBOpenIDConnectActivation => "1", |
||||
oidcRPMetaDataExportedVars => { |
||||
rp => { |
||||
email => "mail", |
||||
family_name => "cn", |
||||
name => "cn" |
||||
} |
||||
}, |
||||
oidcServiceMetaDataIssuer => "http://auth.op.com/", |
||||
oidcServiceMetaDataAuthorizeURI => "authorize", |
||||
oidcServiceMetaDataCheckSessionURI => "checksession.html", |
||||
oidcServiceMetaDataJWKSURI => "jwks", |
||||
oidcServiceMetaDataEndSessionURI => "logout", |
||||
oidcServiceMetaDataRegistrationURI => "register", |
||||
oidcServiceMetaDataTokenURI => "token", |
||||
oidcServiceMetaDataUserInfoURI => "userinfo", |
||||
oidcServiceAllowHybridFlow => 1, |
||||
oidcServiceAllowImplicitFlow => 1, |
||||
oidcServiceAllowDynamicRegistration => 1, |
||||
oidcServiceAllowAuthorizationCodeFlow => 1, |
||||
oidcRPMetaDataOptions => { |
||||
rp => { |
||||
oidcRPMetaDataOptionsDisplayName => "RP", |
||||
oidcRPMetaDataOptionsIDTokenExpiration => 3600, |
||||
oidcRPMetaDataOptionsClientID => "rpid", |
||||
oidcRPMetaDataOptionsIDTokenSignAlg => "HS512", |
||||
oidcRPMetaDataOptionsBypassConsent => 0, |
||||
oidcRPMetaDataOptionsClientSecret => "rpsecret", |
||||
oidcRPMetaDataOptionsUserIDAttr => "", |
||||
oidcRPMetaDataOptionsAccessTokenExpiration => 3600, |
||||
oidcRPMetaDataOptionsPostLogoutRedirectUris => |
||||
"http://auth.rp.com/?logout=1" |
||||
} |
||||
}, |
||||
oidcOPMetaDataOptions => {}, |
||||
oidcOPMetaDataJSON => {}, |
||||
oidcOPMetaDataJWKS => {}, |
||||
oidcStorageOptions => {}, |
||||
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----- |
||||
", |
||||
} |
||||
} |
||||
); |
||||
} |
||||
|
||||
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', |
||||
restSessionServer => 1, |
||||
oidcOPMetaDataExportedVars => { |
||||
op => { |
||||
cn => "name", |
||||
uid => "sub", |
||||
sn => "family_name", |
||||
mail => "email" |
||||
} |
||||
}, |
||||
oidcOPMetaDataOptions => { |
||||
op => { |
||||
oidcOPMetaDataOptionsJWKSTimeout => 0, |
||||
oidcOPMetaDataOptionsClientSecret => "rpsecret", |
||||
oidcOPMetaDataOptionsScope => "openid profile", |
||||
oidcOPMetaDataOptionsStoreIDToken => 0, |
||||
oidcOPMetaDataOptionsDisplay => "", |
||||
oidcOPMetaDataOptionsClientID => "rpid", |
||||
oidcOPMetaDataOptionsConfigurationURI => |
||||
"https://auth.op.com/.well-known/openid-configuration" |
||||
} |
||||
}, |
||||
oidcOPMetaDataJWKS => { |
||||
op => $jwks, |
||||
}, |
||||
oidcOPMetaDataJSON => { |
||||
op => $metadata, |
||||
} |
||||
} |
||||
} |
||||
); |
||||
} |
Loading…
Reference in new issue