use lib 'inc'; use Test::More; use strict; use URI; use URI::QueryParam; 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 ); # 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: { unless ( eval 'use Lasso; (Lasso::check_version( 2, 5, 1, Lasso::Constants::CHECK_VERSION_NUMERIC) )? 1 : 0' ) { skip 'Lasso not found or too old', $maintests; } # Initialization $issuer = register( 'issuer', \&issuer ); $sp = register( 'sp', \&sp ); # Simple SP access my $res; ok( $res = $sp->_get( '/', accept => 'text/html', query => 'url=aHR0cDovL3Rlc3QxLmV4YW1wbGUuY29tLw==' ), 'Unauth SP request' ); my ( $host, $url, $query ); ( $url, $query ) = expectRedirection( $res, qr#^http://auth.idp.com(/saml/singleSignOn)\?(SAMLRequest=.+)# ); # Push SAML request to IdP switch ('issuer'); ok( $res = $issuer->_get( $url, query => $query, accept => 'text/html', ), 'Launch SAML request to IdP' ); expectOK($res); my $pdata = 'lemonldappdata=' . expectCookie( $res, 'lemonldappdata' ); # Try to authenticate with an expired OTT to IdP # Waiting Time::Fake->offset("+150s"); my $body = $res->[2]->[0]; $body =~ s/^.*?//s; $body =~ s#.*$##s; my %fields = ( $body =~ /[2]->[0]; $body =~ s/^.*?//s; $body =~ s#.*$##s; %fields = ( $body =~ /_post( $url, IO::String->new($query), accept => 'text/html', length => length($query), ), 'Post SAML response to SP' ); my $spId = expectCookie($res); expectRedirection( $res, 'http://test1.example.com/' ); 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' ); ( $url, $query ) = expectRedirection( $res, qr#^http://auth.idp.com(/saml/singleLogout)\?(SAMLRequest=.+)# ); my $uri = URI->new; $uri->query($query); is( $uri->query_param("SigAlg"), 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384', 'SHA256 used to sign Logout Request' ); # Push SAML logout request to IdP switch ('issuer'); ok( $res = $issuer->_get( $url, query => $query, accept => 'text/html', cookie => "lemonldap=$idpId", ), 'Launch SAML logout request to IdP' ); ( $url, $query ) = expectRedirection( $res, qr#^http://auth.sp.com(/saml/proxySingleLogoutReturn)\?(SAMLResponse=.+)# ); my $removedCookie = expectCookie($res); is( $removedCookie, 0, "IDP Cookie removed" ); # Send SAML response to SP switch ('sp'); ok( $res = $sp->_get( $url, query => $query, accept => 'text/html', ), 'Launch SAML logout request to SP' ); expectOK($res); # 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' ); expectRedirection( $res, qr#^http://auth.idp.com(/saml/singleSignOn)\?(SAMLRequest=.+)# ); } count($maintests); clean_sessions(); done_testing( count() ); sub issuer { return LLNG::Manager::Test->new( { ini => { logLevel => $debug, domain => 'idp.com', portal => 'http://auth.idp.com', authentication => 'Demo', userDB => 'Same', issuersTimeout => 120, issuerDBSAMLActivation => 1, samlSPMetaDataOptions => { 'sp.com' => { samlSPMetaDataOptionsEncryptionMode => 'none', samlSPMetaDataOptionsSignSSOMessage => 1, samlSPMetaDataOptionsSignSLOMessage => 1, samlSPMetaDataOptionsCheckSSOMessageSignature => 1, samlSPMetaDataOptionsCheckSLOMessageSignature => 1, samlSPMetaDataOptionsSignatureMethod => "RSA_SHA256", } }, 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 => saml_key_idp_private_enc, samlServicePrivateKeySig => saml_key_idp_private_sig, samlServicePublicKeyEnc => saml_key_idp_public_enc, samlServicePublicKeySig => saml_key_idp_public_sig, samlServiceSignatureMethod => "RSA_SHA1", 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, restSessionServer => 1, samlIDPMetaDataExportedAttributes => { idp => { 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, samlIDPMetaDataOptionsSignatureMethod => "RSA_SHA384", } }, samlIDPMetaDataExportedAttributes => { idp => { "uid" => "0;uid;;", "cn" => "1;cn;;", }, }, samlIDPMetaDataXML => { idp => { samlIDPMetaDataXML => samlIDPMetaDataXML( 'idp', 'HTTP-Redirect' ) } }, samlOrganizationDisplayName => "SP", samlOrganizationName => "SP", samlOrganizationURL => "http://www.sp.com", samlServicePublicKeySig => saml_key_sp_public_sig, samlServicePrivateKeyEnc => saml_key_sp_private_enc, samlServicePrivateKeySig => saml_key_sp_private_sig, samlServicePublicKeyEnc => saml_key_sp_public_enc, samlServiceSignatureMethod => "RSA_SHA1", samlSPSSODescriptorAuthnRequestsSigned => 1, }, } ); }