Skip to content

Commit 02452df

Browse files
authored
Merge pull request #127 from OP-Lamminen/slo-with-xml-encoding
Fix for SLO when XML specifies encoding
2 parents c9bacbd + 771072e commit 02452df

File tree

8 files changed

+111
-23
lines changed

8 files changed

+111
-23
lines changed

src/onelogin/saml2/compat.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
if isinstance(b'', type('')): # py 2.x
2424
text_types = (basestring,) # noqa
25+
bytes_type = bytes
2526
str_type = basestring # noqa
2627

2728
def utf8(data):
@@ -42,6 +43,7 @@ def to_bytes(data):
4243

4344
else: # py 3.x
4445
text_types = (bytes, str)
46+
bytes_type = bytes
4547
str_type = str
4648

4749
def utf8(data):

src/onelogin/saml2/xml_utils.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class OneLogin_Saml2_XML(object):
2525
_parse_etree = staticmethod(fromstring)
2626
_schema_class = etree.XMLSchema
2727
_text_class = compat.text_types
28+
_bytes_class = compat.bytes_type
2829
_unparse_etree = staticmethod(tostring)
2930

3031
dump = staticmethod(etree.dump)
@@ -61,8 +62,10 @@ def to_etree(xml):
6162
"""
6263
if isinstance(xml, OneLogin_Saml2_XML._element_class):
6364
return xml
64-
if isinstance(xml, OneLogin_Saml2_XML._text_class):
65+
if isinstance(xml, OneLogin_Saml2_XML._bytes_class):
6566
return OneLogin_Saml2_XML._parse_etree(xml, forbid_dtd=True)
67+
if isinstance(xml, OneLogin_Saml2_XML._text_class):
68+
return OneLogin_Saml2_XML._parse_etree(compat.to_bytes(xml), forbid_dtd=True)
6669

6770
raise ValueError('unsupported type %r' % type(xml))
6871

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<samlp:LogoutRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
3+
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
4+
ID="ONELOGIN_21584ccdfaca36a145ae990442dcd96bfe60151e"
5+
Version="2.0"
6+
IssueInstant="2013-12-10T04:39:31Z"
7+
Destination="http://stuff.com/endpoints/endpoints/sls.php"
8+
>
9+
<saml:Issuer>http://idp.example.com/</saml:Issuer>
10+
<saml:NameID SPNameQualifier="http://idp.example.com/"
11+
Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
12+
>ONELOGIN_1e442c129e1f822c8096086a1103c5ee2c7cae1c</saml:NameID>
13+
</samlp:LogoutRequest>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<samlp:LogoutResponse xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
3+
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
4+
ID="_f9ee61bd9dbf63606faa9ae3b10548d5b3656fb859"
5+
Version="2.0"
6+
IssueInstant="2013-12-10T04:39:31Z"
7+
Destination="http://stuff.com/endpoints/endpoints/sls.php"
8+
InResponseTo="ONELOGIN_21584ccdfaca36a145ae990442dcd96bfe60151e"
9+
>
10+
<saml:Issuer>http://idp.example.com/</saml:Issuer>
11+
<samlp:Status>
12+
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" />
13+
</samlp:Status>
14+
</samlp:LogoutResponse>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
fVLdS8MwEH8X/B9K3tcmaxvWsFbELwZzAzd98EXS5KqFNgm9VPbnO6pjm1jzFO5+X8nd/GrXNsEndFhbkxMWUhKAUVbX5j0nz9v7yYxcFZcXc5Rt48TSvtvePwE6axCCPdegGFo56TsjrMQahZEtoPBKbK4fl2IaUuE6662yDbm8CP48R6X/hSQidH6fdVRpcZuTtyoD4KzUmS4rHnPKKykzCXHJaJrMdFrGPOVVOUuzUZ2Xw5/sbcfNEHtYGPTS+D2SsnjCphNGtzQRcSZi9jpKvQX0tZF+8Pjw3okoQt9XVahsG4HRztbG48kNGwzdhxsPYw6D2dqcrFd3y/XDYvU2ZeksUUpXUsmYS5akErKMJslUK53xsgJOWcpgVLf4bgwrIIYXd8VP4Fq7EHaydQ0MsefRKeiE58TGS99jcTQ5q99YDcGLbHr4f/44oMWmVwoQSRAdTKJfLofC+cYWXw==

tests/data/metadata/testshib-providers.xml

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -37,28 +37,23 @@
3737
<ds:KeyInfo>
3838
<ds:X509Data>
3939
<ds:X509Certificate>
40-
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
56+
MLRKhDgdmA==
6257
</ds:X509Certificate>
6358
</ds:X509Data>
6459
</ds:KeyInfo>

tests/src/OneLogin/saml2_tests/logout_request_test.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,38 @@ def testIsValid(self):
414414
logout_request5 = OneLogin_Saml2_Logout_Request(settings, OneLogin_Saml2_Utils.b64encode(request))
415415
self.assertTrue(logout_request5.is_valid(request_data))
416416

417+
def testIsValidWithXMLEncoding(self):
418+
"""
419+
Tests the is_valid method of the OneLogin_Saml2_LogoutRequest
420+
"""
421+
request_data = {
422+
'http_host': 'example.com',
423+
'script_name': 'index.html'
424+
}
425+
request = self.file_contents(join(self.data_path, 'logout_requests', 'logout_request_with_encoding.xml'))
426+
settings = OneLogin_Saml2_Settings(self.loadSettingsJSON())
427+
428+
logout_request = OneLogin_Saml2_Logout_Request(settings, OneLogin_Saml2_Utils.b64encode(request))
429+
self.assertTrue(logout_request.is_valid(request_data))
430+
431+
settings.set_strict(True)
432+
logout_request2 = OneLogin_Saml2_Logout_Request(settings, OneLogin_Saml2_Utils.b64encode(request))
433+
self.assertFalse(logout_request2.is_valid(request_data))
434+
435+
settings.set_strict(False)
436+
dom = parseString(request)
437+
logout_request3 = OneLogin_Saml2_Logout_Request(settings, OneLogin_Saml2_Utils.b64encode(dom.toxml()))
438+
self.assertTrue(logout_request3.is_valid(request_data))
439+
440+
settings.set_strict(True)
441+
logout_request4 = OneLogin_Saml2_Logout_Request(settings, OneLogin_Saml2_Utils.b64encode(dom.toxml()))
442+
self.assertFalse(logout_request4.is_valid(request_data))
443+
444+
current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)
445+
request = request.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url)
446+
logout_request5 = OneLogin_Saml2_Logout_Request(settings, OneLogin_Saml2_Utils.b64encode(request))
447+
self.assertTrue(logout_request5.is_valid(request_data))
448+
417449
def testIsValidRaisesExceptionWhenRaisesArgumentIsTrue(self):
418450
request = OneLogin_Saml2_Utils.b64encode('<xml>invalid</xml>')
419451
request_data = {

tests/src/OneLogin/saml2_tests/logout_response_test.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,34 @@ def testIsValid(self):
276276
response_3 = OneLogin_Saml2_Logout_Response(settings, message_3)
277277
self.assertTrue(response_3.is_valid(request_data))
278278

279+
def testIsValidWithXMLEncoding(self):
280+
"""
281+
Tests the is_valid method of the OneLogin_Saml2_LogoutResponse
282+
"""
283+
request_data = {
284+
'http_host': 'example.com',
285+
'script_name': 'index.html',
286+
'get_data': {}
287+
}
288+
settings = OneLogin_Saml2_Settings(self.loadSettingsJSON())
289+
message = self.file_contents(join(self.data_path, 'logout_responses', 'logout_response_with_encoding_deflated.xml.base64'))
290+
291+
response = OneLogin_Saml2_Logout_Response(settings, message)
292+
self.assertTrue(response.is_valid(request_data))
293+
294+
settings.set_strict(True)
295+
response_2 = OneLogin_Saml2_Logout_Response(settings, message)
296+
with self.assertRaisesRegex(Exception, 'The LogoutResponse was received at'):
297+
response_2.is_valid(request_data, raise_exceptions=True)
298+
299+
plain_message = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(message))
300+
current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)
301+
plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url)
302+
message_3 = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message)
303+
304+
response_3 = OneLogin_Saml2_Logout_Response(settings, message_3)
305+
self.assertTrue(response_3.is_valid(request_data))
306+
279307
def testIsValidRaisesExceptionWhenRaisesArgumentIsTrue(self):
280308
message = OneLogin_Saml2_Utils.deflate_and_base64_encode('<xml>invalid</xml>')
281309
request_data = {

0 commit comments

Comments
 (0)