5
from saml2 import create_class_from_xml_string, class_name, make_vals, md
6
from saml2.saml import NameID, Issuer, SubjectLocality, AuthnContextClassRef
7
from saml2.saml import SubjectConfirmationData, SubjectConfirmation
8
from saml2.saml import Attribute
10
from py.test import raises
14
from xml.etree import cElementTree as ElementTree
17
import cElementTree as ElementTree
19
from elementtree import ElementTree
22
NameID: ["""<?xml version="1.0" encoding="utf-8"?>
23
<NameID xmlns="urn:oasis:names:tc:SAML:2.0:assertion"
24
Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
25
SPProvidedID="sp provided id">
28
""", """<?xml version="1.0" encoding="utf-8"?>
29
<NameID xmlns="urn:oasis:names:tc:SAML:2.0:assertion"
30
SPNameQualifier="https://foo.example.com/sp"
31
Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient">_1632879f09d08ea5ede2dc667cbed7e429ebc4335c</NameID>
32
""", """<?xml version="1.0" encoding="utf-8"?>
33
<NameID xmlns="urn:oasis:names:tc:SAML:2.0:assertion"
34
Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"
35
NameQualifier="http://authentic.example.com/saml/metadata"
36
SPNameQualifier="http://auth.example.com/saml/metadata">test
38
Issuer: """<?xml version="1.0" encoding="utf-8"?>
39
<Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">
40
http://www.example.com/test
43
SubjectLocality: """<?xml version="1.0" encoding="utf-8"?>
44
<SubjectLocality xmlns="urn:oasis:names:tc:SAML:2.0:assertion"
45
Address="127.0.0.1" DNSName="localhost"/>
47
SubjectConfirmationData:
48
"""<?xml version="1.0" encoding="utf-8"?>
49
<SubjectConfirmationData xmlns="urn:oasis:names:tc:SAML:2.0:assertion"
50
InResponseTo="_1683146e27983964fbe7bf8f08961108d166a652e5"
51
NotOnOrAfter="2010-02-18T13:52:13.959Z"
52
NotBefore="2010-01-16T12:00:00Z"
53
Recipient="http://192.168.0.10/saml/sp" />""",
55
"""<?xml version="1.0" encoding="utf-8"?>
56
<SubjectConfirmation xmlns="urn:oasis:names:tc:SAML:2.0:assertion"
57
Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><NameID
58
Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
59
NameQualifier="http://authentic.example.com/saml/metadata">test@example.com
61
<SubjectConfirmationData
62
NotOnOrAfter="2010-02-17T17:02:38Z"
63
Recipient="http://auth.example.com/saml/proxySingleSignOnRedirect"
64
InResponseTo="_59B3A01B03334032C31E434C63F89E3E"/></SubjectConfirmation>"""
67
#def pytest_generate_tests(metafunc):
68
# if "target_class" in metafunc.funcargnames:
69
# for tcl,xml in ITEMS.items():
70
# metafunc.addcall(funcargs={"target_class":tcl,"xml_string":xml})
73
return set(l1) == set(l2)
76
def test_create_class_from_xml_string_nameid():
77
kl = create_class_from_xml_string(NameID, ITEMS[NameID][0])
79
assert kl.format == "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
80
assert kl.sp_provided_id == "sp provided id"
81
assert kl.text.strip() == "roland@example.com"
82
assert _eq(kl.keyswv(), ['sp_provided_id', 'format', 'text'])
83
assert class_name(kl) == "urn:oasis:names:tc:SAML:2.0:assertion:NameID"
84
assert _eq(kl.keys(), ['sp_provided_id', 'sp_name_qualifier',
85
'name_qualifier', 'format', 'text'])
87
kl = create_class_from_xml_string(NameID, ITEMS[NameID][1])
89
assert kl.format == "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
90
assert kl.sp_name_qualifier == "https://foo.example.com/sp"
91
assert kl.text.strip() == "_1632879f09d08ea5ede2dc667cbed7e429ebc4335c"
92
assert _eq(kl.keyswv(), ['sp_name_qualifier', 'format', 'text'])
93
assert class_name(kl) == "urn:oasis:names:tc:SAML:2.0:assertion:NameID"
95
kl = create_class_from_xml_string(NameID, ITEMS[NameID][2])
97
assert kl.format == "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"
98
assert kl.name_qualifier == "http://authentic.example.com/saml/metadata"
99
assert kl.sp_name_qualifier == "http://auth.example.com/saml/metadata"
100
assert kl.text.strip() == "test"
101
assert _eq(kl.keyswv(), ['sp_name_qualifier', 'format', 'name_qualifier',
103
assert class_name(kl) == "urn:oasis:names:tc:SAML:2.0:assertion:NameID"
106
def test_create_class_from_xml_string_issuer():
107
kl = create_class_from_xml_string(Issuer, ITEMS[Issuer])
109
assert kl.text.strip() == "http://www.example.com/test"
110
assert _eq(kl.keyswv(), ['text'])
111
assert class_name(kl) == "urn:oasis:names:tc:SAML:2.0:assertion:Issuer"
114
def test_create_class_from_xml_string_subject_locality():
115
kl = create_class_from_xml_string(SubjectLocality, ITEMS[SubjectLocality])
117
assert _eq(kl.keyswv(), ['address', "dns_name"])
118
assert kl.address == "127.0.0.1"
119
assert kl.dns_name == "localhost"
121
kl) == "urn:oasis:names:tc:SAML:2.0:assertion:SubjectLocality"
124
def test_create_class_from_xml_string_subject_confirmation_data():
125
kl = create_class_from_xml_string(SubjectConfirmationData,
126
ITEMS[SubjectConfirmationData])
128
assert _eq(kl.keyswv(), ['in_response_to', 'not_on_or_after',
129
'not_before', 'recipient'])
130
assert kl.in_response_to == "_1683146e27983964fbe7bf8f08961108d166a652e5"
131
assert kl.not_on_or_after == "2010-02-18T13:52:13.959Z"
132
assert kl.not_before == "2010-01-16T12:00:00Z"
133
assert kl.recipient == "http://192.168.0.10/saml/sp"
134
assert class_name(kl) == \
135
"urn:oasis:names:tc:SAML:2.0:assertion:SubjectConfirmationData"
138
def test_create_class_from_xml_string_subject_confirmation():
139
kl = create_class_from_xml_string(SubjectConfirmation,
140
ITEMS[SubjectConfirmation])
142
assert _eq(kl.keyswv(), ['method', 'name_id',
143
'subject_confirmation_data'])
144
assert kl.method == "urn:oasis:names:tc:SAML:2.0:cm:bearer"
146
assert _eq(name_id.keyswv(), ['format', 'name_qualifier', 'text'])
147
assert name_id.format == "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
148
assert name_id.name_qualifier == "http://authentic.example.com/saml/metadata"
149
assert name_id.text.strip() == "test@example.com"
150
subject_confirmation_data = kl.subject_confirmation_data
151
assert _eq(subject_confirmation_data.keyswv(), ['not_on_or_after',
154
assert subject_confirmation_data.recipient == \
155
"http://auth.example.com/saml/proxySingleSignOnRedirect"
156
assert subject_confirmation_data.not_on_or_after == "2010-02-17T17:02:38Z"
157
assert subject_confirmation_data.in_response_to == \
158
"_59B3A01B03334032C31E434C63F89E3E"
159
assert class_name(kl) == \
160
"urn:oasis:names:tc:SAML:2.0:assertion:SubjectConfirmation"
163
def test_create_class_from_xml_string_wrong_class_spec():
164
kl = create_class_from_xml_string(SubjectConfirmationData,
165
ITEMS[SubjectConfirmation])
170
ee = saml2.extension_element_from_string(
171
"""<?xml version='1.0' encoding='UTF-8'?><foo>bar</foo>""")
174
assert ee.attributes == {}
175
assert ee.tag == "foo"
176
assert ee.namespace == None
177
assert ee.children == []
178
assert ee.text == "bar"
182
ee = saml2.extension_element_from_string(
183
"""<?xml version='1.0' encoding='UTF-8'?><foo id="xyz">bar</foo>""")
186
assert ee.attributes == {"id": "xyz"}
187
assert ee.tag == "foo"
188
assert ee.namespace == None
189
assert ee.children == []
190
assert ee.text == "bar"
194
ee = saml2.extension_element_from_string(
195
"""<?xml version='1.0' encoding='UTF-8'?>
196
<foo xmlns="urn:mace:example.com:saml:ns"
197
id="xyz">bar</foo>""")
200
assert ee.attributes == {"id": "xyz"}
201
assert ee.tag == "foo"
202
assert ee.namespace == "urn:mace:example.com:saml:ns"
203
assert ee.children == []
204
assert ee.text == "bar"
208
ee = saml2.extension_element_from_string(
209
"""<?xml version='1.0' encoding='UTF-8'?>
210
<foo xmlns="urn:mace:example.com:saml:ns">
211
<id>xyz</id><bar>tre</bar></foo>""")
214
assert ee.attributes == {}
215
assert ee.tag == "foo"
216
assert ee.namespace == "urn:mace:example.com:saml:ns"
217
assert len(ee.children) == 2
218
assert ee.text.strip() == ""
219
cid = ee.find_children("id", "urn:mace:example.com:saml:namespace")
221
ids = ee.find_children("id", "urn:mace:example.com:saml:ns")
225
assert cid.attributes == {}
226
assert cid.tag == "id"
227
assert cid.namespace == "urn:mace:example.com:saml:ns"
228
assert cid.children == []
229
assert cid.text.strip() == "xyz"
233
ee = saml2.extension_element_from_string(
234
"""<?xml version='1.0' encoding='UTF-8'?>
235
<foo xmlns="urn:mace:example.com:saml:ns">bar</foo>""")
237
ce = saml2.extension_element_from_string(
238
"""<?xml version='1.0' encoding='UTF-8'?>
239
<educause xmlns="urn:mace:example.com:saml:cu">rev</educause>""")
241
ee.children.append(ce)
245
assert ee.attributes == {}
246
assert ee.tag == "foo"
247
assert ee.namespace == "urn:mace:example.com:saml:ns"
248
assert len(ee.children) == 1
249
assert ee.text.strip() == "bar"
254
child = ee.find_children(namespace="urn:mace:example.com:saml:cu")
255
assert len(child) == 1
256
child = ee.find_children(namespace="urn:mace:example.com:saml:ns")
257
assert len(child) == 0
258
child = ee.find_children("educause", "urn:mace:example.com:saml:cu")
259
assert len(child) == 1
260
child = ee.find_children("edugain", "urn:mace:example.com:saml:cu")
261
assert len(child) == 0
266
ee = saml2.extension_element_from_string(
267
"""<?xml version='1.0' encoding='UTF-8'?>
268
<foo xmlns="urn:mace:example.com:saml:ns">bar</foo>""")
270
ce = saml2.extension_element_from_string(
271
"""<?xml version='1.0' encoding='UTF-8'?>
272
<educause xmlns="urn:mace:example.com:saml:cu">rev</educause>""")
274
et = ee.transfer_to_element_tree()
275
ce.become_child_element_of(et)
277
pee = saml2._extension_element_from_element_tree(et)
281
assert pee.attributes == {}
282
assert pee.tag == "foo"
283
assert pee.namespace == "urn:mace:example.com:saml:ns"
284
assert len(pee.children) == 1
285
assert pee.text.strip() == "bar"
290
child = pee.find_children(namespace="urn:mace:example.com:saml:cu")
291
assert len(child) == 1
292
child = pee.find_children(namespace="urn:mace:example.com:saml:ns")
293
assert len(child) == 0
294
child = pee.find_children("educause", "urn:mace:example.com:saml:cu")
295
assert len(child) == 1
296
child = pee.find_children("edugain", "urn:mace:example.com:saml:cu")
297
assert len(child) == 0
298
print pee.to_string()
301
NAMEID_WITH_ATTRIBUTE_EXTENSION = """<?xml version="1.0" encoding="utf-8"?>
302
<NameID xmlns="urn:oasis:names:tc:SAML:2.0:assertion"
303
xmlns:local="urn:mace:example.com:saml:assertion"
304
Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
305
SPProvidedID="sp provided id"
312
def test_nameid_with_extension():
313
kl = create_class_from_xml_string(NameID, NAMEID_WITH_ATTRIBUTE_EXTENSION)
316
assert kl.format == "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
317
assert kl.sp_provided_id == "sp provided id"
318
assert kl.text.strip() == "roland@example.com"
319
assert _eq(kl.keyswv(), ['sp_provided_id', 'format',
320
'extension_attributes', 'text'])
321
assert class_name(kl) == "urn:oasis:names:tc:SAML:2.0:assertion:NameID"
322
assert _eq(kl.keys(), ['sp_provided_id', 'sp_name_qualifier',
323
'name_qualifier', 'format', 'text'])
324
assert kl.extension_attributes == {
325
'{urn:mace:example.com:saml:assertion}Foo': 'BAR'}
328
SUBJECT_CONFIRMATION_WITH_MEMBER_EXTENSION = """<?xml version="1.0" encoding="utf-8"?>
329
<SubjectConfirmation xmlns="urn:oasis:names:tc:SAML:2.0:assertion"
330
Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
332
Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
333
NameQualifier="http://authentic.example.com/saml/metadata">test@example.com
335
<SubjectConfirmationData
336
NotOnOrAfter="2010-02-17T17:02:38Z"
337
Recipient="http://auth.example.com/saml/proxySingleSignOnRedirect"
338
InResponseTo="_59B3A01B03334032C31E434C63F89E3E"/>
339
<local:Trustlevel xmlns:local="urn:mace:example.com:saml:assertion">
342
</SubjectConfirmation>"""
345
def test_subject_confirmation_with_extension():
346
kl = create_class_from_xml_string(SubjectConfirmation,
347
SUBJECT_CONFIRMATION_WITH_MEMBER_EXTENSION)
350
assert kl.extension_attributes == {}
351
assert kl.method == "urn:oasis:names:tc:SAML:2.0:cm:bearer"
353
assert _eq(name_id.keyswv(), ['format', 'name_qualifier', 'text'])
354
assert name_id.format == "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
355
assert name_id.name_qualifier == "http://authentic.example.com/saml/metadata"
356
assert name_id.text.strip() == "test@example.com"
357
subject_confirmation_data = kl.subject_confirmation_data
358
assert _eq(subject_confirmation_data.keyswv(), ['not_on_or_after',
361
assert subject_confirmation_data.recipient == \
362
"http://auth.example.com/saml/proxySingleSignOnRedirect"
363
assert subject_confirmation_data.not_on_or_after == "2010-02-17T17:02:38Z"
364
assert subject_confirmation_data.in_response_to == \
365
"_59B3A01B03334032C31E434C63F89E3E"
366
assert len(kl.extension_elements) == 1
367
ee = kl.extension_elements[0]
368
assert ee.tag == "Trustlevel"
369
assert ee.namespace == "urn:mace:example.com:saml:assertion"
370
assert ee.text.strip() == "Excellent"
373
def test_to_fro_string_1():
374
kl = create_class_from_xml_string(SubjectConfirmation,
375
SUBJECT_CONFIRMATION_WITH_MEMBER_EXTENSION)
377
cpy = create_class_from_xml_string(SubjectConfirmation, txt)
382
assert kl.text.strip() == cpy.text.strip()
383
assert _eq(kl.keyswv(), cpy.keyswv())
384
assert len(kl.extension_elements) == len(cpy.extension_elements)
385
klee = kl.extension_elements[0]
386
cpyee = cpy.extension_elements[0]
387
assert klee.text.strip() == cpyee.text.strip()
388
assert klee.tag == cpyee.tag
389
assert klee.namespace == cpyee.namespace
392
def test_make_vals_str():
393
kl = make_vals("Jeter", md.GivenName, part=True)
394
assert isinstance(kl, md.GivenName)
395
assert kl.text == "Jeter"
398
def test_make_vals_list_of_strs():
399
cp = md.ContactPerson()
400
make_vals(["Derek", "Sanderson"], md.GivenName, cp, "given_name")
401
assert len(cp.given_name) == 2
402
assert _eq([i.text for i in cp.given_name], ["Sanderson", "Derek"])
405
def test_attribute_element_to_extension_element():
406
attr = create_class_from_xml_string(Attribute, saml2_data.TEST_ATTRIBUTE)
407
ee = saml2.element_to_extension_element(attr)
409
assert ee.tag == "Attribute"
410
assert ee.namespace == 'urn:oasis:names:tc:SAML:2.0:assertion'
411
assert _eq(ee.attributes.keys(), ['FriendlyName', 'Name', 'NameFormat'])
412
assert ee.attributes["FriendlyName"] == 'test attribute'
413
assert ee.attributes["Name"] == "testAttribute"
414
assert ee.attributes["NameFormat"] == \
415
'urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified'
416
assert len(ee.children) == 2
417
for child in ee.children:
418
# children are also extension element instances
419
assert child.namespace == 'urn:oasis:names:tc:SAML:2.0:assertion'
420
assert child.tag == "AttributeValue"
424
ee = saml2.extension_element_from_string(
425
"""<?xml version='1.0' encoding='UTF-8'?>
426
<ExternalEntityAttributeAuthority
427
xmlns="urn:oasis:names:tc:SAML:metadata:dynamicsaml">
429
<NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">
430
http://federationX.org
434
https://federationX.org/?ID=a87s76a5765da76576a57as
436
</ExternalEntityAttributeAuthority>
440
assert len(ee.children) == 2
441
for child in ee.children:
442
assert child.namespace == "urn:oasis:names:tc:SAML:metadata:dynamicsaml"
443
assert _eq(["AssertingEntity", "RetrievalEndpoint"],
444
[c.tag for c in ee.children])
445
aes = [c for c in ee.children if c.tag == "AssertingEntity"]
447
assert len(aes[0].children) == 1
448
assert _eq(aes[0].attributes.keys(), [])
449
nid = aes[0].children[0]
450
assert nid.tag == "NameID"
451
assert nid.namespace == "urn:oasis:names:tc:SAML:metadata:dynamicsaml"
452
assert len(nid.children) == 0
453
assert _eq(nid.attributes.keys(), ["Format"])
454
assert nid.text.strip() == "http://federationX.org"
457
def test_extension_element_loadd():
458
ava = {'attributes': {},
459
'tag': 'ExternalEntityAttributeAuthority',
460
'namespace': 'urn:oasis:names:tc:SAML:metadata:dynamicsaml',
462
"tag": "AssertingEntity",
463
"namespace": "urn:oasis:names:tc:SAML:metadata:dynamicsaml",
467
"namespace": "urn:oasis:names:tc:SAML:metadata:dynamicsaml",
468
"text": "http://federationX.org",
470
"Format": "urn:oasis:names:tc:SAML:2.0:nameid-format:entity"
474
"tag": "RetrievalEndpoint",
475
"namespace": "urn:oasis:names:tc:SAML:metadata"
477
"text": "https://federationX.org/?ID=a87s76a5765da76576a57as",
481
ee = saml2.ExtensionElement(ava["tag"]).loadd(ava)
483
assert len(ee.children) == 2
484
for child in ee.children:
485
assert child.namespace == "urn:oasis:names:tc:SAML:metadata:dynamicsaml"
486
assert _eq(["AssertingEntity", "RetrievalEndpoint"],
487
[c.tag for c in ee.children])
488
aes = [c for c in ee.children if c.tag == "AssertingEntity"]
490
assert len(aes[0].children) == 1
491
assert _eq(aes[0].attributes.keys(), [])
492
nid = aes[0].children[0]
493
assert nid.tag == "NameID"
494
assert nid.namespace == "urn:oasis:names:tc:SAML:metadata:dynamicsaml"
495
assert len(nid.children) == 0
496
assert _eq(nid.attributes.keys(), ["Format"])
497
assert nid.text.strip() == "http://federationX.org"
500
def test_extensions_loadd():
501
ava = {"extension_elements": [
504
'tag': 'ExternalEntityAttributeAuthority',
505
'namespace': 'urn:oasis:names:tc:SAML:metadata:dynamicsaml',
507
{"tag": "AssertingEntity",
508
"namespace": "urn:oasis:names:tc:SAML:metadata:dynamicsaml",
511
"namespace": "urn:oasis:names:tc:SAML:metadata:dynamicsaml",
512
"text": "http://federationX.org",
514
"Format": "urn:oasis:names:tc:SAML:2.0:nameid-format:entity"
519
"tag": "RetrievalEndpoint",
520
"namespace": "urn:oasis:names:tc:SAML:metadata:dynamicsaml",
521
"text": "https://federationX.org/?ID=a87s76a5765da76576a57as",
524
"extension_attributes": {
529
extension = saml2.SamlBase()
532
print extension.__dict__
533
assert len(extension.extension_elements) == 1
534
ee = extension.extension_elements[0]
535
assert len(ee.children) == 2
536
for child in ee.children:
537
assert child.namespace == "urn:oasis:names:tc:SAML:metadata:dynamicsaml"
538
assert _eq(["AssertingEntity", "RetrievalEndpoint"],
539
[c.tag for c in ee.children])
540
aes = [c for c in ee.children if c.tag == "AssertingEntity"]
542
assert len(aes[0].children) == 1
543
assert _eq(aes[0].attributes.keys(), [])
544
nid = aes[0].children[0]
545
assert nid.tag == "NameID"
546
assert nid.namespace == "urn:oasis:names:tc:SAML:metadata:dynamicsaml"
547
assert len(nid.children) == 0
548
assert _eq(nid.attributes.keys(), ["Format"])
549
assert nid.text.strip() == "http://federationX.org"
551
assert extension.extension_attributes.keys() == ["foo"]
552
assert extension.extension_attributes["foo"] == "bar"