Skip to content

Commit 57142a1

Browse files
authored
Merge pull request #64 from waterkip/bug-61
Fix SAML metadata signing
2 parents b18d316 + 5e6c0f0 commit 57142a1

File tree

7 files changed

+136
-38
lines changed

7 files changed

+136
-38
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,5 @@ xt/testapp/sign-nopw-cert-dsa.pem
2222
xt/testapp/sign-private-dsa.pem
2323
Release-*
2424
xt/testapp/IdPs/*/*
25+
/Net-SAML2-*/**
26+
/Net-SAML2-*.tar.gz

Makefile.PL

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ my %WriteMakefileArgs = (
2424
"DateTime" => 0,
2525
"DateTime::Format::XSD" => 0,
2626
"DateTime::HiRes" => 0,
27+
"Digest::MD5" => 0,
2728
"Exporter" => 0,
2829
"File::Slurper" => 0,
2930
"HTTP::Request::Common" => 0,
@@ -68,7 +69,7 @@ my %WriteMakefileArgs = (
6869
"URI::URL" => 0,
6970
"XML::LibXML::XPathContext" => 0
7071
},
71-
"VERSION" => "0.55",
72+
"VERSION" => "0.56",
7273
"test" => {
7374
"TESTS" => "t/*.t t/author/*.t"
7475
}
@@ -84,6 +85,7 @@ my %FallbackPrereqs = (
8485
"DateTime" => 0,
8586
"DateTime::Format::XSD" => 0,
8687
"DateTime::HiRes" => 0,
88+
"Digest::MD5" => 0,
8789
"Exporter" => 0,
8890
"File::Slurper" => 0,
8991
"HTTP::Request::Common" => 0,

README

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ NAME
22
Net::SAML2 - SAML2 bindings and protocol implementation
33

44
VERSION
5-
version 0.55
5+
version 0.56
66

77
SYNOPSIS
88
See TUTORIAL.md for implementation documentation and

cpanfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ requires "Crypt::OpenSSL::X509" => "0";
88
requires "DateTime" => "0";
99
requires "DateTime::Format::XSD" => "0";
1010
requires "DateTime::HiRes" => "0";
11+
requires "Digest::MD5" => "0";
1112
requires "Exporter" => "0";
1213
requires "File::Slurper" => "0";
1314
requires "HTTP::Request::Common" => "0";

lib/Net/SAML2/SP.pm

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ Net::SAML2::SP - SAML Service Provider object
2828
use Crypt::OpenSSL::X509;
2929
use XML::Generator;
3030

31+
use Digest::MD5 ();
32+
3133
use Net::SAML2::Binding::POST;
3234
use Net::SAML2::Binding::Redirect;
3335
use Net::SAML2::Binding::SOAP;
@@ -327,14 +329,15 @@ sub generate_metadata {
327329
return $x->EntityDescriptor(
328330
$md,
329331
{
330-
entityID => $self->id },
332+
entityID => $self->id,
333+
ID => $self->generate_sp_desciptor_id(),
334+
},
331335
$x->SPSSODescriptor(
332336
$md,
333337
{ AuthnRequestsSigned => $self->authnreq_signed,
334338
WantAssertionsSigned => $self->want_assertions_signed,
335339
errorURL => $self->url . $self->error_url,
336340
protocolSupportEnumeration => 'urn:oasis:names:tc:SAML:2.0:protocol',
337-
ID => $self->generate_sp_desciptor_id(),
338341
},
339342
$x->KeyDescriptor(
340343
$md,
@@ -348,7 +351,12 @@ sub generate_metadata {
348351
$ds,
349352
$self->_cert_text,
350353
)
351-
)
354+
),
355+
$x->KeyName(
356+
$ds,
357+
Digest::MD5::md5_hex($self->_cert_text)
358+
),
359+
352360
)
353361
),
354362
$x->SingleLogoutService(
@@ -438,6 +446,8 @@ sub metadata {
438446
sig_hash => 'sha256',
439447
digest_hash => 'sha256',
440448
x509 => 1,
449+
ns => { md => 'urn:oasis:names:tc:SAML:2.0:metadata' },
450+
id_attr => '/md:EntityDescriptor[@ID]',
441451
}
442452
);
443453
return $signer->sign($metadata);

t/02-create-sp.t

Lines changed: 107 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,8 @@ my $xpath = get_xpath(
1111
ds => 'http://www.w3.org/2000/09/xmldsig#'
1212
);
1313

14-
my $nodes = $xpath->findnodes('//md:EntityDescriptor/md:SPSSODescriptor');
15-
is($nodes->size, 1, "We have one PSSODescriptor");
16-
my $node = $nodes->get_node(1);
14+
my $node
15+
= get_single_node_ok($xpath, '//md:EntityDescriptor/md:SPSSODescriptor');
1716
ok(!$node->getAttribute('WantAssertionsSigned'),
1817
'Wants assertions to be signed');
1918
ok(
@@ -37,54 +36,129 @@ if (is(@ssos, 2, "Got two assertionConsumerService(s)")) {
3736
);
3837
}
3938

39+
get_single_node_ok($xpath, '//ds:Signature');
40+
41+
{
42+
my $sp = net_saml2_sp(sign_metadata => 0);
43+
my $xpath = get_xpath(
44+
$sp->metadata,
45+
md => 'urn:oasis:names:tc:SAML:2.0:metadata',
46+
ds => 'http://www.w3.org/2000/09/xmldsig#'
47+
);
48+
49+
my $nodes = $xpath->findnodes('//ds:Signature');
50+
is($nodes->size(), 0, "We don't have any ds:Signature present");
51+
52+
}
53+
4054
{
4155
my $sp = Net::SAML2::SP->new(
42-
id => 'http://localhost:3000',
43-
url => 'http://localhost:3000',
44-
cert => 't/sign-nopw-cert.pem',
45-
key => 't/sign-nopw-cert.pem',
46-
cacert => 't/cacert.pem',
47-
org_name => 'Test',
48-
org_display_name => 'Test',
56+
id => 'Some entity ID',
57+
url => 'http://localhost:3000',
58+
cert => 't/sign-nopw-cert.pem',
59+
key => 't/sign-nopw-cert.pem',
60+
cacert => 't/cacert.pem',
61+
62+
org_name => 'Net::SAML2::SP',
63+
org_display_name => 'Net::SAML2::SP testsuite',
4964
org_contact => 'test@example.com',
65+
5066
org_url => 'http://www.example.com',
5167
slo_url_soap => '/slo-soap',
5268
slo_url_redirect => '/sls-redirect-response',
5369
slo_url_post => '/sls-post-response',
5470
acs_url_post => '/consumer-post',
5571
acs_url_artifact => '/consumer-artifact',
56-
org_name => 'Net::SAML2 Saml2Test',
57-
org_display_name => 'Saml2Test app for Net::SAML2',
58-
org_contact => 'saml2test@example.com',
5972
error_url => '/error',
6073
);
6174

62-
my $xpath = get_xpath($sp->metadata,
63-
md => 'urn:oasis:names:tc:SAML:2.0:metadata');
64-
my $nodes = $xpath->findnodes('//md:EntityDescriptor/md:SPSSODescriptor');
65-
is($nodes->size, 1, "We have one PSSODescriptor");
66-
my $node = $nodes->get_node(1);
67-
ok($node->getAttribute('WantAssertionsSigned'),
68-
'Wants assertions to be signed');
69-
ok(
70-
$node->getAttribute('AuthnRequestsSigned'),
71-
'.. and also authn requests to be signed'
72-
);
73-
}
74-
75-
$nodes = $xpath->findnodes('//ds:Signature');
76-
is($nodes->size(), 1, "We have a signed metadata document ds:Signature present");
77-
78-
{
79-
my $sp = net_saml2_sp(sign_metadata => 0);
8075
my $xpath = get_xpath(
8176
$sp->metadata,
8277
md => 'urn:oasis:names:tc:SAML:2.0:metadata',
8378
ds => 'http://www.w3.org/2000/09/xmldsig#'
8479
);
8580

86-
my $nodes = $xpath->findnodes('//ds:Signature');
87-
is($nodes->size(), 0, "We don't have any ds:Signature present");
81+
my $node = get_single_node_ok($xpath, '/md:EntityDescriptor');
82+
is(
83+
$node->getAttribute('entityID'),
84+
'Some entity ID',
85+
'.. has the correct entity ID'
86+
);
87+
88+
ok($node->getAttribute('ID'), '.. has an ID');
89+
90+
{
91+
# Test ContactPerson
92+
my $node = get_single_node_ok($xpath, '/node()/md:ContactPerson');
93+
my $p = $node->nodePath();
94+
95+
my $company = get_single_node_ok($xpath, "$p/md:Company");
96+
is(
97+
$company->textContent,
98+
'Net::SAML2::SP testsuite',
99+
"Got the correct company name for the contact person"
100+
);
101+
102+
my $email = get_single_node_ok($xpath, "$p/md:EmailAddress");
103+
is($email->textContent, 'test@example.com',
104+
".. and the correct email");
105+
}
106+
107+
{
108+
# Test Organisation
109+
my $node = get_single_node_ok($xpath, '/node()/md:Organization');
110+
my $p = $node->nodePath();
111+
112+
my $name = get_single_node_ok($xpath, "$p/md:OrganizationName");
113+
is($name->textContent, 'Net::SAML2::SP',
114+
"Got the correct company name");
115+
116+
my $display_name
117+
= get_single_node_ok($xpath, "$p/md:OrganizationDisplayName");
118+
is(
119+
$display_name->textContent,
120+
'Net::SAML2::SP testsuite',
121+
".. and the correct display name"
122+
);
123+
124+
my $url = get_single_node_ok($xpath, "$p/md:OrganizationURL");
125+
is($url->textContent, 'http://www.example.com',
126+
".. and the correct URI");
127+
}
128+
129+
{
130+
# Test SPSSODescriptor
131+
my $node = get_single_node_ok($xpath, '/node()/md:SPSSODescriptor');
132+
is($node->getAttribute('AuthnRequestsSigned'),
133+
'1', '.. and authn request needs signing');
134+
is($node->getAttribute('WantAssertionsSigned'),
135+
'1', '.. as does assertions');
136+
is($node->getAttribute('errorURL'),
137+
'http://localhost:3000/error', 'Got the correct error URI');
138+
139+
my $p = $node->nodePath();
140+
141+
my $kd = get_single_node_ok($xpath, "$p/md:KeyDescriptor");
142+
143+
is($kd->getAttribute('use'),
144+
"signing", "Key descriptor is there for signing only");
145+
146+
my $ki = get_single_node_ok($xpath, $kd->nodePath() . "/ds:KeyInfo");
147+
148+
my $cert = get_single_node_ok($xpath,
149+
$ki->nodePath() . "/ds:X509Data/ds:X509Certificate");
150+
ok($cert->textContent, "And we have the certificate data");
151+
152+
my $keyname
153+
= get_single_node_ok($xpath, $ki->nodePath() . "/ds:KeyName");
154+
ok($keyname->textContent, "... and we have a key name");
155+
}
156+
157+
}
158+
159+
{
160+
# Test Signature
161+
my $node = get_single_node_ok($xpath, '/node()/ds:Signature');
88162

89163
}
90164

t/lib/Test/Net/SAML2/Util.pm

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ our @EXPORT = qw(
1010
get_xpath
1111
test_xml_attribute_ok
1212
test_xml_value_ok
13+
get_single_node_ok
1314
net_saml2_sp
1415
looks_like_a_cert
1516
net_saml2_binding_redirect_request
@@ -216,6 +217,14 @@ sub get_xpath {
216217
return $xp;
217218
}
218219

220+
sub get_single_node_ok {
221+
my $xpc = shift;
222+
my $xpath = shift;
223+
my $nodes = $xpc->findnodes($xpath);
224+
is($nodes->size, 1, "Got 1 node for $xpath");
225+
return $nodes->get_node(1);
226+
}
227+
219228
sub test_xml_attribute_ok {
220229
my ($xpath, $search, $value) = @_;
221230

0 commit comments

Comments
 (0)