Skip to content

Commit f428b72

Browse files
committed
Add more test. Fix bug getting last response on process_slo
1 parent a088a3e commit f428b72

7 files changed

Lines changed: 169 additions & 17 deletions

File tree

src/onelogin/saml2/auth.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ def process_slo(self, keep_local_session=False, request_id=None, delete_session_
128128

129129
if 'get_data' in self.__request_data and 'SAMLResponse' in self.__request_data['get_data']:
130130
logout_response = OneLogin_Saml2_Logout_Response(self.__settings, self.__request_data['get_data']['SAMLResponse'])
131-
self.__logout_response = logout_response.get_xml()
131+
self.__last_response = logout_response.get_xml()
132132
if not logout_response.is_valid(self.__request_data, request_id):
133133
self.__errors.append('invalid_logout_response')
134134
self.__error_reason = logout_response.get_error()
@@ -150,7 +150,7 @@ def process_slo(self, keep_local_session=False, request_id=None, delete_session_
150150
in_response_to = logout_request.id
151151
response_builder = OneLogin_Saml2_Logout_Response(self.__settings)
152152
response_builder.build(in_response_to)
153-
self.__logout_response = response_builder.get_xml()
153+
self.__last_response = response_builder.get_xml()
154154
logout_response = response_builder.get_response()
155155

156156
parameters = {'SAMLResponse': logout_response}
@@ -465,7 +465,7 @@ def get_last_response_xml(self, pretty_print_if_possible=False):
465465
:rtype: string|None
466466
"""
467467
response = None
468-
if self.__last_response:
468+
if self.__last_response is not None:
469469
if isinstance(self.__last_response, basestring):
470470
response = self.__last_response
471471
else:
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="pfxc3d2b542-0f7e-8767-8e87-5b0dc6913375" Version="2.0" IssueInstant="2014-03-21T13:41:09Z" Destination="https://pitbulk.no-ip.org/newonelogin/demo1/index.php?acs" InResponseTo="ONELOGIN_5d9e319c1b8a67da48227964c28d280e7860f804">
2+
<saml:Issuer>https://pitbulk.no-ip.org/simplesaml/saml2/idp/metadata.php</saml:Issuer>
3+
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
4+
<ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
5+
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
6+
<ds:Reference URI="#pfxc3d2b542-0f7e-8767-8e87-5b0dc6913375"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue>1dQFiYU0o2OF7c/RVV8Gpgb4u3I=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>wRgBXOq/FiLZc2mureTC/j6zY709OikJ5HeUSruHTdYjEg9aZy1RbxlKIYEIfXpnX7NBoKxfAMm+O0fsrqOjgcYxTVkqZjOr71qiXNbtwjeAkdYSpk5brsAcnfcPdv8QReYr3D7t5ZVCgYuvXQ+dNELKeag7e1ASOzVqOdp5Z9Y=</ds:SignatureValue>
7+
<ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIICgTCCAeoCCQCbOlrWDdX7FTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMCTk8xGDAWBgNVBAgTD0FuZHJlYXMgU29sYmVyZzEMMAoGA1UEBxMDRm9vMRAwDgYDVQQKEwdVTklORVRUMRgwFgYDVQQDEw9mZWlkZS5lcmxhbmcubm8xITAfBgkqhkiG9w0BCQEWEmFuZHJlYXNAdW5pbmV0dC5ubzAeFw0wNzA2MTUxMjAxMzVaFw0wNzA4MTQxMjAxMzVaMIGEMQswCQYDVQQGEwJOTzEYMBYGA1UECBMPQW5kcmVhcyBTb2xiZXJnMQwwCgYDVQQHEwNGb28xEDAOBgNVBAoTB1VOSU5FVFQxGDAWBgNVBAMTD2ZlaWRlLmVybGFuZy5ubzEhMB8GCSqGSIb3DQEJARYSYW5kcmVhc0B1bmluZXR0Lm5vMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDivbhR7P516x/S3BqKxupQe0LONoliupiBOesCO3SHbDrl3+q9IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHISKOtPlAeTZSnb8QAu7aRjZq3+PbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d1EDwXJW1rRXuUt4C8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACDVfp86HObqY+e8BUoWQ9+VMQx1ASDohBjwOsg2WykUqRXF+dLfcUH9dWR63CtZIKFDbStNomPnQz7nbK+onygwBspVEbnHuUihZq3ZUdmumQqCw4Uvs/1Uvq3orOo/WJVhTyvLgFVK2QarQ4/67OZfHd7R+POBXhophSMv1ZOo</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature>
8+
<samlp:Status>
9+
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
10+
</samlp:Status>
11+
<saml:Assertion xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" ID="_cccd6024116641fe48e0ae2c51220d02755f96c98d" Version="2.0" IssueInstant="2014-03-21T13:41:09Z">
12+
<saml:Issuer>https://pitbulk.no-ip.org/simplesaml/saml2/idp/metadata.php</saml:Issuer>
13+
<saml:Subject>
14+
<saml:NameID SPNameQualifier="https://pitbulk.no-ip.org/newonelogin/demo1/metadata.php" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient">_b98f98bb1ab512ced653b58baaff543448daed535d</saml:NameID>
15+
<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
16+
<saml:SubjectConfirmationData NotOnOrAfter="2023-09-22T19:01:09Z" Recipient="https://pitbulk.no-ip.org/newonelogin/demo1/index.php?acs" InResponseTo="ONELOGIN_5d9e319c1b8a67da48227964c28d280e7860f804"/>
17+
</saml:SubjectConfirmation>
18+
</saml:Subject>
19+
<saml:Conditions NotBefore="2014-03-21T13:40:39Z" NotOnOrAfter="2023-09-22T19:01:09Z">
20+
<saml:AudienceRestriction>
21+
<saml:Audience>https://pitbulk.no-ip.org/newonelogin/demo1/metadata.php</saml:Audience>
22+
</saml:AudienceRestriction>
23+
</saml:Conditions>
24+
<saml:AuthnStatement AuthnInstant="2014-03-21T13:41:09Z" SessionNotOnOrAfter="2014-03-21T21:41:09Z" SessionIndex="_9fe0c8dcd3302e7364fcab22a52748ebf2224df0aa">
25+
<saml:AuthnContext>
26+
<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml:AuthnContextClassRef>
27+
</saml:AuthnContext>
28+
</saml:AuthnStatement>
29+
<saml:AttributeStatement>
30+
<saml:Attribute Name="uid" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
31+
<saml:AttributeValue xsi:type="xs:string">test</saml:AttributeValue>
32+
</saml:Attribute>
33+
<saml:Attribute Name="mail" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
34+
<saml:AttributeValue xsi:type="xs:string">test@example.com</saml:AttributeValue>
35+
</saml:Attribute>
36+
<saml:Attribute Name="cn" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
37+
<saml:AttributeValue xsi:type="xs:string">test</saml:AttributeValue>
38+
</saml:Attribute>
39+
<saml:Attribute Name="sn" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
40+
<saml:AttributeValue xsi:type="xs:string">waa2</saml:AttributeValue>
41+
</saml:Attribute>
42+
<saml:Attribute Name="eduPersonAffiliation" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
43+
<saml:AttributeValue xsi:type="xs:string">user</saml:AttributeValue>
44+
<saml:AttributeValue xsi:type="xs:string">admin</saml:AttributeValue>
45+
</saml:Attribute>
46+
</saml:AttributeStatement>
47+
</saml:Assertion>
48+
</samlp:Response>

tests/src/OneLogin/saml2_tests/auth_test.py

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -910,18 +910,26 @@ def testBuildResponseSignature(self):
910910
except Exception as e:
911911
self.assertIn("Trying to sign the SAMLResponse but can't load the SP private key", e.message)
912912

913-
def testGetLastDecryptedResponse(self):
913+
def testGetLastSAMLResponse(self):
914914
settings = self.loadSettingsJSON()
915+
message = self.file_contents(join(self.data_path, 'responses', 'signed_message_response.xml.base64'))
916+
message_wrapper = {'post_data': {'SAMLResponse': message}}
917+
auth = OneLogin_Saml2_Auth(message_wrapper, old_settings=settings)
918+
auth.process_response()
919+
expected_message = self.file_contents(join(self.data_path, 'responses', 'pretty_signed_message_response.xml'))
920+
self.assertEqual(auth.get_last_response_xml(True), expected_message)
921+
922+
# with encrypted assertion
915923
message = self.file_contents(join(self.data_path, 'responses', 'valid_encrypted_assertion.xml.base64'))
916924
message_wrapper = {'post_data': {'SAMLResponse': message}}
917925
auth = OneLogin_Saml2_Auth(message_wrapper, old_settings=settings)
918926
auth.process_response()
919927
decrypted_response = self.file_contents(join(self.data_path, 'responses', 'decrypted_valid_encrypted_assertion.xml'))
920928
self.assertEqual(auth.get_last_response_xml(False), decrypted_response)
921-
decrypted_response = self.file_contents(join(self.data_path, 'responses', 'pretty_decrypted_valid_encrypted_assertion.xml'))
922-
self.assertEqual(auth.get_last_response_xml(True), decrypted_response)
929+
pretty_decrypted_response = self.file_contents(join(self.data_path, 'responses', 'pretty_decrypted_valid_encrypted_assertion.xml'))
930+
self.assertEqual(auth.get_last_response_xml(True), pretty_decrypted_response)
923931

924-
def testGetLastSentRequest(self):
932+
def testGetLastAuthnRequest(self):
925933
settings = self.loadSettingsJSON()
926934
auth = OneLogin_Saml2_Auth({'http_host': 'localhost', 'script_name': 'thing'}, old_settings=settings)
927935
auth.login()
@@ -940,6 +948,51 @@ def testGetLastSentRequest(self):
940948
)
941949
self.assertIn(expectedFragment, auth.get_last_request_xml())
942950

951+
def testGetLastLogoutRequest(self):
952+
settings = self.loadSettingsJSON()
953+
auth = OneLogin_Saml2_Auth({'http_host': 'localhost', 'script_name': 'thing'}, old_settings=settings)
954+
auth.logout()
955+
expectedFragment = (
956+
' Destination="http://idp.example.com/SingleLogoutService.php">\n'
957+
' <saml:Issuer>http://stuff.com/endpoints/metadata.php</saml:Issuer>\n'
958+
' <saml:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity" SPNameQualifier="http://stuff.com/endpoints/metadata.php">http://idp.example.com/</saml:NameID>\n'
959+
' \n </samlp:LogoutRequest>'
960+
)
961+
self.assertIn(expectedFragment, auth.get_last_request_xml())
962+
963+
request = self.file_contents(join(self.data_path, 'logout_requests', 'logout_request.xml'))
964+
message = OneLogin_Saml2_Utils.deflate_and_base64_encode(request)
965+
message_wrapper = {'get_data': {'SAMLRequest': message}}
966+
auth = OneLogin_Saml2_Auth(message_wrapper, old_settings=settings)
967+
auth.process_slo()
968+
self.assertEqual(request, auth.get_last_request_xml())
969+
970+
def testGetLastLogoutResponse(self):
971+
settings = self.loadSettingsJSON()
972+
request = self.file_contents(join(self.data_path, 'logout_requests', 'logout_request.xml'))
973+
message = OneLogin_Saml2_Utils.deflate_and_base64_encode(request)
974+
message_wrapper = {'get_data': {'SAMLRequest': message}}
975+
auth = OneLogin_Saml2_Auth(message_wrapper, old_settings=settings)
976+
auth.process_slo()
977+
expectedFragment = (
978+
'Destination="http://idp.example.com/SingleLogoutService.php"\n'
979+
' InResponseTo="ONELOGIN_21584ccdfaca36a145ae990442dcd96bfe60151e"\n>\n'
980+
' <saml:Issuer>http://stuff.com/endpoints/metadata.php</saml:Issuer>\n'
981+
' <samlp:Status>\n'
982+
' <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" />\n'
983+
' </samlp:Status>\n'
984+
'</samlp:LogoutResponse>'
985+
)
986+
self.assertIn(expectedFragment, auth.get_last_response_xml())
987+
988+
response = self.file_contents(join(self.data_path, 'logout_responses', 'logout_response.xml'))
989+
message = OneLogin_Saml2_Utils.deflate_and_base64_encode(response)
990+
message_wrapper = {'get_data': {'SAMLResponse': message}}
991+
auth = OneLogin_Saml2_Auth(message_wrapper, old_settings=settings)
992+
auth.process_slo()
993+
self.assertEqual(response, auth.get_last_response_xml())
994+
995+
943996
if __name__ == '__main__':
944997
if is_running_under_teamcity():
945998
runner = TeamcityTestRunner()

tests/src/OneLogin/saml2_tests/authn_request_test.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,25 +64,25 @@ def testCreateRequest(self):
6464
self.assertRegexpMatches(inflated, '^<samlp:AuthnRequest')
6565
self.assertNotIn('ProviderName="SP test"', inflated)
6666

67-
def testGetRequestXML(self):
67+
def testGetXML(self):
6868
"""
6969
Tests that we can get the request XML directly without
7070
going through intermediate steps
7171
"""
72-
7372
saml_settings = self.loadSettingsJSON()
74-
settings = OneLogin_Saml2_Settings(saml_settings)
75-
settings._OneLogin_Saml2_Settings__organization = {
73+
saml_settings['organization'] = {
7674
u'en-US': {
7775
u'url': u'http://sp.example.com',
78-
u'name': u'sp_test'
76+
u'name': u'sp_test',
77+
u'displayname': u'SP test',
7978
}
8079
}
80+
settings = OneLogin_Saml2_Settings(saml_settings)
8181

8282
authn_request = OneLogin_Saml2_Authn_Request(settings)
8383
inflated = authn_request.get_xml()
8484
self.assertRegexpMatches(inflated, '^<samlp:AuthnRequest')
85-
self.assertNotIn('ProviderName="SP test"', inflated)
85+
self.assertIn('ProviderName="SP test"', inflated)
8686

8787
saml_settings['organization'] = {}
8888
settings = OneLogin_Saml2_Settings(saml_settings)

tests/src/OneLogin/saml2_tests/logout_request_test.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,27 @@ def testIsValidSign(self):
442442
except Exception as e:
443443
self.assertIn('In order to validate the sign on the Logout Request, the x509cert of the IdP is required', e.message)
444444

445+
def testGetXML(self):
446+
"""
447+
Tests that we can get the logout request XML directly without
448+
going through intermediate steps
449+
"""
450+
request = self.file_contents(join(self.data_path, 'logout_requests', 'logout_request.xml'))
451+
settings = OneLogin_Saml2_Settings(self.loadSettingsJSON())
452+
453+
logout_request_generated = OneLogin_Saml2_Logout_Request(settings)
454+
455+
expectedFragment = (
456+
'Destination="http://idp.example.com/SingleLogoutService.php">\n'
457+
' <saml:Issuer>http://stuff.com/endpoints/metadata.php</saml:Issuer>\n'
458+
' <saml:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity" SPNameQualifier="http://stuff.com/endpoints/metadata.php">http://idp.example.com/</saml:NameID>\n'
459+
' \n </samlp:LogoutRequest>'
460+
)
461+
self.assertIn(expectedFragment, logout_request_generated.get_xml())
462+
463+
logout_request_processed = OneLogin_Saml2_Logout_Request(settings, b64encode(request))
464+
self.assertEqual(request, logout_request_processed.get_xml())
465+
445466

446467
if __name__ == '__main__':
447468
if is_running_under_teamcity():

tests/src/OneLogin/saml2_tests/logout_response_test.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,31 @@ def testIsValid(self):
385385
response_3 = OneLogin_Saml2_Logout_Response(settings, message_3)
386386
self.assertTrue(response_3.is_valid(request_data))
387387

388+
def testGetXML(self):
389+
"""
390+
Tests that we can get the logout response XML directly without
391+
going through intermediate steps
392+
"""
393+
response = self.file_contents(join(self.data_path, 'logout_responses', 'logout_response.xml'))
394+
settings = OneLogin_Saml2_Settings(self.loadSettingsJSON())
395+
396+
logout_response_generated = OneLogin_Saml2_Logout_Response(settings)
397+
logout_response_generated.build("InResponseValue")
398+
399+
expectedFragment = (
400+
'Destination="http://idp.example.com/SingleLogoutService.php"\n'
401+
' InResponseTo="InResponseValue"\n>\n'
402+
' <saml:Issuer>http://stuff.com/endpoints/metadata.php</saml:Issuer>\n'
403+
' <samlp:Status>\n'
404+
' <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" />\n'
405+
' </samlp:Status>\n'
406+
'</samlp:LogoutResponse>'
407+
)
408+
self.assertIn(expectedFragment, logout_response_generated.get_xml())
409+
410+
logout_response_processed = OneLogin_Saml2_Logout_Response(settings, OneLogin_Saml2_Utils.deflate_and_base64_encode(response))
411+
self.assertEqual(response, logout_response_processed.get_xml())
412+
388413

389414
if __name__ == '__main__':
390415
if is_running_under_teamcity():

tests/src/OneLogin/saml2_tests/response_test.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,18 +60,23 @@ def testConstruct(self):
6060

6161
self.assertIsInstance(response_enc, OneLogin_Saml2_Response)
6262

63-
def test_get_decrypted_xml(self):
63+
def test_get_xml_document(self):
6464
"""
6565
Tests that we can retrieve the raw text of an encrypted XML response
6666
without going through intermediate steps
6767
"""
6868
json_settings = self.loadSettingsJSON()
69-
7069
settings = OneLogin_Saml2_Settings(json_settings)
71-
xml = self.file_contents(join(self.data_path, 'responses', 'valid_encrypted_assertion.xml.base64'))
70+
71+
xml = self.file_contents(join(self.data_path, 'responses', 'signed_message_response.xml.base64'))
7272
response = OneLogin_Saml2_Response(settings, xml)
73+
prety_xml = self.file_contents(join(self.data_path, 'responses', 'pretty_signed_message_response.xml'))
74+
self.assertEqual(etree.tostring(response.get_xml_document(), pretty_print=True), prety_xml)
75+
76+
xml_2 = self.file_contents(join(self.data_path, 'responses', 'valid_encrypted_assertion.xml.base64'))
77+
response_2 = OneLogin_Saml2_Response(settings, xml_2)
7378
decrypted = self.file_contents(join(self.data_path, 'responses', 'decrypted_valid_encrypted_assertion.xml'))
74-
self.assertEqual(etree.tostring(response.get_xml_document()), decrypted)
79+
self.assertEqual(etree.tostring(response_2.get_xml_document()), decrypted)
7580

7681
def testReturnNameId(self):
7782
"""

0 commit comments

Comments
 (0)