lemonldap-ng/lemonldap-ng-manager/t/04-providers-api.t
Xavier Guimard 4459a47f76 Tidy
2020-02-20 23:37:05 +01:00

560 lines
19 KiB
Perl

# Test Providers API
use Test::More;
use strict;
use JSON;
use IO::String;
require 't/test-lib.pm';
our $_json = JSON->new->allow_nonref;
sub check200 {
my ( $test, $res ) = splice @_;
#diag Dumper($res);
ok( $res->[0] == 200, "$test: Result code is 200" );
count(1);
checkJson( $test, $res );
}
sub check404 {
my ( $test, $res ) = splice @_;
#diag Dumper($res);
ok( $res->[0] == 404, "$test: Result code is 404" );
count(1);
checkJson( $test, $res );
}
sub check405 {
my ( $test, $res ) = splice @_;
ok( $res->[0] == 405, "$test: Result code is 405" );
count(1);
checkJson( $test, $res );
}
sub checkJson {
my ( $test, $res ) = splice @_;
my $key;
#diag Dumper($res->[2]->[0]);
ok( $key = from_json( $res->[2]->[0] ), "$test: Response is JSON" );
count(1);
}
sub add {
my ( $test, $type, $obj ) = splice @_;
my $j = $_json->encode($obj);
my $res;
#diag Dumper($j);
ok(
$res = &client->_post(
"/api/v1/providers/$type", '',
IO::String->new($j), 'application/json',
length($j)
),
"$test: Request succeed"
);
count(1);
return $res;
}
sub checkAdd {
my ( $test, $type, $add ) = splice @_;
check200( $test, add( $test, $type, $add ) );
}
sub checkAddFailsIfExists {
my ( $test, $type, $add ) = splice @_;
check405( $test, add( $test, $type, $add ) );
}
sub checkAddWithUnknownAttributes {
my ( $test, $type, $add ) = splice @_;
check405( $test, add( $test, $type, $add ) );
}
sub get {
my ( $test, $type, $confKey ) = splice @_;
my $res;
ok( $res = &client->_get( "/api/v1/providers/$type/$confKey", '' ),
"$test: Request succeed" );
count(1);
return $res;
}
sub checkGet {
my ( $test, $type, $confKey, $attrPath, $expectedValue ) = splice @_;
my $res = get( $test, $type, $confKey );
check200( $test, $res );
my @path = split '/', $attrPath;
my $key = from_json( $res->[2]->[0] );
for (@path) {
$key = $key->{$_};
}
ok(
$key eq $expectedValue,
"$test: check if $attrPath value \"$key\" matches expected value \"$expectedValue\""
);
count(1);
}
sub checkGetNotFound {
my ( $test, $type, $confKey ) = splice @_;
check404( $test, get( $test, $type, $confKey ) );
}
sub update {
my ( $test, $type, $confKey, $obj ) = splice @_;
my $j = $_json->encode($obj);
#diag Dumper($j);
my $res;
ok(
$res = &client->_patch(
"/api/v1/providers/$type/$confKey", '',
IO::String->new($j), 'application/json',
length($j)
),
"$test: Request succeed"
);
count(1);
return $res;
}
sub checkUpdate {
my ( $test, $type, $confKey, $update ) = splice @_;
check200( $test, update( $test, $type, $confKey, $update ) );
}
sub checkUpdateNotFound {
my ( $test, $type, $confKey, $update ) = splice @_;
check404( $test, update( $test, $type, $confKey, $update ) );
}
sub checkUpdateFailsIfExists {
my ( $test, $type, $confKey, $update ) = splice @_;
check405( $test, update( $test, $type, $confKey, $update ) );
}
sub checkUpdateWithUnknownAttributes {
my ( $test, $type, $confKey, $update ) = splice @_;
check405( $test, update( $test, $type, $confKey, $update ) );
}
sub replace {
my ( $test, $type, $confKey, $obj ) = splice @_;
my $j = $_json->encode($obj);
my $res;
ok(
$res = &client->_put(
"/api/v1/providers/$type/$confKey", '',
IO::String->new($j), 'application/json',
length($j)
),
"$test: Request succeed"
);
count(1);
return $res;
}
sub checkReplace {
my ( $test, $type, $confKey, $replace ) = splice @_;
check200( $test, replace( $test, $type, $confKey, $replace ) );
}
sub checkReplaceAlreadyThere {
my ( $test, $type, $confKey, $replace ) = splice @_;
check405( $test, replace( $test, $type, $confKey, $replace ) );
}
sub checkReplaceNotFound {
my ( $test, $type, $confKey, $update ) = splice @_;
check404( $test, replace( $test, $type, $confKey, $update ) );
}
sub checkReplaceWithUnknownAttribute {
my ( $test, $type, $confKey, $replace ) = splice @_;
check405( $test, replace( $test, $type, $confKey, $replace ) );
}
sub findByConfKey {
my ( $test, $type, $confKey ) = splice @_;
my $res;
ok(
$res = &client->_get(
"/api/v1/providers/$type/findByConfKey",
"pattern=$confKey"
),
"$test: Request succeed"
);
count(1);
return $res;
}
sub checkFindByConfKey {
my ( $test, $type, $confKey, $expectedHits ) = splice @_;
my $res = findByConfKey( $test, $type, $confKey );
check200( $test, $res );
my $hits = from_json( $res->[2]->[0] );
my $hit;
my $counter = 0;
foreach $hit ( @{$hits} ) {
$counter++;
ok(
$hit->{confKey} =~ $confKey,
"$test: check if confKey value \"$hit->{confKey}\" matches pattern \"$confKey\""
);
count(1);
}
ok(
$counter eq $expectedHits,
"$test: check if nb of hits returned ($counter) matches expectation ($expectedHits)"
);
count(1);
}
sub findByProviderId {
my ( $test, $type, $providerIdName, $providerId ) = splice @_;
my $res;
ok(
$res = &client->_get(
"/api/v1/providers/$type/findBy" . ucfirst $providerIdName,
"$providerIdName=$providerId"
),
"$test: Request succeed"
);
count(1);
return $res;
}
sub checkFindByProviderId {
my ( $test, $type, $providerIdName, $providerId ) = splice @_;
my $res = findByProviderId( $test, $type, $providerIdName, $providerId );
check200( $test, $res );
my $result = from_json( $res->[2]->[0] );
my $gotProviderId;
if ( $providerIdName eq 'entityId' ) {
($gotProviderId) = $result->{metadata} =~ m/entityID=['"](.+?)['"]/i;
}
else {
$gotProviderId = $result->{$providerIdName};
}
ok(
$gotProviderId eq $providerId,
"$test: Check $providerIdName value returned \"$gotProviderId\" matched expected value \"$providerId\""
);
count(1);
}
sub checkFindByProviderIdNotFound {
my ( $test, $type, $providerIdName, $providerId ) = splice @_;
check404( $test,
findByProviderId( $test, $type, $providerIdName, $providerId ) );
}
sub deleteProvider {
my ( $test, $type, $confKey ) = splice @_;
my $res;
ok(
$res = &client->_del(
"/api/v1/providers/$type/$confKey",
'', '', 'application/json', 0
),
"$test: Request succeed"
);
count(1);
return $res;
}
sub checkDelete {
my ( $test, $type, $confKey ) = splice @_;
check200( $test, deleteProvider( $test, $type, $confKey ) );
}
sub checkDeleteNotFound {
my ( $test, $type, $confKey ) = splice @_;
check404( $test, deleteProvider( $test, $type, $confKey ) );
}
my $test;
my $oidcRp = {
confKey => 'myOidcRp1',
clientId => 'myOidcClient1',
exportedVars => {
'sub' => "uid",
family_name => "sn",
given_name => "givenName"
},
extraClaim => {
phone => 'telephoneNumber',
email => 'mail'
},
options => {
oidcRPMetaDataOptionsClientSecret => 'secret',
oidcRPMetaDataOptionsIcon => 'web.png'
}
};
$test = "OidcRp - Add should succeed";
checkAdd( $test, 'oidc/rp', $oidcRp );
checkGet( $test, 'oidc/rp', 'myOidcRp1', 'options/oidcRPMetaDataOptionsIcon',
'web.png' );
checkGet( $test, 'oidc/rp', 'myOidcRp1',
'options/oidcRPMetaDataOptionsClientSecret', 'secret' );
$test = "OidcRp - Check attribute default value was set after add";
checkGet( $test, 'oidc/rp', 'myOidcRp1',
'options/oidcRPMetaDataOptionsIDTokenSignAlg', 'HS512' );
$test = "OidcRp - Add Should fail on duplicate confKey";
checkAddFailsIfExists( $test, 'oidc/rp', $oidcRp );
$test = "OidcRp - Update should succeed and keep existing values";
$oidcRp->{options}->{oidcRPMetaDataOptionsClientSecret} = 'secret2';
$oidcRp->{options}->{oidcRPMetaDataOptionsIDTokenSignAlg} = 'RS512';
delete $oidcRp->{options}->{oidcRPMetaDataOptionsIcon};
delete $oidcRp->{extraClaim};
delete $oidcRp->{exportedVars};
$oidcRp->{exportedVars}->{cn} = 'cn';
checkUpdate( $test, 'oidc/rp', 'myOidcRp1', $oidcRp );
checkGet( $test, 'oidc/rp', 'myOidcRp1',
'options/oidcRPMetaDataOptionsClientSecret', 'secret2' );
checkGet( $test, 'oidc/rp', 'myOidcRp1',
'options/oidcRPMetaDataOptionsIDTokenSignAlg', 'RS512' );
checkGet( $test, 'oidc/rp', 'myOidcRp1', 'options/oidcRPMetaDataOptionsIcon',
'web.png' );
checkGet( $test, 'oidc/rp', 'myOidcRp1', 'exportedVars/cn', 'cn' );
checkGet( $test, 'oidc/rp', 'myOidcRp1', 'exportedVars/family_name', 'sn' );
checkGet( $test, 'oidc/rp', 'myOidcRp1', 'extraClaim/phone',
'telephoneNumber' );
$test = "OidcRp - Update should fail on non existing options";
$oidcRp->{options}->{playingPossum} = 'elephant';
checkUpdateWithUnknownAttributes( $test, 'oidc/rp', 'myOidcRp1', $oidcRp );
delete $oidcRp->{options}->{playingPossum};
$test = "OidcRp - Add Should fail on duplicate clientId";
$oidcRp->{confKey} = 'myOidcRp2';
checkAddFailsIfExists( $test, 'oidc/rp', $oidcRp );
$test = "OidcRp - Add Should fail on non existing options";
$oidcRp->{confKey} = 'myOidcRp2';
$oidcRp->{clientId} = 'myOidcClient2';
$oidcRp->{options}->{playingPossum} = 'ElephantInTheRoom';
checkAddWithUnknownAttributes( $test, 'oidc/rp', $oidcRp );
delete $oidcRp->{options}->{playingPossum};
$test = "OidcRp - 2nd add should succeed";
checkAdd( $test, 'oidc/rp', $oidcRp );
$test = "OidcRp - Update should fail if client id exists";
$oidcRp->{clientId} = 'myOidcClient1';
checkUpdateFailsIfExists( $test, 'oidc/rp', 'myOidcRp2', $oidcRp );
$test = "OidcRp - Update should fail if confKey not found";
$oidcRp->{confKey} = 'myOidcRp3';
checkUpdateNotFound( $test, 'oidc/rp', 'myOidcRp3', $oidcRp );
$test = "OidcRp - Replace should succeed";
$oidcRp->{confKey} = 'myOidcRp2';
$oidcRp->{clientId} = 'myOidcClient2';
delete $oidcRp->{options}->{oidcRPMetaDataOptionsIcon};
delete $oidcRp->{options}->{oidcRPMetaDataOptionsIDTokenSignAlg};
checkReplace( $test, 'oidc/rp', 'myOidcRp2', $oidcRp );
$test = "OidcRp - Check attribute default value was set after replace";
checkGet( $test, 'oidc/rp', 'myOidcRp2',
'options/oidcRPMetaDataOptionsIDTokenSignAlg', 'HS512' );
$test = "OidcRp - Replace should fail on non existing options";
$oidcRp->{options}->{playingPossum} = 'elephant';
checkReplaceWithUnknownAttribute( $test, 'oidc/rp', 'myOidcRp2', $oidcRp );
delete $oidcRp->{options}->{playingPossum};
$test = "OidcRp - Replace should fail if confKey not found";
$oidcRp->{confKey} = 'myOidcRp3';
checkReplaceNotFound( $test, 'oidc/rp', 'myOidcRp3', $oidcRp );
$test = "OidcRp - FindByConfKey should find 2 hits";
checkFindByConfKey( $test, 'oidc/rp', '^myOidcRp.$', 2 );
$test = "OidcRp - FindByConfKey should find 1 hit";
checkFindByConfKey( $test, 'oidc/rp', 'myOidcRp1', 1 );
$test = "OidcRp - FindByConfKey should find 0 hits";
checkFindByConfKey( $test, 'oidc/rp', 'myOidcRp3', 0 );
$test = "OidcRp - FindByClientId should find one entry";
checkFindByProviderId( $test, 'oidc/rp', 'clientId', 'myOidcClient1' );
$test = "OidcRp - FindByClientId should find nothing";
checkFindByProviderIdNotFound( $test, 'oidc/rp', 'clientId', 'myOidcClient3' );
$test = "OidcRp - Clean up";
checkDelete( $test, 'oidc/rp', 'myOidcRp1' );
checkDelete( $test, 'oidc/rp', 'myOidcRp2' );
$test = "OidcRp - Entity should not be found after clean up";
checkDeleteNotFound( $test, 'oidc/rp', 'myOidcRp1' );
my $metadata1 =
"<?xml version=\"1.0\"?><md:EntityDescriptor xmlns:md=\"urn:oasis:names:tc:SAML:2.0:metadata\" validUntil=\"2019-09-25T16:44:38Z\" cacheDuration=\"PT604800S\" entityID=\"https://myapp.domain.com/saml/metadata\"><md:SPSSODescriptor AuthnRequestsSigned=\"false\" WantAssertionsSigned=\"false\" protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\"><md:SingleLogoutService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\" Location=\"https://myapp.domain.com/saml/sls\" /><md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat><md:AssertionConsumerService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" Location=\"https://myapp.domain.com/saml/acs\" index=\"1\" /></md:SPSSODescriptor></md:EntityDescriptor>";
my $metadata2 =
"<?xml version=\"1.0\"?><md:EntityDescriptor xmlns:md=\"urn:oasis:names:tc:SAML:2.0:metadata\" validUntil=\"2019-09-25T16:44:38Z\" cacheDuration=\"PT604800S\" entityID=\"https://myapp2.domain.com/saml/metadata\"><md:SPSSODescriptor AuthnRequestsSigned=\"false\" WantAssertionsSigned=\"false\" protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\"><md:SingleLogoutService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\" Location=\"https://myapp2.domain.com/saml/sls\" /><md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat><md:AssertionConsumerService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" Location=\"https://myapp2.domain.com/saml/acs\" index=\"1\" /></md:SPSSODescriptor></md:EntityDescriptor>";
my $samlSp = {
confKey => 'mySamlSp1',
metadata => $metadata1,
exportedAttributes => {
family_name => {
format => "urn:oasis:names:tc:SAML:2.0:attrname-format:basic",
friendlyName => "surname",
mandatory => "false",
name => "sn"
},
cn => {
friendlyName => "commonname",
mandatory => "true",
name => "uid"
},
uid => {
mandatory => "true",
name => "uid"
},
phone => {
mandatory => "false",
format => "urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified",
name => "telephoneNumber"
},
function => {
name => "title",
mandatory => "false",
format => "urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
},
given_name => {
mandatory => "false",
name => "givenName"
}
},
options => {
samlSPMetaDataOptionsCheckSLOMessageSignature => 0,
samlSPMetaDataOptionsEncryptionMode => "assertion",
samlSPMetaDataOptionsSessionNotOnOrAfterTimeout => 36000
}
};
$test = "SamlSp - Add should succeed";
checkAdd( $test, 'saml/sp', $samlSp );
checkGet( $test, 'saml/sp', 'mySamlSp1',
'options/samlSPMetaDataOptionsEncryptionMode', 'assertion' );
checkGet( $test, 'saml/sp', 'mySamlSp1',
'options/samlSPMetaDataOptionsSessionNotOnOrAfterTimeout', 36000 );
$test = "SamlSp - Check attribute default value was set after add";
checkGet( $test, 'saml/sp', 'mySamlSp1',
'options/samlSPMetaDataOptionsNotOnOrAfterTimeout', 72000 );
$test = "SamlSp - Add Should fail on duplicate confKey";
checkAddFailsIfExists( $test, 'saml/sp', $samlSp );
$test = "SamlSp - Update should succeed and keep existing values";
$samlSp->{options}->{samlSPMetaDataOptionsCheckSLOMessageSignature} = 1;
$samlSp->{options}->{samlSPMetaDataOptionsEncryptionMode} = 'nameid';
delete $samlSp->{options}->{samlSPMetaDataOptionsSessionNotOnOrAfterTimeout};
delete $samlSp->{exportedAttributes};
$samlSp->{exportedAttributes}->{cn}->{name} = "cn",
$samlSp->{exportedAttributes}->{cn}->{friendlyName} = "common_name",
$samlSp->{exportedAttributes}->{cn}->{mandatory} = "false",
checkUpdate( $test, 'saml/sp', 'mySamlSp1', $samlSp );
checkGet( $test, 'saml/sp', 'mySamlSp1',
'options/samlSPMetaDataOptionsCheckSLOMessageSignature', 1 );
checkGet( $test, 'saml/sp', 'mySamlSp1',
'options/samlSPMetaDataOptionsSessionNotOnOrAfterTimeout', 36000 );
checkGet( $test, 'saml/sp', 'mySamlSp1', 'exportedAttributes/cn/friendlyName',
'common_name' );
checkGet( $test, 'saml/sp', 'mySamlSp1', 'exportedAttributes/cn/mandatory',
'false' );
checkGet( $test, 'saml/sp', 'mySamlSp1', 'exportedAttributes/cn/mandatory',
'false' );
checkGet( $test, 'saml/sp', 'mySamlSp1', 'exportedAttributes/cn/name', 'uid' );
checkGet( $test, 'saml/sp', 'mySamlSp1', 'exportedAttributes/given_name/name',
'givenName' );
$test = "SamlSp - Update should fail on non existing options";
$samlSp->{options}->{playingPossum} = 'elephant';
checkUpdateWithUnknownAttributes( $test, 'saml/sp', 'mySamlSp1', $samlSp );
delete $samlSp->{options}->{playingPossum};
$test = "SamlSp - Add Should fail on duplicate entityId";
$samlSp->{confKey} = 'mySamlSp2';
checkAddFailsIfExists( $test, 'saml/sp', $samlSp );
$test = "SamlSp - Add Should fail on non existing options";
$samlSp->{confKey} = 'mySamlSp2';
$samlSp->{metadata} = $metadata2;
$samlSp->{options}->{playingPossum} = 'ElephantInTheRoom';
checkAddWithUnknownAttributes( $test, 'saml/sp', $samlSp );
delete $samlSp->{options}->{playingPossum};
$test = "SamlSp - 2nd add should succeed";
checkAdd( $test, 'saml/sp', $samlSp );
$test = "SamlSp - Update should fail if client id exists";
$samlSp->{metadata} = $metadata1;
checkUpdateFailsIfExists( $test, 'saml/sp', 'mySamlSp2', $samlSp );
$test = "SamlSp - Update should fail if confKey not found";
$samlSp->{confKey} = 'mySamlSp3';
checkUpdateNotFound( $test, 'saml/sp', 'mySamlSp3', $samlSp );
$test = "SamlSp - Replace should succeed";
$samlSp->{confKey} = 'mySamlSp2';
$samlSp->{metadata} = $metadata2;
delete $samlSp->{options}->{samlSPMetaDataOptionsEncryptionMode};
checkReplace( $test, 'saml/sp', 'mySamlSp2', $samlSp );
$test = "SamlSp - Check attribute default value was set after replace";
checkGet( $test, 'saml/sp', 'mySamlSp2',
'options/samlSPMetaDataOptionsEncryptionMode', 'none' );
$test = "SamlSp - Replace should fail on non existing options";
$samlSp->{options}->{playingPossum} = 'elephant';
checkReplaceWithUnknownAttribute( $test, 'saml/sp', 'mySamlSp2', $samlSp );
delete $samlSp->{options}->{playingPossum};
$test = "SamlSp - Replace should fail if confKey not found";
$samlSp->{confKey} = 'mySamlSp3';
checkReplaceNotFound( $test, 'saml/sp', 'mySamlSp3', $samlSp );
$test = "SamlSp - FindByConfKey should find 2 hits";
checkFindByConfKey( $test, 'saml/sp', '^mySamlSp.$', 2 );
$test = "SamlSp - FindByConfKey should find 1 hit";
checkFindByConfKey( $test, 'saml/sp', 'mySamlSp1', 1 );
$test = "SamlSp - FindByConfKey should find 0 hits";
checkFindByConfKey( $test, 'saml/sp', 'mySamlSp3', 0 );
$test = "SamlSp - FindByEntityId should find one entry";
checkFindByProviderId( $test, 'saml/sp', 'entityId',
'https://myapp.domain.com/saml/metadata' );
$test = "SamlSp - FindByEntityId should find nothing";
checkFindByProviderIdNotFound( $test, 'saml/sp', 'entityId',
'https://myapp3.domain.com/saml/metadata' );
$test = "SamlSp - Clean up";
checkDelete( $test, 'saml/sp', 'mySamlSp1' );
checkDelete( $test, 'saml/sp', 'mySamlSp2' );
$test = "SamlSp - Entity should not be found after clean up";
checkDeleteNotFound( $test, 'saml/sp', 'mySamlSp1' );
# Clean up generated conf files, except for "lmConf-1.json"
unlink grep { $_ ne "t/conf/lmConf-1.json" } glob "t/conf/lmConf-*.json";
done_testing();