Fix proxied services in CAS (#1183)
parent
f07c2e40cd
commit
00423fc223
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,260 @@ |
||||
use Test::More; # skip_all => 'CAS is in rebuild'; |
||||
use strict; |
||||
use IO::String; |
||||
use MIME::Base64; |
||||
|
||||
BEGIN { |
||||
require 't/test-lib.pm'; |
||||
} |
||||
|
||||
my $debug = 'error'; |
||||
my ( $issuer, $sp, $res ); |
||||
my %handlerOR = ( issuer => [], sp => [] ); |
||||
|
||||
no warnings 'redefine'; |
||||
|
||||
ok( $issuer = issuer(), 'Issuer portal' ); |
||||
$handlerOR{issuer} = \@Lemonldap::NG::Handler::Main::_onReload; |
||||
count(1); |
||||
switch ('sp'); |
||||
|
||||
ok( $sp = sp(), 'SP portal' ); |
||||
count(1); |
||||
$handlerOR{sp} = \@Lemonldap::NG::Handler::Main::_onReload; |
||||
|
||||
# Simple SP access |
||||
ok( |
||||
$res = $sp->_get( |
||||
'/', accept => 'text/html', |
||||
), |
||||
'Unauth SP request' |
||||
); |
||||
count(1); |
||||
expectRedirection( $res, |
||||
'http://auth.idp.com/cas/login?service=http%3A%2F%2Fauth.sp.com%2F' ); |
||||
|
||||
# Query IdP |
||||
switch ('issuer'); |
||||
ok( |
||||
$res = $issuer->_get( |
||||
'/cas/login', |
||||
query => 'service=http://auth.sp.com/', |
||||
accept => 'text/html' |
||||
), |
||||
'Query CAS server' |
||||
); |
||||
count(1); |
||||
expectOK($res); |
||||
|
||||
# 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; |
||||
my $s = join( '&', map { "$_=" . uri_escape( $fields{$_} ) } keys %fields ); |
||||
ok( |
||||
$res = $issuer->_post( |
||||
'/cas/login', |
||||
IO::String->new($s), |
||||
accept => 'text/html', |
||||
length => length($s), |
||||
), |
||||
'Post authentication' |
||||
); |
||||
count(1); |
||||
my ($query) = |
||||
expectRedirection( $res, qr#^http://auth.sp.com/\?(ticket=[^&]+)$# ); |
||||
my $idpId = expectCookie($res); |
||||
|
||||
# Back to SP |
||||
switch ('sp'); |
||||
ok( $res = $sp->_get( '/', query => $query, accept => 'text/html' ), |
||||
'Query SP with ticket' ); |
||||
count(1); |
||||
my $spId = expectCookie($res); |
||||
|
||||
# Test authentication |
||||
ok( $res = $sp->_get( '/', cookie => "lemonldap=$spId" ), 'Get / on SP' ); |
||||
count(1); |
||||
expectOK($res); |
||||
expectAuthenticatedAs( $res, 'french' ); |
||||
|
||||
# Test attributes |
||||
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' ); |
||||
count(3); |
||||
|
||||
# Logout initiated by SP |
||||
ok( |
||||
$res = $sp->_get( |
||||
'/', |
||||
query => 'logout', |
||||
cookie => "lemonldap=$spId", |
||||
accept => 'text/html' |
||||
), |
||||
'Query SP for logout' |
||||
); |
||||
count(1); |
||||
expectOK($res); |
||||
ok( |
||||
$res->[2]->[0] =~ m#iframe src="http://auth.idp.com(/cas/logout)\?(.+?)"#s, |
||||
'Found iframe' |
||||
); |
||||
count(1); |
||||
|
||||
# Query IdP with iframe src |
||||
my $url = $1; |
||||
$query = $2; |
||||
ok( getHeader( $res, 'Content-Security-Policy' ) =~ /child-src auth.idp.com/, |
||||
'Frame is authorizated' ) |
||||
or |
||||
explain( $res->[1], 'Content-Security-Policy => ...child-src auth.idp.com' ); |
||||
count(1); |
||||
|
||||
switch ('issuer'); |
||||
ok( |
||||
$res = $issuer->_get( |
||||
$url, |
||||
query => $query, |
||||
accept => 'text/html', |
||||
cookie => "lemonldap=$idpId" |
||||
), |
||||
'Get iframe from IdP' |
||||
); |
||||
count(1); |
||||
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' ); |
||||
count(1); |
||||
|
||||
# Verify that user has been disconnected |
||||
ok( $res = $issuer->_get( '/', cookie => "lemonldap=$idpId" ), 'Query IdP' ); |
||||
count(1); |
||||
expectReject($res); |
||||
|
||||
switch ('sp'); |
||||
ok( |
||||
$res = |
||||
$sp->_get( '/', accept => 'text/html', cookie => "lemonldap=$idpId" ), |
||||
'Query IdP' |
||||
); |
||||
count(1); |
||||
expectRedirection( $res, |
||||
'http://auth.idp.com/cas/login?service=http%3A%2F%2Fauth.sp.com%2F' ); |
||||
|
||||
clean_sessions(); |
||||
done_testing( count() ); |
||||
|
||||
# Redefine LWP methods for tests |
||||
no warnings 'redefine'; |
||||
|
||||
sub LWP::UserAgent::request { |
||||
my ( $self, $req ) = @_; |
||||
ok( $req->uri =~ m#http://auth.((?:id|s)p).com([^\?]*)(?:\?(.*))?$#, |
||||
' Request to '.$req->uri ); |
||||
my $host = $1; |
||||
my $url = $2; |
||||
my $query = $3; |
||||
my $res; |
||||
my $client = ( $host eq 'idp' ? $issuer : $sp ); |
||||
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); |
||||
my $httpResp = HTTP::Response->new( $res->[0], 'OK' ); |
||||
|
||||
while ( my $name = shift @{ $res->[1] } ) { |
||||
$httpResp->header( $name, shift( @{ $res->[1] } ) ); |
||||
} |
||||
$httpResp->content( join( '', @{ $res->[2] } ) ); |
||||
count(2); |
||||
return $httpResp; |
||||
} |
||||
|
||||
sub switch { |
||||
my $type = shift; |
||||
@Lemonldap::NG::Handler::Main::_onReload = @{ |
||||
$handlerOR{$type}; |
||||
}; |
||||
} |
||||
|
||||
sub issuer { |
||||
return LLNG::Manager::Test->new( |
||||
{ |
||||
ini => { |
||||
logLevel => $debug, |
||||
templatesDir => 'site/htdocs/static', |
||||
domain => 'idp.com', |
||||
portal => 'http://auth.idp.com', |
||||
authentication => 'Demo', |
||||
userDB => 'Same', |
||||
issuerDBCASActivation => 1, |
||||
casAttr => 'uid', |
||||
casAttributes => { cn => 'cn', uid => 'uid', }, |
||||
casAccessControlPolicy => 'none', |
||||
multiValuesSeparator => ';', |
||||
} |
||||
} |
||||
); |
||||
} |
||||
|
||||
sub sp { |
||||
return LLNG::Manager::Test->new( |
||||
{ |
||||
ini => { |
||||
logLevel => $debug, |
||||
domain => 'sp.com', |
||||
portal => 'http://auth.sp.com', |
||||
authentication => 'CAS', |
||||
userDB => 'CAS', |
||||
restSessionServer => 1, |
||||
issuerDBCASActivation => 0, |
||||
multiValuesSeparator => ';', |
||||
casSrvMetaDataExportedVars => { |
||||
idp => { |
||||
cn => 'cn', |
||||
mail => 'mail', |
||||
uid => 'uid', |
||||
} |
||||
}, |
||||
casSrvMetaDataOptions => { |
||||
idp => { |
||||
casSrvMetaDataOptionsUrl => 'http://auth.idp.com/cas', |
||||
casSrvMetaDataOptionsGateway => 0, |
||||
casSrvMetaDataOptionsProxiedServices => { |
||||
test => 'http://test.sp.com/', |
||||
}, |
||||
} |
||||
}, |
||||
}, |
||||
} |
||||
); |
||||
} |
Loading…
Reference in new issue