Skip to content

Commit 52685d9

Browse files
authored
Merge pull request #161 from timlegge/author-tests-2023-02-12
Author tests to ensure XML messages comply with schema
2 parents f38df60 + cf0b39b commit 52685d9

File tree

3 files changed

+677
-0
lines changed

3 files changed

+677
-0
lines changed

t/author/01-confirm-schema.t

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
use strict;
2+
use warnings;
3+
use Test::Lib;
4+
use Test::Net::SAML2;
5+
6+
if (!$ENV{AUTHOR_TESTING}) {
7+
plan skip_all => 'Author test skipped';
8+
}
9+
10+
use MIME::Base64 qw/decode_base64/;
11+
use XML::LibXML;
12+
use Net::SAML2;
13+
14+
my $sp = net_saml2_sp();
15+
16+
my $metadata = path('t/data/idp-samlid-metadata.xml')->slurp;
17+
18+
my $idp = Net::SAML2::IdP->new_from_xml(
19+
xml => $metadata,
20+
cacert => 't/data/cacert-samlid.pem'
21+
);
22+
isa_ok($idp, "Net::SAML2::IdP");
23+
24+
my $xsd_filename = 't/data/saml-schema-protocol-2.0.xsd';
25+
26+
sub validate_schema {
27+
my ($xml, $message) = @_;
28+
29+
my $doc = XML::LibXML->new->parse_string($xml);
30+
my $xmlschema = XML::LibXML::Schema->new( location => $xsd_filename, no_network => 0 );
31+
32+
my $result;
33+
eval { $result = $xmlschema->validate( $doc ); };
34+
35+
unlike ( $@, qr/Schemas validity error/, "XSD Schema Validation Succeeded for $message");
36+
}
37+
38+
my $post = $sp->sp_post_binding($idp, 'SAMLRequest');
39+
isa_ok($post, 'Net::SAML2::Binding::POST');
40+
41+
my $authnreq = $sp->authn_request(
42+
$idp->entityid,
43+
$idp->format('persistent')
44+
)->as_xml;
45+
46+
my $post_request = $post->sign_xml($authnreq);
47+
validate_schema(decode_base64($post_request), "AuthnRequest");
48+
49+
my $logoutreq = $sp->logout_request(
50+
$idp->slo_url('urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'),
51+
'timlegge@cpan.org',
52+
$idp->format || undef,
53+
'94750270472009384017107023022',
54+
)->as_xml;
55+
56+
$post_request = $post->sign_xml($logoutreq);
57+
validate_schema(decode_base64($post_request), "LogoutRequest");
58+
59+
my $logout_resp = Net::SAML2::Protocol::LogoutResponse->new(
60+
issuer => 'https://netsaml2-testapp.local',
61+
destination => 'destination',
62+
status => 'status',
63+
response_to => 'NETSAML2_dkajhdahdkhdkdj',
64+
)->as_xml;
65+
66+
$post_request = $post->sign_xml($logout_resp);
67+
validate_schema(decode_base64($post_request), "LogoutResponse");
68+
69+
my $art_request = $sp->artifact_request(
70+
$idp->art_url("urn:oasis:names:tc:SAML:2.0:bindings:SOAP"),
71+
'dshsodowejd9323ne292jd2odh202d',
72+
)->as_xml;
73+
74+
my $ua = LWP::UserAgent->new;
75+
76+
require LWP::Protocol::https;
77+
$ua->ssl_opts( (verify_hostname => 1));
78+
79+
my $soap = Net::SAML2::Binding::SOAP->new(
80+
ua => $ua,
81+
url => $idp->art_url("urn:oasis:names:tc:SAML:2.0:bindings:SOAP"),
82+
key => 't/net-saml2-key.pem',
83+
cert => 't/net-saml2-cert.pem',
84+
idp_cert => $idp->cert('signing'),
85+
);
86+
87+
my $soap_request = $soap->create_soap_envelope($art_request);
88+
89+
$soap_request =~ s!(<samlp:ArtifactResolve.*?</samlp:ArtifactResolve>)!!s;
90+
validate_schema($1, "ArtifactResolve");
91+
92+
done_testing;
Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
<?xml version="1.0" encoding="US-ASCII"?>
2+
<schema
3+
targetNamespace="urn:oasis:names:tc:SAML:2.0:assertion"
4+
xmlns="http://www.w3.org/2001/XMLSchema"
5+
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
6+
xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
7+
xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"
8+
elementFormDefault="unqualified"
9+
attributeFormDefault="unqualified"
10+
blockDefault="substitution"
11+
version="2.0">
12+
<import namespace="http://www.w3.org/2000/09/xmldsig#"
13+
schemaLocation="http://www.w3.org/TR/2002/REC-xmldsig-core-20020212/xmldsig-core-schema.xsd"/>
14+
<import namespace="http://www.w3.org/2001/04/xmlenc#"
15+
schemaLocation="http://www.w3.org/TR/2002/REC-xmlenc-core-20021210/xenc-schema.xsd"/>
16+
<annotation>
17+
<documentation>
18+
Document identifier: saml-schema-assertion-2.0
19+
Location: http://docs.oasis-open.org/security/saml/v2.0/
20+
Revision history:
21+
V1.0 (November, 2002):
22+
Initial Standard Schema.
23+
V1.1 (September, 2003):
24+
Updates within the same V1.0 namespace.
25+
V2.0 (March, 2005):
26+
New assertion schema for SAML V2.0 namespace.
27+
</documentation>
28+
</annotation>
29+
<attributeGroup name="IDNameQualifiers">
30+
<attribute name="NameQualifier" type="string" use="optional"/>
31+
<attribute name="SPNameQualifier" type="string" use="optional"/>
32+
</attributeGroup>
33+
<element name="BaseID" type="saml:BaseIDAbstractType"/>
34+
<complexType name="BaseIDAbstractType" abstract="true">
35+
<attributeGroup ref="saml:IDNameQualifiers"/>
36+
</complexType>
37+
<element name="NameID" type="saml:NameIDType"/>
38+
<complexType name="NameIDType">
39+
<simpleContent>
40+
<extension base="string">
41+
<attributeGroup ref="saml:IDNameQualifiers"/>
42+
<attribute name="Format" type="anyURI" use="optional"/>
43+
<attribute name="SPProvidedID" type="string" use="optional"/>
44+
</extension>
45+
</simpleContent>
46+
</complexType>
47+
<complexType name="EncryptedElementType">
48+
<sequence>
49+
<element ref="xenc:EncryptedData"/>
50+
<element ref="xenc:EncryptedKey" minOccurs="0" maxOccurs="unbounded"/>
51+
</sequence>
52+
</complexType>
53+
<element name="EncryptedID" type="saml:EncryptedElementType"/>
54+
<element name="Issuer" type="saml:NameIDType"/>
55+
<element name="AssertionIDRef" type="NCName"/>
56+
<element name="AssertionURIRef" type="anyURI"/>
57+
<element name="Assertion" type="saml:AssertionType"/>
58+
<complexType name="AssertionType">
59+
<sequence>
60+
<element ref="saml:Issuer"/>
61+
<element ref="ds:Signature" minOccurs="0"/>
62+
<element ref="saml:Subject" minOccurs="0"/>
63+
<element ref="saml:Conditions" minOccurs="0"/>
64+
<element ref="saml:Advice" minOccurs="0"/>
65+
<choice minOccurs="0" maxOccurs="unbounded">
66+
<element ref="saml:Statement"/>
67+
<element ref="saml:AuthnStatement"/>
68+
<element ref="saml:AuthzDecisionStatement"/>
69+
<element ref="saml:AttributeStatement"/>
70+
</choice>
71+
</sequence>
72+
<attribute name="Version" type="string" use="required"/>
73+
<attribute name="ID" type="ID" use="required"/>
74+
<attribute name="IssueInstant" type="dateTime" use="required"/>
75+
</complexType>
76+
<element name="Subject" type="saml:SubjectType"/>
77+
<complexType name="SubjectType">
78+
<choice>
79+
<sequence>
80+
<choice>
81+
<element ref="saml:BaseID"/>
82+
<element ref="saml:NameID"/>
83+
<element ref="saml:EncryptedID"/>
84+
</choice>
85+
<element ref="saml:SubjectConfirmation" minOccurs="0" maxOccurs="unbounded"/>
86+
</sequence>
87+
<element ref="saml:SubjectConfirmation" maxOccurs="unbounded"/>
88+
</choice>
89+
</complexType>
90+
<element name="SubjectConfirmation" type="saml:SubjectConfirmationType"/>
91+
<complexType name="SubjectConfirmationType">
92+
<sequence>
93+
<choice minOccurs="0">
94+
<element ref="saml:BaseID"/>
95+
<element ref="saml:NameID"/>
96+
<element ref="saml:EncryptedID"/>
97+
</choice>
98+
<element ref="saml:SubjectConfirmationData" minOccurs="0"/>
99+
</sequence>
100+
<attribute name="Method" type="anyURI" use="required"/>
101+
</complexType>
102+
<element name="SubjectConfirmationData" type="saml:SubjectConfirmationDataType"/>
103+
<complexType name="SubjectConfirmationDataType" mixed="true">
104+
<complexContent>
105+
<restriction base="anyType">
106+
<sequence>
107+
<any namespace="##any" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
108+
</sequence>
109+
<attribute name="NotBefore" type="dateTime" use="optional"/>
110+
<attribute name="NotOnOrAfter" type="dateTime" use="optional"/>
111+
<attribute name="Recipient" type="anyURI" use="optional"/>
112+
<attribute name="InResponseTo" type="NCName" use="optional"/>
113+
<attribute name="Address" type="string" use="optional"/>
114+
<anyAttribute namespace="##other" processContents="lax"/>
115+
</restriction>
116+
</complexContent>
117+
</complexType>
118+
<complexType name="KeyInfoConfirmationDataType" mixed="false">
119+
<complexContent>
120+
<restriction base="saml:SubjectConfirmationDataType">
121+
<sequence>
122+
<element ref="ds:KeyInfo" maxOccurs="unbounded"/>
123+
</sequence>
124+
</restriction>
125+
</complexContent>
126+
</complexType>
127+
<element name="Conditions" type="saml:ConditionsType"/>
128+
<complexType name="ConditionsType">
129+
<choice minOccurs="0" maxOccurs="unbounded">
130+
<element ref="saml:Condition"/>
131+
<element ref="saml:AudienceRestriction"/>
132+
<element ref="saml:OneTimeUse"/>
133+
<element ref="saml:ProxyRestriction"/>
134+
</choice>
135+
<attribute name="NotBefore" type="dateTime" use="optional"/>
136+
<attribute name="NotOnOrAfter" type="dateTime" use="optional"/>
137+
</complexType>
138+
<element name="Condition" type="saml:ConditionAbstractType"/>
139+
<complexType name="ConditionAbstractType" abstract="true"/>
140+
<element name="AudienceRestriction" type="saml:AudienceRestrictionType"/>
141+
<complexType name="AudienceRestrictionType">
142+
<complexContent>
143+
<extension base="saml:ConditionAbstractType">
144+
<sequence>
145+
<element ref="saml:Audience" maxOccurs="unbounded"/>
146+
</sequence>
147+
</extension>
148+
</complexContent>
149+
</complexType>
150+
<element name="Audience" type="anyURI"/>
151+
<element name="OneTimeUse" type="saml:OneTimeUseType" />
152+
<complexType name="OneTimeUseType">
153+
<complexContent>
154+
<extension base="saml:ConditionAbstractType"/>
155+
</complexContent>
156+
</complexType>
157+
<element name="ProxyRestriction" type="saml:ProxyRestrictionType"/>
158+
<complexType name="ProxyRestrictionType">
159+
<complexContent>
160+
<extension base="saml:ConditionAbstractType">
161+
<sequence>
162+
<element ref="saml:Audience" minOccurs="0" maxOccurs="unbounded"/>
163+
</sequence>
164+
<attribute name="Count" type="nonNegativeInteger" use="optional"/>
165+
</extension>
166+
</complexContent>
167+
</complexType>
168+
<element name="Advice" type="saml:AdviceType"/>
169+
<complexType name="AdviceType">
170+
<choice minOccurs="0" maxOccurs="unbounded">
171+
<element ref="saml:AssertionIDRef"/>
172+
<element ref="saml:AssertionURIRef"/>
173+
<element ref="saml:Assertion"/>
174+
<element ref="saml:EncryptedAssertion"/>
175+
<any namespace="##other" processContents="lax"/>
176+
</choice>
177+
</complexType>
178+
<element name="EncryptedAssertion" type="saml:EncryptedElementType"/>
179+
<element name="Statement" type="saml:StatementAbstractType"/>
180+
<complexType name="StatementAbstractType" abstract="true"/>
181+
<element name="AuthnStatement" type="saml:AuthnStatementType"/>
182+
<complexType name="AuthnStatementType">
183+
<complexContent>
184+
<extension base="saml:StatementAbstractType">
185+
<sequence>
186+
<element ref="saml:SubjectLocality" minOccurs="0"/>
187+
<element ref="saml:AuthnContext"/>
188+
</sequence>
189+
<attribute name="AuthnInstant" type="dateTime" use="required"/>
190+
<attribute name="SessionIndex" type="string" use="optional"/>
191+
<attribute name="SessionNotOnOrAfter" type="dateTime" use="optional"/>
192+
</extension>
193+
</complexContent>
194+
</complexType>
195+
<element name="SubjectLocality" type="saml:SubjectLocalityType"/>
196+
<complexType name="SubjectLocalityType">
197+
<attribute name="Address" type="string" use="optional"/>
198+
<attribute name="DNSName" type="string" use="optional"/>
199+
</complexType>
200+
<element name="AuthnContext" type="saml:AuthnContextType"/>
201+
<complexType name="AuthnContextType">
202+
<sequence>
203+
<choice>
204+
<sequence>
205+
<element ref="saml:AuthnContextClassRef"/>
206+
<choice minOccurs="0">
207+
<element ref="saml:AuthnContextDecl"/>
208+
<element ref="saml:AuthnContextDeclRef"/>
209+
</choice>
210+
</sequence>
211+
<choice>
212+
<element ref="saml:AuthnContextDecl"/>
213+
<element ref="saml:AuthnContextDeclRef"/>
214+
</choice>
215+
</choice>
216+
<element ref="saml:AuthenticatingAuthority" minOccurs="0" maxOccurs="unbounded"/>
217+
</sequence>
218+
</complexType>
219+
<element name="AuthnContextClassRef" type="anyURI"/>
220+
<element name="AuthnContextDeclRef" type="anyURI"/>
221+
<element name="AuthnContextDecl" type="anyType"/>
222+
<element name="AuthenticatingAuthority" type="anyURI"/>
223+
<element name="AuthzDecisionStatement" type="saml:AuthzDecisionStatementType"/>
224+
<complexType name="AuthzDecisionStatementType">
225+
<complexContent>
226+
<extension base="saml:StatementAbstractType">
227+
<sequence>
228+
<element ref="saml:Action" maxOccurs="unbounded"/>
229+
<element ref="saml:Evidence" minOccurs="0"/>
230+
</sequence>
231+
<attribute name="Resource" type="anyURI" use="required"/>
232+
<attribute name="Decision" type="saml:DecisionType" use="required"/>
233+
</extension>
234+
</complexContent>
235+
</complexType>
236+
<simpleType name="DecisionType">
237+
<restriction base="string">
238+
<enumeration value="Permit"/>
239+
<enumeration value="Deny"/>
240+
<enumeration value="Indeterminate"/>
241+
</restriction>
242+
</simpleType>
243+
<element name="Action" type="saml:ActionType"/>
244+
<complexType name="ActionType">
245+
<simpleContent>
246+
<extension base="string">
247+
<attribute name="Namespace" type="anyURI" use="required"/>
248+
</extension>
249+
</simpleContent>
250+
</complexType>
251+
<element name="Evidence" type="saml:EvidenceType"/>
252+
<complexType name="EvidenceType">
253+
<choice maxOccurs="unbounded">
254+
<element ref="saml:AssertionIDRef"/>
255+
<element ref="saml:AssertionURIRef"/>
256+
<element ref="saml:Assertion"/>
257+
<element ref="saml:EncryptedAssertion"/>
258+
</choice>
259+
</complexType>
260+
<element name="AttributeStatement" type="saml:AttributeStatementType"/>
261+
<complexType name="AttributeStatementType">
262+
<complexContent>
263+
<extension base="saml:StatementAbstractType">
264+
<choice maxOccurs="unbounded">
265+
<element ref="saml:Attribute"/>
266+
<element ref="saml:EncryptedAttribute"/>
267+
</choice>
268+
</extension>
269+
</complexContent>
270+
</complexType>
271+
<element name="Attribute" type="saml:AttributeType"/>
272+
<complexType name="AttributeType">
273+
<sequence>
274+
<element ref="saml:AttributeValue" minOccurs="0" maxOccurs="unbounded"/>
275+
</sequence>
276+
<attribute name="Name" type="string" use="required"/>
277+
<attribute name="NameFormat" type="anyURI" use="optional"/>
278+
<attribute name="FriendlyName" type="string" use="optional"/>
279+
<anyAttribute namespace="##other" processContents="lax"/>
280+
</complexType>
281+
<element name="AttributeValue" type="anyType" nillable="true"/>
282+
<element name="EncryptedAttribute" type="saml:EncryptedElementType"/>
283+
</schema>

0 commit comments

Comments
 (0)