From 476212094a652c14b0e6089c91ff1c57bafe0bca Mon Sep 17 00:00:00 2001 From: Christophe Maudoux Date: Sun, 14 Apr 2019 21:13:43 +0200 Subject: [PATCH] Merge branch 'v2.0' --- .../lib/Lemonldap/NG/Manager/Viewer.pm | 38 ++- lemonldap-ng-manager/t/70-viewer.t | 8 +- .../t/71-viewer-with-no-diff.t | 46 ++- lemonldap-ng-portal/MANIFEST | 1 + .../lib/Lemonldap/NG/Portal/Auth/CAS.pm | 2 - .../Lemonldap/NG/Portal/Auth/OpenIDConnect.pm | 5 +- .../lib/Lemonldap/NG/Portal/Main/Run.pm | 3 +- .../t/20-Auth-and-password-DBI-dynamic-hash.t | 41 +-- .../t/30-Auth-SAML-with-choice.t | 12 +- ...th-and-issuer-CAS-with-choice-and-cancel.t | 44 ++- .../t/32-Auth-and-issuer-OIDC-sorted.t | 291 ++++++++++++++++++ 11 files changed, 436 insertions(+), 55 deletions(-) create mode 100644 lemonldap-ng-portal/t/32-Auth-and-issuer-OIDC-sorted.t diff --git a/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Viewer.pm b/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Viewer.pm index 2eb29e6e2..de61cbfb6 100644 --- a/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Viewer.pm +++ b/lemonldap-ng-manager/lib/Lemonldap/NG/Manager/Viewer.pm @@ -26,7 +26,7 @@ sub addRoutes { $self->ua( Lemonldap::NG::Common::UserAgent->new($conf) ); my $hiddenPK = ''; - $hiddenPK = $self->{viewerHiddenKeys} || $conf->{viewerHiddenKeys}; + $hiddenPK = $self->{viewerHiddenKeys}; my @enabledPK = (); my @keys = qw(virtualHosts samlIDPMetaDataNodes samlSPMetaDataNodes applicationList oidcOPMetaDataNodes oidcRPMetaDataNodes @@ -66,17 +66,9 @@ sub addRoutes { view => { diff => { ':conf1' => { ':conf2' => 'viewDiff' } } } ) ->addRoute( 'viewDiff.html', undef, ['GET'] ); } - unless ( $self->{viewerAllowBrowser} ) { - $self->addRoute( - view => { ':cfgNum' => 'rejectKey' }, - ['GET'] - ); - } # Other keys - else { - $self->addRoute( view => { ':cfgNum' => { '*' => 'getKey' } }, ['GET'] ); - } + $self->addRoute( view => { ':cfgNum' => { '*' => 'viewKey' } }, ['GET'] ); } sub getConfByNum { @@ -124,8 +116,32 @@ sub viewDiff { } sub rejectKey { - my ( $self, $req ) = @_; + my ( $self, $req, @args ) = @_; return $self->sendJSONresponse( $req, { 'value' => '_Hidden_' } ); } +sub viewKey { + my ( $self, $req, @args ) = @_; + my $lastConf; + $self->logger->debug("Viewer requested URI -> $req->{env}->{REQUEST_URI}"); + if ( $self->{viewerAllowBrowser} ) { + $self->logger->debug(" No restriction"); + $self->SUPER::getKey( $req, @args ); + } + else { + if ( $req->{env}->{REQUEST_URI} =~ m%/view/(?:latest|\d+/\w+)$% ) { + $self->logger->debug(" $req->{env}->{REQUEST_URI} -> URI allowed"); + $self->SUPER::getKey( $req, @args ); + } + else { + $self->logger->debug( + " $req->{env}->{REQUEST_URI} -> URI FORBIDDEN"); + my $user = $req->{userData}->{_whatToTrace} || 'anonymous'; + $self->userLogger->warn("$user tried to browse configurations!!!"); + $self->rejectKey( $req, @args ); + } + + } +} + 1; diff --git a/lemonldap-ng-manager/t/70-viewer.t b/lemonldap-ng-manager/t/70-viewer.t index 553db18b5..780081bd6 100644 --- a/lemonldap-ng-manager/t/70-viewer.t +++ b/lemonldap-ng-manager/t/70-viewer.t @@ -29,7 +29,7 @@ count(2); # Try to display latest conf $res = &client->jsonResponse('/view/latest'); -ok( $res->{cfgNum} eq '1', 'Browser is allowed' ); +ok( $res->{cfgNum} eq '1', 'Latest conf loaded' ); count(1); ok( @@ -61,6 +61,12 @@ ok( 6 == keys %{ $res->[1] }, 'Right number of keys found' ) or print STDERR Dumper($res); count(2); +# Try to display previous conf +$res = &client->jsonResponse('/view/1'); +ok( $res->{cfgNum} eq '1', 'Browser is allowed' ) +or print STDERR Dumper($res); +count(1); + # Remove new conf `rm -rf t/conf/lmConf-2.json`; diff --git a/lemonldap-ng-manager/t/71-viewer-with-no-diff.t b/lemonldap-ng-manager/t/71-viewer-with-no-diff.t index 8fee87c90..bcd62014b 100644 --- a/lemonldap-ng-manager/t/71-viewer-with-no-diff.t +++ b/lemonldap-ng-manager/t/71-viewer-with-no-diff.t @@ -8,6 +8,7 @@ use JSON qw(from_json); require 't/test-lib.pm'; my $struct = 't/jsonfiles/70-diff.json'; + sub body { return IO::File->new( $struct, 'r' ); } @@ -21,11 +22,13 @@ ok( 'Client object' ); - - # Try to compare confs 1 & 2 -ok( my $res = $client2->_post( '/confs/', 'cfgNum=1&force=1', &body, 'application/json' ), - "Request succeed" ); +ok( + my $res = $client2->_post( + '/confs/', 'cfgNum=1&force=1', &body, 'application/json' + ), + "Request succeed" +); ok( $res->[0] == 200, "Result code is 200" ); my $resBody; ok( $resBody = from_json( $res->[2]->[0] ), "Result body contains JSON text" ); @@ -38,13 +41,40 @@ foreach my $i ( 0 .. 1 ) { ) or print STDERR Dumper($resBody); } count(2); -$res = $client2->jsonResponse('/view/diff/1/2'); -ok( $res->{value} eq '_Hidden_', 'Diff is NOT allowed' ); + +# Test that Conf key value is sent +my $res = $client2->jsonResponse('/view/2/portalDisplayOidcConsents'); +ok( $res->{value} eq '$_oidcConnectedRP', 'Key found' ) + or print STDERR Dumper($res); +count(1); + +# Test that hidden key values are NOT sent +$res = &client->jsonResponse('/view/2/portalDisplayLogout'); +ok( $res->{value} eq '_Hidden_', 'Key is hidden' ) + or explain( $res, 'value => "_Hidden_"' ); +count(1); + +# Browse confs is forbidden +my $res = $client2->jsonResponse('/view/2'); +ok( $res->{value} eq '_Hidden_', 'Key is hidden' ) + or print STDERR Dumper($res); count(1); # Try to display latest conf -$res = $client2->jsonResponse('/view/2'); -ok( $res->{value} eq '_Hidden_', 'Browser is NOT allowed' ); +$res = &client->jsonResponse('/view/latest'); +ok( $res->{cfgNum} eq '2', 'Latest conf loaded' ); +count(1); + +# Try to compare confs +$res = $client2->jsonResponse('/view/diff/1/2'); +ok( $res->{value} eq '_Hidden_', 'Diff is NOT allowed' ) + or print STDERR Dumper($res); +count(1); + +# Try to display latest conf +$res = $client2->jsonResponse('/view/1'); +ok( $res->{value} eq '_Hidden_', 'Browser is NOT allowed' ) + or print STDERR Dumper($res); count(2); # Remove new conf diff --git a/lemonldap-ng-portal/MANIFEST b/lemonldap-ng-portal/MANIFEST index 13e0d5091..85c1fdbe6 100644 --- a/lemonldap-ng-portal/MANIFEST +++ b/lemonldap-ng-portal/MANIFEST @@ -449,6 +449,7 @@ t/32-Auth-and-issuer-OIDC-authorization_code-with-authchoice.t t/32-Auth-and-issuer-OIDC-authorization_code.t t/32-Auth-and-issuer-OIDC-hybrid.t t/32-Auth-and-issuer-OIDC-implicit.t +t/32-Auth-and-issuer-OIDC-sorted.t t/32-OIDC-RP-rule.t t/33-Auth-and-issuer-OpenID2.t t/34-Auth-Proxy-and-REST-Server.t diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/CAS.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/CAS.pm index 933ea06e1..4451d6f16 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/CAS.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/CAS.pm @@ -35,9 +35,7 @@ sub init { } $self->srvNumber( scalar @tab ); my @list; - my $portalPath = $self->conf->{portal}; - $portalPath =~ s#^https?://[^/]+/?#/#; foreach (@tab) { my $name = $_; diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/OpenIDConnect.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/OpenIDConnect.pm index efc01cdd0..03807f194 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/OpenIDConnect.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Auth/OpenIDConnect.pm @@ -35,11 +35,8 @@ sub init { } $self->opNumber( scalar @tab ); my @list = (); - my $portalPath = $self->conf->{portal}; - #$portalPath =~ s#^https?://[^/]+/?#/#; - foreach (@tab) { my $name = $_; $name = @@ -243,7 +240,7 @@ sub extractFormInfo { else { # IDP list - my $portalPath = $self->{portal}; + my $portalPath = $self->{conf}->{portal}; $portalPath =~ s#^https?://[^/]+/?#/#; $req->data->{list} = $self->opList; diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Run.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Run.pm index fc4053d28..5d0fecd12 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Run.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Run.pm @@ -240,7 +240,8 @@ sub do { $req, { result => 1, - code => $err + error => $err, + id => $req->id } ); } diff --git a/lemonldap-ng-portal/t/20-Auth-and-password-DBI-dynamic-hash.t b/lemonldap-ng-portal/t/20-Auth-and-password-DBI-dynamic-hash.t index b5f14d61a..e9aef600b 100644 --- a/lemonldap-ng-portal/t/20-Auth-and-password-DBI-dynamic-hash.t +++ b/lemonldap-ng-portal/t/20-Auth-and-password-DBI-dynamic-hash.t @@ -1,7 +1,23 @@ -use Lemonldap::NG::Portal::Lib::DBI; use MIME::Base64; -{ +use Test::More; +use strict; +use IO::String; + +require 't/test-lib.pm'; + +my $res; +my $maintests = 6; + +eval { unlink 't/userdb.db' }; + +SKIP: { + eval +'require DBD::SQLite; use Digest::SHA; use Lemonldap::NG::Portal::Lib::DBI'; + if ($@) { + skip 'DBI/DBD::SQLite not found', $maintests; + } + eval q` no warnings 'redefine'; sub Lemonldap::NG::Portal::Lib::DBI::hash_password_from_database { @@ -72,24 +88,8 @@ use MIME::Base64; # Return encode_base64(SQL_METHOD(password + salt) + salt) } -} +`; -use Test::More; -use strict; -use IO::String; - -require 't/test-lib.pm'; - -my $res; -my $maintests = 6; - -eval { unlink 't/userdb.db' }; - -SKIP: { - eval { require DBI; require DBD::SQLite; use Digest::SHA }; - 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)'); @@ -106,7 +106,8 @@ SKIP: { $dbh->do( "INSERT INTO users VALUES ('jsmith','{ssha512}wr0zU/I6f7U4bVoeOlJnNFbhF0a9np59LUeNnhokohVI/wiNzt8Y4JujfOfNQiGuiVgY+xrYggfmgpke6KdjxKS7W0GR1ZCe','John Smith')" ); - my $client = LLNG::Manager::Test->new( { + my $client = LLNG::Manager::Test->new( + { ini => { logLevel => 'error', useSafeJail => 1, diff --git a/lemonldap-ng-portal/t/30-Auth-SAML-with-choice.t b/lemonldap-ng-portal/t/30-Auth-SAML-with-choice.t index 545eef18a..05d62bd2b 100644 --- a/lemonldap-ng-portal/t/30-Auth-SAML-with-choice.t +++ b/lemonldap-ng-portal/t/30-Auth-SAML-with-choice.t @@ -68,7 +68,7 @@ SKIP: { # IDP must be sorted my @idp = map /val="http:\/\/(.+?)\/saml\/metadata">/g, $res->[2]->[0]; ok( $idp[0] eq 'auth.idp2.com', '1st = idp2' ) or print STDERR Dumper( \@idp ); - ok( $idp[1] eq 'auth.z_idp2.com', '2nd = z_idp2' ) or print STDERR Dumper( \@idp ); + ok( $idp[1] eq 'auth.idp2_z.com', '2nd = idp2_z' ) or print STDERR Dumper( \@idp ); ok( $idp[2] eq 'auth.idp3.com', '3rd = idp3' ) or print STDERR Dumper( \@idp ); ok( $idp[3] eq 'auth.idp.com', '4th= idp' ) or print STDERR Dumper( \@idp ); @@ -261,7 +261,7 @@ sub sp { uid => "1;uid", cn => "0;cn" }, - z_idp2 => { + idp2_z => { mail => "0;mail;;", uid => "1;uid", cn => "0;cn" @@ -305,7 +305,7 @@ sub sp { samlIDPMetaDataOptionsSortNumber => 1, samlIDPMetaDataOptionsDisplayName => 'Test_Sort', }, - z_idp2 => { + idp2_z => { samlIDPMetaDataOptionsEncryptionMode => 'none', samlIDPMetaDataOptionsSSOBinding => 'post', samlIDPMetaDataOptionsSLOBinding => 'post', @@ -329,7 +329,7 @@ sub sp { "uid" => "0;uid;;", "cn" => "1;cn;;", }, - z_idp2 => { + idp2_z => { "uid" => "0;uid;;", "cn" => "1;cn;;", }, @@ -347,9 +347,9 @@ sub sp { samlIDPMetaDataXML => samlIDPMetaDataXML( 'idp3', 'HTTP-POST' ) }, - z_idp2 => { + idp2_z => { samlIDPMetaDataXML => - samlIDPMetaDataXML( 'z_idp2', 'HTTP-POST' ) + samlIDPMetaDataXML( 'idp2_z', 'HTTP-POST' ) }, }, samlOrganizationDisplayName => "SP", diff --git a/lemonldap-ng-portal/t/31-Auth-and-issuer-CAS-with-choice-and-cancel.t b/lemonldap-ng-portal/t/31-Auth-and-issuer-CAS-with-choice-and-cancel.t index 1d81c1649..aa7cf5c1c 100644 --- a/lemonldap-ng-portal/t/31-Auth-and-issuer-CAS-with-choice-and-cancel.t +++ b/lemonldap-ng-portal/t/31-Auth-and-issuer-CAS-with-choice-and-cancel.t @@ -11,7 +11,7 @@ BEGIN { } eval { unlink 't/userdb.db' }; -my $maintests = 7; +my $maintests = 13; my $debug = 'error'; my ( $issuer, $sp, $res ); my %handlerOR = ( issuer => [], sp => [] ); @@ -92,6 +92,7 @@ SKIP: { ); ok( $res->[2]->[0] =~ s#^.*(
]*CAS.*?
).*$#$1#s, 'Found CAS entry' ); + my ( $host, $url, $query ) = expectForm($res); ok( $res = $sp->_get( @@ -101,6 +102,23 @@ SKIP: { ), 'Unauth SP request' ); + + # CAS idp must be sorted + my @idp = map /idploop py-3" val="(.+?)">/g, $res->[2]->[0]; + ok( $idp[0] eq 'idp', '1st = idp' ) or print STDERR Dumper( \@idp ); + ok( $idp[1] eq 'idp3', '2nd = idp3' ) or print STDERR Dumper( \@idp ); + ok( $idp[2] eq 'idp4', '3rd = idp4' ) or print STDERR Dumper( \@idp ); + ok( $idp[3] eq 'idp2', '4th= idp2' ) or print STDERR Dumper( \@idp ); + + # Found CAS idp logo and display name + ok( + $res->[2]->[0] =~ +qr%idp4%, + 'Found CAS idp logo' + ) or print STDERR Dumper( $res->[2]->[0] ); + ok( $res->[2]->[0] =~ qr%CAS1%, 'Found CAS idp display name' ) + or print STDERR Dumper( $res->[2]->[0] ); + my $pdata = 'lemonldappdata=' . expectCookie( $res, 'lemonldappdata' ); expectForm( $res, undef, undef ); ok( @@ -182,16 +200,38 @@ sub sp { mail => 'mail', uid => 'uid', }, + idp3 => { + cn => 'cn', + mail => 'mail', + uid => 'uid', + }, + idp4 => { + cn => 'cn', + mail => 'mail', + uid => 'uid', + }, }, casSrvMetaDataOptions => { idp => { casSrvMetaDataOptionsUrl => 'http://auth.idp.com/cas', - casSrvMetaDataOptionsGateway => 0, + casSrvMetaDataOptionsGateway => 0, + casSrvMetaDataOptionsDisplayName => 'CAS1', }, idp2 => { + casSrvMetaDataOptionsUrl => 'http://auth.idp.com/cas', + casSrvMetaDataOptionsGateway => 0, + casSrvMetaDataOptionsSortNumber => 5, + }, + idp3 => { casSrvMetaDataOptionsUrl => 'http://auth.idp.com/cas', casSrvMetaDataOptionsGateway => 0, }, + idp4 => { + casSrvMetaDataOptionsUrl => 'http://auth.idp.com/cas', + casSrvMetaDataOptionsGateway => 0, + casSrvMetaDataOptionsIcon => 'icons/sfa_manager.png', + casSrvMetaDataOptionsSortNumber => 2, + }, }, }, } diff --git a/lemonldap-ng-portal/t/32-Auth-and-issuer-OIDC-sorted.t b/lemonldap-ng-portal/t/32-Auth-and-issuer-OIDC-sorted.t new file mode 100644 index 000000000..90ee3064d --- /dev/null +++ b/lemonldap-ng-portal/t/32-Auth-and-issuer-OIDC-sorted.t @@ -0,0 +1,291 @@ +use lib 'inc'; +use Test::More; +use strict; +use IO::String; +use LWP::UserAgent; +use LWP::Protocol::PSGI; +use MIME::Base64; + +BEGIN { + require 't/test-lib.pm'; +} + +my $debug = 'error'; +my ( $op, $rp, $res ); +my %handlerOR = ( op => [], rp => [] ); + +LWP::Protocol::PSGI->register( + sub { + my $req = Plack::Request->new(@_); + 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 [ 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' ); + count(4); + return $res; + } +); + +# 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]; +count(3); + +switch('rp'); +&Lemonldap::NG::Handler::Main::cfgNum( 0, 0 ); +ok( $rp = rp( $jwks, $metadata ), 'RP portal' ); +count(1); + +# Query RP for auth +ok( $res = $rp->_get( '/', accept => 'text/html' ), 'Unauth SP request' ); +count(1); + +# OIDC idp must be sorted +my @idp = map /idploop py-3" val="(.+?)">/g, $res->[2]->[0]; +ok( $idp[0] eq 'op2', '1st = op2' ) or print STDERR Dumper( \@idp ); +ok( $idp[1] eq 'op3', '2nd = op3' ) or print STDERR Dumper( \@idp ); +ok( $idp[2] eq 'op', '3rd = op' ) or print STDERR Dumper( \@idp ); +count(3); + +# Found OIDC idp logo and display name +ok( + $res->[2]->[0] =~ +qr%op2%, + 'Found OIDC idp logo' +) or print STDERR Dumper( $res->[2]->[0] ); +ok( $res->[2]->[0] =~ qr%idpOPtest%, 'Found OIDC idp display name' ) + or print STDERR Dumper( $res->[2]->[0] ); +count(2); +clean_sessions(); +done_testing( count() ); + +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 => 'Demo', + userDB => 'Same', + issuerDBOpenIDConnectActivation => "1", + oidcRPMetaDataExportedVars => { + rp => { + email => "mail", + family_name => "cn", + name => "cn" + } + }, + oidcServiceMetaDataIssuer => "http://auth.op.com", + oidcServiceMetaDataAuthorizeURI => "authorize", + oidcServiceMetaDataCheckSessionURI => "checksession", + 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 + } + }, + oidcOPMetaDataOptions => {}, + oidcOPMetaDataJSON => {}, + oidcOPMetaDataJWKS => {}, + 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', + oidcOPMetaDataExportedVars => { + op => { + cn => "name", + uid => "sub", + sn => "family_name", + mail => "email" + }, + op2 => { + cn => "name", + uid => "sub", + sn => "family_name", + mail => "email" + }, + op3 => { + cn => "name", + uid => "sub", + sn => "family_name", + mail => "email" + } + }, + oidcOPMetaDataOptions => { + op => { + oidcOPMetaDataOptionsJWKSTimeout => 0, + oidcOPMetaDataOptionsClientSecret => "rpsecret", + oidcOPMetaDataOptionsScope => "openid profile", + oidcOPMetaDataOptionsStoreIDToken => 0, + oidcOPMetaDataOptionsDisplay => "", + oidcOPMetaDataOptionsClientID => "rpid", + oidcOPMetaDataOptionsDisplayName => 'idpOPtest', + oidcOPMetaDataOptionsSortNumber => '3', + oidcOPMetaDataOptionsConfigurationURI => + "https://auth.op.com/.well-known/openid-configuration" + }, + op2 => { + oidcOPMetaDataOptionsJWKSTimeout => 0, + oidcOPMetaDataOptionsClientSecret => "rpsecret", + oidcOPMetaDataOptionsScope => "openid profile", + oidcOPMetaDataOptionsStoreIDToken => 0, + oidcOPMetaDataOptionsDisplay => "", + oidcOPMetaDataOptionsIcon => 'icons/sfa_manager.png', + oidcOPMetaDataOptionsClientID => "rpid", + oidcOPMetaDataOptionsConfigurationURI => + "https://auth.op.com/.well-known/openid-configuration" + }, + op3 => { + oidcOPMetaDataOptionsJWKSTimeout => 0, + oidcOPMetaDataOptionsClientSecret => "rpsecret", + oidcOPMetaDataOptionsScope => "openid profile", + oidcOPMetaDataOptionsStoreIDToken => 0, + oidcOPMetaDataOptionsDisplay => "", + oidcOPMetaDataOptionsSortNumber => '1', + oidcOPMetaDataOptionsClientID => "rpid", + oidcOPMetaDataOptionsConfigurationURI => + "https://auth.op.com/.well-known/openid-configuration" + } + }, + oidcOPMetaDataJWKS => { + op => $jwks, + op2 => $jwks, + op3 => $jwks, + }, + oidcOPMetaDataJSON => { + op => $metadata, + op2 => $metadata, + op3 => $metadata, + } + } + } + ); +}