Skip to content

Commit b0e7046

Browse files
committed
Fix for lxml issues parsing SLO XML messages specifying encoding.
- Adding bytes_type to compat - Changing OneLogin_Saml2_XML.to_etree() to always pass bytes to _parse_etree() - Adding test cases for SLO messages with XML encoding specified
1 parent a82cbdc commit b0e7046

File tree

7 files changed

+94
-1
lines changed

7 files changed

+94
-1
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/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)