From c56c0383a576e3699054de9d520fa03420eec110 Mon Sep 17 00:00:00 2001 From: Sergio Pulgarin Date: Tue, 12 May 2015 18:05:21 -0500 Subject: [PATCH 01/10] Removed hardcoded binging - Use from settings. Defaults to HTTP-Post. --- src/onelogin/saml2/authn_request.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/onelogin/saml2/authn_request.py b/src/onelogin/saml2/authn_request.py index 8e8495e7..47ff719b 100644 --- a/src/onelogin/saml2/authn_request.py +++ b/src/onelogin/saml2/authn_request.py @@ -72,6 +72,13 @@ def __init__(self, settings, force_authn=False, is_passive=False): if is_passive is True: is_passive_str = 'IsPassive="true"' + # to keep backwards compatibility, use POST binding by default + # for configurations that didn't specified one, since it + # wasn't required previously. + acs_binding = sp_data['assertionConsumerService'].get( + 'binding', OneLogin_Saml2_Constants.BINDING_HTTP_POST + ) + requested_authn_context_str = '' if 'requestedAuthnContext' in security.keys() and security['requestedAuthnContext'] is not False: if security['requestedAuthnContext'] is True: @@ -94,7 +101,7 @@ def __init__(self, settings, force_authn=False, is_passive=False): %(is_passive_str)s IssueInstant="%(issue_instant)s" Destination="%(destination)s" - ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" + ProtocolBinding="%(acs_binding)s" AssertionConsumerServiceURL="%(assertion_url)s"> %(entity_id)s Date: Tue, 12 May 2015 18:06:09 -0500 Subject: [PATCH 02/10] Validated assertionConsumerService binding parameter. --- src/onelogin/saml2/settings.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/onelogin/saml2/settings.py b/src/onelogin/saml2/settings.py index 213d078e..85198b4d 100644 --- a/src/onelogin/saml2/settings.py +++ b/src/onelogin/saml2/settings.py @@ -361,6 +361,12 @@ def check_settings(self, settings): errors.append('sp_acs_not_found') elif not validate_url(sp['assertionConsumerService']['url']): errors.append('sp_acs_url_invalid') + elif 'binding' in sp['assertionConsumerService']: + # checking that provided binding is a valid one for acs. + acs_binding = sp['assertionConsumerService']['binding'] + if acs_binding != OneLogin_Saml2_Constants.BINDING_HTTP_POST and\ + acs_binding != OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT: + errors.append('sp_acs_binding_invalid') if 'singleLogoutService' in sp and \ 'url' in sp['singleLogoutService'] and \ From 9ec85010ec800bdee3a805251b5ee25baecec3da Mon Sep 17 00:00:00 2001 From: Sergio Pulgarin Date: Tue, 12 May 2015 18:07:30 -0500 Subject: [PATCH 03/10] Implemented Redirect Response class. Using a unique staticmethod to process the incoming response depending on the binding. --- src/onelogin/saml2/response.py | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/src/onelogin/saml2/response.py b/src/onelogin/saml2/response.py index c41e3cf0..4f48e9dc 100644 --- a/src/onelogin/saml2/response.py +++ b/src/onelogin/saml2/response.py @@ -39,7 +39,7 @@ def __init__(self, settings, response): """ self.__settings = settings self.__error = None - self.response = b64decode(response) + self.response = self.__class__.decode_response(response) self.document = fromstring(self.response) self.decrypted_document = None self.encrypted = None @@ -51,6 +51,10 @@ def __init__(self, settings, response): self.encrypted = True self.decrypted_document = self.__decrypt_assertion(decrypted_document) + @staticmethod + def decode_response(self, response): + raise NotImplementedError() + def is_valid(self, request_data, request_id=None): """ Validates the response object. @@ -458,3 +462,31 @@ def get_error(self): After execute a validation process, if fails this method returns the cause """ return self.__error + + +class OneLogin_Saml2_Response_Post(OneLogin_Saml2_Response): + + @staticmethod + def decode_response(response): + """ + Decodes a HTTP-POST binding response. + + :param response: Encoded Response + :returns: Decoded Response + :rtype: String + """ + return b64decode(response) + + +class OneLogin_Saml2_Response_Redirect(OneLogin_Saml2_Response): + + @staticmethod + def decode_response(response): + """ + Decodes a HTTP-Response binding response. + + :param response: Encoded Response + :returns: Decoded Response + :rtype: String + """ + return OneLogin_Saml2_Utils.decode_base64_and_inflate(response) From 5ae5fa7e351c7f4a173ab2bb54739ddffd9e3ac1 Mon Sep 17 00:00:00 2001 From: Sergio Pulgarin Date: Tue, 12 May 2015 18:08:31 -0500 Subject: [PATCH 04/10] Implemented support for Redirect binding. Keeps backwards compatibility. --- src/onelogin/saml2/auth.py | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/onelogin/saml2/auth.py b/src/onelogin/saml2/auth.py index 914caada..9a0a72f5 100644 --- a/src/onelogin/saml2/auth.py +++ b/src/onelogin/saml2/auth.py @@ -17,7 +17,8 @@ import dm.xmlsec.binding as xmlsec from onelogin.saml2.settings import OneLogin_Saml2_Settings -from onelogin.saml2.response import OneLogin_Saml2_Response +from onelogin.saml2.response import ( + OneLogin_Saml2_Response_Post, OneLogin_Saml2_Response_Redirect) from onelogin.saml2.errors import OneLogin_Saml2_Error from onelogin.saml2.logout_response import OneLogin_Saml2_Logout_Response from onelogin.saml2.constants import OneLogin_Saml2_Constants @@ -87,9 +88,22 @@ def process_response(self, request_id=None): """ self.__errors = [] - if 'post_data' in self.__request_data and 'SAMLResponse' in self.__request_data['post_data']: - # AuthnResponse -- HTTP_POST Binding - response = OneLogin_Saml2_Response(self.__settings, self.__request_data['post_data']['SAMLResponse']) + sp_data = self.get_settings().get_sp_data() + acs_binding = sp_data['assertionConsumerService'].get( + 'binding', OneLogin_Saml2_Constants.BINDING_HTTP_POST + ) + + # finds what attribute to access given the configured binding. + if acs_binding == OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT: + response_class = OneLogin_Saml2_Response_Redirect + response_attr = 'get_data' + else: + response_class = OneLogin_Saml2_Response_Post + response_attr = 'post_data' + + if response_attr in self.__request_data and 'SAMLResponse' in self.__request_data[response_attr]: + response = response_class( + self.__settings, self.__request_data[response_attr]['SAMLResponse']) if response.is_valid(self.__request_data, request_id): self.__attributes = response.get_attributes() @@ -100,11 +114,10 @@ def process_response(self, request_id=None): else: self.__errors.append('invalid_response') self.__error_reason = response.get_error() - else: self.__errors.append('invalid_binding') raise OneLogin_Saml2_Error( - 'SAML Response not found, Only supported HTTP_POST Binding', + 'SAML Response not found for binding %s'.format(acs_binding.split(':')[-1]), OneLogin_Saml2_Error.SAML_RESPONSE_NOT_FOUND ) From b2fc6cad78489e3b91217bf0048640b154951765 Mon Sep 17 00:00:00 2001 From: Sergio Pulgarin Date: Tue, 12 May 2015 18:33:56 -0500 Subject: [PATCH 05/10] Fixed bug in method arguments --- src/onelogin/saml2/response.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/onelogin/saml2/response.py b/src/onelogin/saml2/response.py index 4f48e9dc..ef8b3a85 100644 --- a/src/onelogin/saml2/response.py +++ b/src/onelogin/saml2/response.py @@ -52,7 +52,7 @@ def __init__(self, settings, response): self.decrypted_document = self.__decrypt_assertion(decrypted_document) @staticmethod - def decode_response(self, response): + def decode_response(response): raise NotImplementedError() def is_valid(self, request_data, request_id=None): From 467bb380679373c7b12f57e395c67fbd8628dc83 Mon Sep 17 00:00:00 2001 From: Sergio Pulgarin Date: Tue, 12 May 2015 18:34:14 -0500 Subject: [PATCH 06/10] Replacing class / Using Post for tests --- .../src/OneLogin/saml2_tests/response_test.py | 329 +++++++++--------- .../saml2_tests/signed_response_test.py | 6 +- 2 files changed, 168 insertions(+), 167 deletions(-) diff --git a/tests/src/OneLogin/saml2_tests/response_test.py b/tests/src/OneLogin/saml2_tests/response_test.py index 9ed46dd8..9b3e4669 100644 --- a/tests/src/OneLogin/saml2_tests/response_test.py +++ b/tests/src/OneLogin/saml2_tests/response_test.py @@ -9,7 +9,8 @@ import unittest from xml.dom.minidom import parseString -from onelogin.saml2.response import OneLogin_Saml2_Response +from onelogin.saml2.response import ( + OneLogin_Saml2_Response_Post, OneLogin_Saml2_Response_Redirect) from onelogin.saml2.settings import OneLogin_Saml2_Settings from onelogin.saml2.utils import OneLogin_Saml2_Utils @@ -42,38 +43,38 @@ def get_request_data(self): def testConstruct(self): """ - Tests the OneLogin_Saml2_Response Constructor. + Tests the OneLogin_Saml2_Response_Post Constructor. """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) xml = self.file_contents(join(self.data_path, 'responses', 'response1.xml.base64')) - response = OneLogin_Saml2_Response(settings, xml) + response = OneLogin_Saml2_Response_Post(settings, xml) - self.assertIsInstance(response, OneLogin_Saml2_Response) + self.assertIsInstance(response, OneLogin_Saml2_Response_Post) xml_enc = self.file_contents(join(self.data_path, 'responses', 'valid_encrypted_assertion.xml.base64')) - response_enc = OneLogin_Saml2_Response(settings, xml_enc) + response_enc = OneLogin_Saml2_Response_Post(settings, xml_enc) - self.assertIsInstance(response_enc, OneLogin_Saml2_Response) + self.assertIsInstance(response_enc, OneLogin_Saml2_Response_Post) def testReturnNameId(self): """ - Tests the get_nameid method of the OneLogin_Saml2_Response + Tests the get_nameid method of the OneLogin_Saml2_Response_Post """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) xml = self.file_contents(join(self.data_path, 'responses', 'response1.xml.base64')) - response = OneLogin_Saml2_Response(settings, xml) + response = OneLogin_Saml2_Response_Post(settings, xml) self.assertEqual('support@onelogin.com', response.get_nameid()) xml_2 = self.file_contents(join(self.data_path, 'responses', 'response_encrypted_nameid.xml.base64')) - response_2 = OneLogin_Saml2_Response(settings, xml_2) + response_2 = OneLogin_Saml2_Response_Post(settings, xml_2) self.assertEqual('2de11defd199f8d5bb63f9b7deb265ba5c675c10', response_2.get_nameid()) xml_3 = self.file_contents(join(self.data_path, 'responses', 'valid_encrypted_assertion.xml.base64')) - response_3 = OneLogin_Saml2_Response(settings, xml_3) + response_3 = OneLogin_Saml2_Response_Post(settings, xml_3) self.assertEqual('_68392312d490db6d355555cfbbd8ec95d746516f60', response_3.get_nameid()) xml_4 = self.file_contents(join(self.data_path, 'responses', 'invalids', 'no_nameid.xml.base64')) - response_4 = OneLogin_Saml2_Response(settings, xml_4) + response_4 = OneLogin_Saml2_Response_Post(settings, xml_4) try: response_4.get_nameid() self.assertTrue(False) @@ -82,11 +83,11 @@ def testReturnNameId(self): def testGetNameIdData(self): """ - Tests the get_nameid_data method of the OneLogin_Saml2_Response + Tests the get_nameid_data method of the OneLogin_Saml2_Response_Post """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) xml = self.file_contents(join(self.data_path, 'responses', 'response1.xml.base64')) - response = OneLogin_Saml2_Response(settings, xml) + response = OneLogin_Saml2_Response_Post(settings, xml) expected_nameid_data = { 'Value': 'support@onelogin.com', 'Format': 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress' @@ -95,7 +96,7 @@ def testGetNameIdData(self): self.assertEqual(expected_nameid_data, nameid_data) xml_2 = self.file_contents(join(self.data_path, 'responses', 'response_encrypted_nameid.xml.base64')) - response_2 = OneLogin_Saml2_Response(settings, xml_2) + response_2 = OneLogin_Saml2_Response_Post(settings, xml_2) expected_nameid_data_2 = { 'Value': '2de11defd199f8d5bb63f9b7deb265ba5c675c10', 'Format': 'urn:oasis:names:tc:SAML:2.0:nameid-format:unspecified', @@ -105,7 +106,7 @@ def testGetNameIdData(self): self.assertEqual(expected_nameid_data_2, nameid_data_2) xml_3 = self.file_contents(join(self.data_path, 'responses', 'valid_encrypted_assertion.xml.base64')) - response_3 = OneLogin_Saml2_Response(settings, xml_3) + response_3 = OneLogin_Saml2_Response_Post(settings, xml_3) expected_nameid_data_3 = { 'Value': '_68392312d490db6d355555cfbbd8ec95d746516f60', 'Format': 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient', @@ -115,7 +116,7 @@ def testGetNameIdData(self): self.assertEqual(expected_nameid_data_3, nameid_data_3) xml_4 = self.file_contents(join(self.data_path, 'responses', 'invalids', 'no_nameid.xml.base64')) - response_4 = OneLogin_Saml2_Response(settings, xml_4) + response_4 = OneLogin_Saml2_Response_Post(settings, xml_4) try: response_4.get_nameid_data() self.assertTrue(False) @@ -124,19 +125,19 @@ def testGetNameIdData(self): def testCheckStatus(self): """ - Tests the check_status method of the OneLogin_Saml2_Response + Tests the check_status method of the OneLogin_Saml2_Response_Post """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) xml = self.file_contents(join(self.data_path, 'responses', 'response1.xml.base64')) - response = OneLogin_Saml2_Response(settings, xml) + response = OneLogin_Saml2_Response_Post(settings, xml) response.check_status() xml_enc = self.file_contents(join(self.data_path, 'responses', 'valid_encrypted_assertion.xml.base64')) - response_enc = OneLogin_Saml2_Response(settings, xml_enc) + response_enc = OneLogin_Saml2_Response_Post(settings, xml_enc) response_enc.check_status() xml_2 = self.file_contents(join(self.data_path, 'responses', 'invalids', 'status_code_responder.xml.base64')) - response_2 = OneLogin_Saml2_Response(settings, xml_2) + response_2 = OneLogin_Saml2_Response_Post(settings, xml_2) try: response_2.check_status() 1 / 0 @@ -144,7 +145,7 @@ def testCheckStatus(self): self.assertIn('The status code of the Response was not Success, was Responder', e.message) xml_3 = self.file_contents(join(self.data_path, 'responses', 'invalids', 'status_code_responer_and_msg.xml.base64')) - response_3 = OneLogin_Saml2_Response(settings, xml_3) + response_3 = OneLogin_Saml2_Response_Post(settings, xml_3) try: response_3.check_status() 1 / 0 @@ -153,92 +154,92 @@ def testCheckStatus(self): def testGetAudiences(self): """ - Tests the get_audiences method of the OneLogin_Saml2_Response + Tests the get_audiences method of the OneLogin_Saml2_Response_Post """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) xml = self.file_contents(join(self.data_path, 'responses', 'no_audience.xml.base64')) - response = OneLogin_Saml2_Response(settings, xml) + response = OneLogin_Saml2_Response_Post(settings, xml) self.assertEqual([], response.get_audiences()) xml_2 = self.file_contents(join(self.data_path, 'responses', 'response1.xml.base64')) - response_2 = OneLogin_Saml2_Response(settings, xml_2) + response_2 = OneLogin_Saml2_Response_Post(settings, xml_2) self.assertEqual(['{audience}'], response_2.get_audiences()) xml_3 = self.file_contents(join(self.data_path, 'responses', 'valid_encrypted_assertion.xml.base64')) - response_3 = OneLogin_Saml2_Response(settings, xml_3) + response_3 = OneLogin_Saml2_Response_Post(settings, xml_3) self.assertEqual(['http://stuff.com/endpoints/metadata.php'], response_3.get_audiences()) def testQueryAssertions(self): """ Tests the __query_assertion and __query methods of the - OneLogin_Saml2_Response using the get_issuers call + OneLogin_Saml2_Response_Post using the get_issuers call """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) xml = self.file_contents(join(self.data_path, 'responses', 'response1.xml.base64')) - response = OneLogin_Saml2_Response(settings, xml) + response = OneLogin_Saml2_Response_Post(settings, xml) self.assertEqual(['https://app.onelogin.com/saml/metadata/13590'], response.get_issuers()) xml_2 = self.file_contents(join(self.data_path, 'responses', 'valid_encrypted_assertion.xml.base64')) - response_2 = OneLogin_Saml2_Response(settings, xml_2) + response_2 = OneLogin_Saml2_Response_Post(settings, xml_2) self.assertEqual(['http://idp.example.com/'], response_2.get_issuers()) xml_3 = self.file_contents(join(self.data_path, 'responses', 'double_signed_encrypted_assertion.xml.base64')) - response_3 = OneLogin_Saml2_Response(settings, xml_3) + response_3 = OneLogin_Saml2_Response_Post(settings, xml_3) self.assertEqual(['http://idp.example.com/', 'https://pitbulk.no-ip.org/simplesaml/saml2/idp/metadata.php'], response_3.get_issuers()) xml_4 = self.file_contents(join(self.data_path, 'responses', 'double_signed_response.xml.base64')) - response_4 = OneLogin_Saml2_Response(settings, xml_4) + response_4 = OneLogin_Saml2_Response_Post(settings, xml_4) self.assertEqual(['https://pitbulk.no-ip.org/simplesaml/saml2/idp/metadata.php'], response_4.get_issuers()) xml_5 = self.file_contents(join(self.data_path, 'responses', 'signed_message_encrypted_assertion.xml.base64')) - response_5 = OneLogin_Saml2_Response(settings, xml_5) + response_5 = OneLogin_Saml2_Response_Post(settings, xml_5) self.assertEqual(['http://idp.example.com/', 'https://pitbulk.no-ip.org/simplesaml/saml2/idp/metadata.php'], response_5.get_issuers()) xml_6 = self.file_contents(join(self.data_path, 'responses', 'signed_assertion_response.xml.base64')) - response_6 = OneLogin_Saml2_Response(settings, xml_6) + response_6 = OneLogin_Saml2_Response_Post(settings, xml_6) self.assertEqual(['https://pitbulk.no-ip.org/simplesaml/saml2/idp/metadata.php'], response_6.get_issuers()) xml_7 = self.file_contents(join(self.data_path, 'responses', 'signed_encrypted_assertion.xml.base64')) - response_7 = OneLogin_Saml2_Response(settings, xml_7) + response_7 = OneLogin_Saml2_Response_Post(settings, xml_7) self.assertEqual(['http://idp.example.com/'], response_7.get_issuers()) def testGetIssuers(self): """ - Tests the get_issuers method of the OneLogin_Saml2_Response + Tests the get_issuers method of the OneLogin_Saml2_Response_Post """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) xml = self.file_contents(join(self.data_path, 'responses', 'response1.xml.base64')) - response = OneLogin_Saml2_Response(settings, xml) + response = OneLogin_Saml2_Response_Post(settings, xml) self.assertEqual(['https://app.onelogin.com/saml/metadata/13590'], response.get_issuers()) xml_2 = self.file_contents(join(self.data_path, 'responses', 'valid_encrypted_assertion.xml.base64')) - response_2 = OneLogin_Saml2_Response(settings, xml_2) + response_2 = OneLogin_Saml2_Response_Post(settings, xml_2) self.assertEqual(['http://idp.example.com/'], response_2.get_issuers()) xml_3 = self.file_contents(join(self.data_path, 'responses', 'double_signed_encrypted_assertion.xml.base64')) - response_3 = OneLogin_Saml2_Response(settings, xml_3) + response_3 = OneLogin_Saml2_Response_Post(settings, xml_3) self.assertEqual(['http://idp.example.com/', 'https://pitbulk.no-ip.org/simplesaml/saml2/idp/metadata.php'], response_3.get_issuers()) def testGetSessionIndex(self): """ - Tests the get_session_index method of the OneLogin_Saml2_Response + Tests the get_session_index method of the OneLogin_Saml2_Response_Post """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) xml = self.file_contents(join(self.data_path, 'responses', 'response1.xml.base64')) - response = OneLogin_Saml2_Response(settings, xml) + response = OneLogin_Saml2_Response_Post(settings, xml) self.assertEqual('_531c32d283bdff7e04e487bcdbc4dd8d', response.get_session_index()) xml_2 = self.file_contents(join(self.data_path, 'responses', 'valid_encrypted_assertion.xml.base64')) - response_2 = OneLogin_Saml2_Response(settings, xml_2) + response_2 = OneLogin_Saml2_Response_Post(settings, xml_2) self.assertEqual('_7164a9a9f97828bfdb8d0ebc004a05d2e7d873f70c', response_2.get_session_index()) def testGetAttributes(self): """ - Tests the getAttributes method of the OneLogin_Saml2_Response + Tests the getAttributes method of the OneLogin_Saml2_Response_Post """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) xml = self.file_contents(join(self.data_path, 'responses', 'response1.xml.base64')) - response = OneLogin_Saml2_Response(settings, xml) + response = OneLogin_Saml2_Response_Post(settings, xml) expected_attributes = { 'uid': ['demo'], 'another_value': ['value'] @@ -248,22 +249,22 @@ def testGetAttributes(self): # An assertion that has no attributes should return an empty # array when asked for the attributes xml_2 = self.file_contents(join(self.data_path, 'responses', 'response2.xml.base64')) - response_2 = OneLogin_Saml2_Response(settings, xml_2) + response_2 = OneLogin_Saml2_Response_Post(settings, xml_2) self.assertEqual({}, response_2.get_attributes()) # Encrypted Attributes are not supported xml_3 = self.file_contents(join(self.data_path, 'responses', 'invalids', 'encrypted_attrs.xml.base64')) - response_3 = OneLogin_Saml2_Response(settings, xml_3) + response_3 = OneLogin_Saml2_Response_Post(settings, xml_3) self.assertEqual({}, response_3.get_attributes()) def testOnlyRetrieveAssertionWithIDThatMatchesSignatureReference(self): """ - Tests the get_nameid method of the OneLogin_Saml2_Response + Tests the get_nameid method of the OneLogin_Saml2_Response_Post The Assertion is unsigned so the method fails """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) xml = self.file_contents(join(self.data_path, 'responses', 'wrapped_response_2.xml.base64')) - response = OneLogin_Saml2_Response(settings, xml) + response = OneLogin_Saml2_Response_Post(settings, xml) try: self.assertTrue(response.is_valid(self.get_request_data())) nameid = response.get_nameid() @@ -273,36 +274,36 @@ def testOnlyRetrieveAssertionWithIDThatMatchesSignatureReference(self): def testDoesNotAllowSignatureWrappingAttack(self): """ - Tests the get_nameid method of the OneLogin_Saml2_Response + Tests the get_nameid method of the OneLogin_Saml2_Response_Post Test that the SignatureWrappingAttack is not allowed """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) xml = self.file_contents(join(self.data_path, 'responses', 'response4.xml.base64')) - response = OneLogin_Saml2_Response(settings, xml) + response = OneLogin_Saml2_Response_Post(settings, xml) self.assertEqual('test@onelogin.com', response.get_nameid()) self.assertFalse(response.is_valid(self.get_request_data())) def testGetSessionNotOnOrAfter(self): """ - Tests the get_session_not_on_or_after method of the OneLogin_Saml2_Response + Tests the get_session_not_on_or_after method of the OneLogin_Saml2_Response_Post """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) xml = self.file_contents(join(self.data_path, 'responses', 'response1.xml.base64')) - response = OneLogin_Saml2_Response(settings, xml) + response = OneLogin_Saml2_Response_Post(settings, xml) self.assertEqual(1290203857, response.get_session_not_on_or_after()) # An assertion that do not specified Session timeout should return NULL xml_2 = self.file_contents(join(self.data_path, 'responses', 'response2.xml.base64')) - response_2 = OneLogin_Saml2_Response(settings, xml_2) + response_2 = OneLogin_Saml2_Response_Post(settings, xml_2) self.assertEqual(None, response_2.get_session_not_on_or_after()) xml_3 = self.file_contents(join(self.data_path, 'responses', 'valid_encrypted_assertion.xml.base64')) - response_3 = OneLogin_Saml2_Response(settings, xml_3) + response_3 = OneLogin_Saml2_Response_Post(settings, xml_3) self.assertEqual(2696012228, response_3.get_session_not_on_or_after()) def testIsInvalidXML(self): """ - Tests the is_valid method of the OneLogin_Saml2_Response + Tests the is_valid method of the OneLogin_Saml2_Response_Post Case Invalid XML """ message = b64encode('invalid') @@ -313,61 +314,61 @@ def testIsInvalidXML(self): } settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) - response = OneLogin_Saml2_Response(settings, message) + response = OneLogin_Saml2_Response_Post(settings, message) response.is_valid(request_data) self.assertEqual('No Signature found. SAML Response rejected', response.get_error()) settings.set_strict(True) - response_2 = OneLogin_Saml2_Response(settings, message) + response_2 = OneLogin_Saml2_Response_Post(settings, message) self.assertFalse(response_2.is_valid(request_data)) self.assertEqual('Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd', response_2.get_error()) def testValidateNumAssertions(self): """ - Tests the validate_num_assertions method of the OneLogin_Saml2_Response + Tests the validate_num_assertions method of the OneLogin_Saml2_Response_Post """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) xml = self.file_contents(join(self.data_path, 'responses', 'response1.xml.base64')) - response = OneLogin_Saml2_Response(settings, xml) + response = OneLogin_Saml2_Response_Post(settings, xml) self.assertTrue(response.validate_num_assertions()) xml_multi_assertion = self.file_contents(join(self.data_path, 'responses', 'invalids', 'multiple_assertions.xml.base64')) - response_2 = OneLogin_Saml2_Response(settings, xml_multi_assertion) + response_2 = OneLogin_Saml2_Response_Post(settings, xml_multi_assertion) self.assertFalse(response_2.validate_num_assertions()) def testValidateTimestamps(self): """ - Tests the validate_timestamps method of the OneLogin_Saml2_Response + Tests the validate_timestamps method of the OneLogin_Saml2_Response_Post """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) xml = self.file_contents(join(self.data_path, 'responses', 'valid_response.xml.base64')) - response = OneLogin_Saml2_Response(settings, xml) + response = OneLogin_Saml2_Response_Post(settings, xml) self.assertTrue(response.validate_timestamps()) xml_2 = self.file_contents(join(self.data_path, 'responses', 'valid_encrypted_assertion.xml.base64')) - response_2 = OneLogin_Saml2_Response(settings, xml_2) + response_2 = OneLogin_Saml2_Response_Post(settings, xml_2) self.assertTrue(response_2.validate_timestamps()) xml_3 = self.file_contents(join(self.data_path, 'responses', 'expired_response.xml.base64')) - response_3 = OneLogin_Saml2_Response(settings, xml_3) + response_3 = OneLogin_Saml2_Response_Post(settings, xml_3) self.assertFalse(response_3.validate_timestamps()) xml_4 = self.file_contents(join(self.data_path, 'responses', 'invalids', 'not_after_failed.xml.base64')) - response_4 = OneLogin_Saml2_Response(settings, xml_4) + response_4 = OneLogin_Saml2_Response_Post(settings, xml_4) self.assertFalse(response_4.validate_timestamps()) xml_5 = self.file_contents(join(self.data_path, 'responses', 'invalids', 'not_before_failed.xml.base64')) - response_5 = OneLogin_Saml2_Response(settings, xml_5) + response_5 = OneLogin_Saml2_Response_Post(settings, xml_5) self.assertFalse(response_5.validate_timestamps()) def testValidateVersion(self): """ - Tests the is_valid method of the OneLogin_Saml2_Response + Tests the is_valid method of the OneLogin_Saml2_Response_Post Case invalid version """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) xml = self.file_contents(join(self.data_path, 'responses', 'invalids', 'no_saml2.xml.base64')) - response = OneLogin_Saml2_Response(settings, xml) + response = OneLogin_Saml2_Response_Post(settings, xml) try: valid = response.is_valid(self.get_request_data()) self.assertFalse(valid) @@ -376,12 +377,12 @@ def testValidateVersion(self): def testValidateID(self): """ - Tests the is_valid method of the OneLogin_Saml2_Response + Tests the is_valid method of the OneLogin_Saml2_Response_Post Case invalid no ID """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) xml = self.file_contents(join(self.data_path, 'responses', 'invalids', 'no_id.xml.base64')) - response = OneLogin_Saml2_Response(settings, xml) + response = OneLogin_Saml2_Response_Post(settings, xml) try: valid = response.is_valid(self.get_request_data()) self.assertFalse(valid) @@ -390,12 +391,12 @@ def testValidateID(self): def testIsInValidReference(self): """ - Tests the is_valid method of the OneLogin_Saml2_Response + Tests the is_valid method of the OneLogin_Saml2_Response_Post Case invalid reference """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) xml = self.file_contents(join(self.data_path, 'responses', 'response1.xml.base64')) - response = OneLogin_Saml2_Response(settings, xml) + response = OneLogin_Saml2_Response_Post(settings, xml) try: valid = response.is_valid(self.get_request_data()) self.assertFalse(valid) @@ -404,17 +405,17 @@ def testIsInValidReference(self): def testIsInValidExpired(self): """ - Tests the is_valid method of the OneLogin_Saml2_Response + Tests the is_valid method of the OneLogin_Saml2_Response_Post Case expired response """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) xml = self.file_contents(join(self.data_path, 'responses', 'expired_response.xml.base64')) - response = OneLogin_Saml2_Response(settings, xml) + response = OneLogin_Saml2_Response_Post(settings, xml) response.is_valid(self.get_request_data()) self.assertEqual('No Signature found. SAML Response rejected', response.get_error()) settings.set_strict(True) - response_2 = OneLogin_Saml2_Response(settings, xml) + response_2 = OneLogin_Saml2_Response_Post(settings, xml) try: valid = response_2.is_valid(self.get_request_data()) self.assertFalse(valid) @@ -423,17 +424,17 @@ def testIsInValidExpired(self): def testIsInValidNoStatement(self): """ - Tests the is_valid method of the OneLogin_Saml2_Response + Tests the is_valid method of the OneLogin_Saml2_Response_Post Case no statement """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) xml = self.file_contents(join(self.data_path, 'responses', 'invalids', 'no_signature.xml.base64')) - response = OneLogin_Saml2_Response(settings, xml) + response = OneLogin_Saml2_Response_Post(settings, xml) response.is_valid(self.get_request_data()) self.assertEqual('No Signature found. SAML Response rejected', response.get_error()) settings.set_strict(True) - response_2 = OneLogin_Saml2_Response(settings, xml) + response_2 = OneLogin_Saml2_Response_Post(settings, xml) try: valid = response_2.is_valid(self.get_request_data()) self.assertFalse(valid) @@ -442,12 +443,12 @@ def testIsInValidNoStatement(self): def testIsInValidNoKey(self): """ - Tests the is_valid method of the OneLogin_Saml2_Response + Tests the is_valid method of the OneLogin_Saml2_Response_Post Case no key """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) xml = self.file_contents(join(self.data_path, 'responses', 'invalids', 'no_key.xml.base64')) - response = OneLogin_Saml2_Response(settings, xml) + response = OneLogin_Saml2_Response_Post(settings, xml) try: valid = response.is_valid(self.get_request_data()) self.assertFalse(valid) @@ -456,13 +457,13 @@ def testIsInValidNoKey(self): def testIsInValidMultipleAssertions(self): """ - Tests the is_valid method of the OneLogin_Saml2_Response + Tests the is_valid method of the OneLogin_Saml2_Response_Post Case invalid multiple assertions """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) xml = self.file_contents(join(self.data_path, 'responses', 'invalids', 'multiple_assertions.xml.base64')) - response = OneLogin_Saml2_Response(settings, xml) + response = OneLogin_Saml2_Response_Post(settings, xml) try: valid = response.is_valid(self.get_request_data()) self.assertFalse(valid) @@ -471,17 +472,17 @@ def testIsInValidMultipleAssertions(self): def testIsInValidEncAttrs(self): """ - Tests the is_valid method of the OneLogin_Saml2_Response + Tests the is_valid method of the OneLogin_Saml2_Response_Post Case invalid Encrypted Attrs """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) xml = self.file_contents(join(self.data_path, 'responses', 'invalids', 'encrypted_attrs.xml.base64')) - response = OneLogin_Saml2_Response(settings, xml) + response = OneLogin_Saml2_Response_Post(settings, xml) response.is_valid(self.get_request_data()) self.assertEqual('No Signature found. SAML Response rejected', response.get_error()) settings.set_strict(True) - response_2 = OneLogin_Saml2_Response(settings, xml) + response_2 = OneLogin_Saml2_Response_Post(settings, xml) try: valid = response_2.is_valid(self.get_request_data()) self.assertFalse(valid) @@ -490,36 +491,36 @@ def testIsInValidEncAttrs(self): def testIsInValidDestination(self): """ - Tests the is_valid method of the OneLogin_Saml2_Response class + Tests the is_valid method of the OneLogin_Saml2_Response_Post class Case Invalid Response, Invalid Destination """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) message = self.file_contents(join(self.data_path, 'responses', 'unsigned_response.xml.base64')) - response = OneLogin_Saml2_Response(settings, message) + response = OneLogin_Saml2_Response_Post(settings, message) response.is_valid(self.get_request_data()) self.assertEqual('No Signature found. SAML Response rejected', response.get_error()) settings.set_strict(True) - response_2 = OneLogin_Saml2_Response(settings, message) + response_2 = OneLogin_Saml2_Response_Post(settings, message) self.assertFalse(response_2.is_valid(self.get_request_data())) self.assertIn('The response was received at', response_2.get_error()) dom = parseString(b64decode(message)) dom.firstChild.setAttribute('Destination', '') message_2 = b64encode(dom.toxml()) - response_3 = OneLogin_Saml2_Response(settings, message_2) + response_3 = OneLogin_Saml2_Response_Post(settings, message_2) self.assertFalse(response_3.is_valid(self.get_request_data())) self.assertIn('A valid SubjectConfirmation was not found on this Response', response_3.get_error()) dom.firstChild.removeAttribute('Destination') message_3 = b64encode(dom.toxml()) - response_4 = OneLogin_Saml2_Response(settings, message_3) + response_4 = OneLogin_Saml2_Response_Post(settings, message_3) self.assertFalse(response_4.is_valid(self.get_request_data())) self.assertIn('A valid SubjectConfirmation was not found on this Response', response_4.get_error()) def testIsInValidAudience(self): """ - Tests the is_valid method of the OneLogin_Saml2_Response class + Tests the is_valid method of the OneLogin_Saml2_Response_Post class Case Invalid Response, Invalid Audience """ request_data = { @@ -529,19 +530,19 @@ def testIsInValidAudience(self): settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) message = self.file_contents(join(self.data_path, 'responses', 'invalids', 'invalid_audience.xml.base64')) - response = OneLogin_Saml2_Response(settings, message) + response = OneLogin_Saml2_Response_Post(settings, message) response.is_valid(request_data) self.assertEqual('No Signature found. SAML Response rejected', response.get_error()) settings.set_strict(True) - response_2 = OneLogin_Saml2_Response(settings, message) + response_2 = OneLogin_Saml2_Response_Post(settings, message) self.assertFalse(response_2.is_valid(request_data)) self.assertIn('is not a valid audience for this Response', response_2.get_error()) def testIsInValidIssuer(self): """ - Tests the is_valid method of the OneLogin_Saml2_Response class + Tests the is_valid method of the OneLogin_Saml2_Response_Post class Case Invalid Response, Invalid Issuer """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) @@ -560,23 +561,23 @@ def testIsInValidIssuer(self): plain_message_2 = plain_message_2.replace('http://stuff.com/endpoints/endpoints/acs.php', current_url) message_2 = b64encode(plain_message_2) - response = OneLogin_Saml2_Response(settings, message) + response = OneLogin_Saml2_Response_Post(settings, message) response.is_valid(request_data) self.assertEqual('No Signature found. SAML Response rejected', response.get_error()) - response_2 = OneLogin_Saml2_Response(settings, message_2) + response_2 = OneLogin_Saml2_Response_Post(settings, message_2) response_2.is_valid(request_data) self.assertEqual('No Signature found. SAML Response rejected', response_2.get_error()) settings.set_strict(True) - response_3 = OneLogin_Saml2_Response(settings, message) + response_3 = OneLogin_Saml2_Response_Post(settings, message) try: valid = response_3.is_valid(request_data) self.assertFalse(valid) except Exception as e: self.assertEqual('is not a valid audience for this Response', e.message) - response_4 = OneLogin_Saml2_Response(settings, message_2) + response_4 = OneLogin_Saml2_Response_Post(settings, message_2) try: valid = response_4.is_valid(request_data) self.assertFalse(valid) @@ -585,7 +586,7 @@ def testIsInValidIssuer(self): def testIsInValidSessionIndex(self): """ - Tests the is_valid method of the OneLogin_Saml2_Response class + Tests the is_valid method of the OneLogin_Saml2_Response_Post class Case Invalid Response, Invalid SessionIndex """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) @@ -599,12 +600,12 @@ def testIsInValidSessionIndex(self): plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/acs.php', current_url) message = b64encode(plain_message) - response = OneLogin_Saml2_Response(settings, message) + response = OneLogin_Saml2_Response_Post(settings, message) response.is_valid(request_data) self.assertEqual('No Signature found. SAML Response rejected', response.get_error()) settings.set_strict(True) - response_2 = OneLogin_Saml2_Response(settings, message) + response_2 = OneLogin_Saml2_Response_Post(settings, message) try: valid = response_2.is_valid(request_data) self.assertFalse(valid) @@ -613,7 +614,7 @@ def testIsInValidSessionIndex(self): def testDatetimeWithMiliseconds(self): """ - Tests the is_valid method of the OneLogin_Saml2_Response class + Tests the is_valid method of the OneLogin_Saml2_Response_Post class Somtimes IdPs uses datetimes with miliseconds, this test is to verify that the toolkit supports them """ @@ -628,13 +629,13 @@ def testDatetimeWithMiliseconds(self): plain_message = b64decode(xml) plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/acs.php', current_url) message = b64encode(plain_message) - response = OneLogin_Saml2_Response(settings, message) + response = OneLogin_Saml2_Response_Post(settings, message) response.is_valid(request_data) self.assertEqual('No Signature found. SAML Response rejected', response.get_error()) def testIsInValidSubjectConfirmation(self): """ - Tests the is_valid method of the OneLogin_Saml2_Response class + Tests the is_valid method of the OneLogin_Saml2_Response_Post class Case Invalid Response, Invalid SubjectConfirmation """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) @@ -673,62 +674,62 @@ def testIsInValidSubjectConfirmation(self): plain_message_6 = plain_message_6.replace('http://stuff.com/endpoints/endpoints/acs.php', current_url) message_6 = b64encode(plain_message_6) - response = OneLogin_Saml2_Response(settings, message) + response = OneLogin_Saml2_Response_Post(settings, message) response.is_valid(request_data) self.assertEqual('No Signature found. SAML Response rejected', response.get_error()) - response_2 = OneLogin_Saml2_Response(settings, message_2) + response_2 = OneLogin_Saml2_Response_Post(settings, message_2) response_2.is_valid(request_data) self.assertEqual('No Signature found. SAML Response rejected', response_2.get_error()) - response_3 = OneLogin_Saml2_Response(settings, message_3) + response_3 = OneLogin_Saml2_Response_Post(settings, message_3) response_3.is_valid(request_data) self.assertEqual('No Signature found. SAML Response rejected', response_3.get_error()) - response_4 = OneLogin_Saml2_Response(settings, message_4) + response_4 = OneLogin_Saml2_Response_Post(settings, message_4) response_4.is_valid(request_data) self.assertEqual('No Signature found. SAML Response rejected', response_4.get_error()) - response_5 = OneLogin_Saml2_Response(settings, message_5) + response_5 = OneLogin_Saml2_Response_Post(settings, message_5) response_5.is_valid(request_data) self.assertEqual('No Signature found. SAML Response rejected', response_5.get_error()) - response_6 = OneLogin_Saml2_Response(settings, message_6) + response_6 = OneLogin_Saml2_Response_Post(settings, message_6) response_6.is_valid(request_data) self.assertEqual('No Signature found. SAML Response rejected', response_6.get_error()) settings.set_strict(True) - response = OneLogin_Saml2_Response(settings, message) + response = OneLogin_Saml2_Response_Post(settings, message) try: self.assertFalse(response.is_valid(request_data)) except Exception as e: self.assertEqual('A valid SubjectConfirmation was not found on this Response', e.message) - response_2 = OneLogin_Saml2_Response(settings, message_2) + response_2 = OneLogin_Saml2_Response_Post(settings, message_2) try: self.assertFalse(response_2.is_valid(request_data)) except Exception as e: self.assertEqual('A valid SubjectConfirmation was not found on this Response', e.message) - response_3 = OneLogin_Saml2_Response(settings, message_3) + response_3 = OneLogin_Saml2_Response_Post(settings, message_3) try: self.assertFalse(response_3.is_valid(request_data)) except Exception as e: self.assertEqual('A valid SubjectConfirmation was not found on this Response', e.message) - response_4 = OneLogin_Saml2_Response(settings, message_4) + response_4 = OneLogin_Saml2_Response_Post(settings, message_4) try: self.assertFalse(response_4.is_valid(request_data)) except Exception as e: self.assertEqual('A valid SubjectConfirmation was not found on this Response', e.message) - response_5 = OneLogin_Saml2_Response(settings, message_5) + response_5 = OneLogin_Saml2_Response_Post(settings, message_5) try: self.assertFalse(response_5.is_valid(request_data)) except Exception as e: self.assertEqual('A valid SubjectConfirmation was not found on this Response', e.message) - response_6 = OneLogin_Saml2_Response(settings, message_6) + response_6 = OneLogin_Saml2_Response_Post(settings, message_6) try: self.assertFalse(response_6.is_valid(request_data)) except Exception as e: @@ -736,7 +737,7 @@ def testIsInValidSubjectConfirmation(self): def testIsInValidRequestId(self): """ - Tests the is_valid method of the OneLogin_Saml2_Response class + Tests the is_valid method of the OneLogin_Saml2_Response_Post class Case Invalid Response, Invalid requestID """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) @@ -750,13 +751,13 @@ def testIsInValidRequestId(self): plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/acs.php', current_url) message = b64encode(plain_message) - response = OneLogin_Saml2_Response(settings, message) + response = OneLogin_Saml2_Response_Post(settings, message) request_id = 'invalid' response.is_valid(request_data, request_id) self.assertEqual('No Signature found. SAML Response rejected', response.get_error()) settings.set_strict(True) - response = OneLogin_Saml2_Response(settings, message) + response = OneLogin_Saml2_Response_Post(settings, message) try: self.assertFalse(response.is_valid(request_data, request_id)) except Exception as e: @@ -768,7 +769,7 @@ def testIsInValidRequestId(self): def testIsInValidSignIssues(self): """ - Tests the is_valid method of the OneLogin_Saml2_Response class + Tests the is_valid method of the OneLogin_Saml2_Response_Post class Case Invalid Response, Invalid signing issues """ settings_info = self.loadSettingsJSON() @@ -784,26 +785,26 @@ def testIsInValidSignIssues(self): settings_info['security']['wantAssertionsSigned'] = False settings = OneLogin_Saml2_Settings(settings_info) - response = OneLogin_Saml2_Response(settings, message) + response = OneLogin_Saml2_Response_Post(settings, message) response.is_valid(request_data) self.assertEqual('No Signature found. SAML Response rejected', response.get_error()) settings_info['security']['wantAssertionsSigned'] = True settings_2 = OneLogin_Saml2_Settings(settings_info) - response_2 = OneLogin_Saml2_Response(settings_2, message) + response_2 = OneLogin_Saml2_Response_Post(settings_2, message) response_2.is_valid(request_data) self.assertEqual('No Signature found. SAML Response rejected', response_2.get_error()) settings_info['strict'] = True settings_info['security']['wantAssertionsSigned'] = False settings_3 = OneLogin_Saml2_Settings(settings_info) - response_3 = OneLogin_Saml2_Response(settings_3, message) + response_3 = OneLogin_Saml2_Response_Post(settings_3, message) response_3.is_valid(request_data) self.assertEqual('No Signature found. SAML Response rejected', response_3.get_error()) settings_info['security']['wantAssertionsSigned'] = True settings_4 = OneLogin_Saml2_Settings(settings_info) - response_4 = OneLogin_Saml2_Response(settings_4, message) + response_4 = OneLogin_Saml2_Response_Post(settings_4, message) try: self.assertFalse(response_4.is_valid(request_data)) except Exception as e: @@ -814,26 +815,26 @@ def testIsInValidSignIssues(self): settings_info['security']['wantMessagesSigned'] = False settings_5 = OneLogin_Saml2_Settings(settings_info) - response_5 = OneLogin_Saml2_Response(settings_5, message) + response_5 = OneLogin_Saml2_Response_Post(settings_5, message) response_5.is_valid(request_data) self.assertEqual('No Signature found. SAML Response rejected', response_5.get_error()) settings_info['security']['wantMessagesSigned'] = True settings_6 = OneLogin_Saml2_Settings(settings_info) - response_6 = OneLogin_Saml2_Response(settings_6, message) + response_6 = OneLogin_Saml2_Response_Post(settings_6, message) response_6.is_valid(request_data) self.assertEqual('No Signature found. SAML Response rejected', response_6.get_error()) settings_info['strict'] = True settings_info['security']['wantMessagesSigned'] = False settings_7 = OneLogin_Saml2_Settings(settings_info) - response_7 = OneLogin_Saml2_Response(settings_7, message) + response_7 = OneLogin_Saml2_Response_Post(settings_7, message) response_7.is_valid(request_data) self.assertEqual('No Signature found. SAML Response rejected', response_7.get_error()) settings_info['security']['wantMessagesSigned'] = True settings_8 = OneLogin_Saml2_Settings(settings_info) - response_8 = OneLogin_Saml2_Response(settings_8, message) + response_8 = OneLogin_Saml2_Response_Post(settings_8, message) try: self.assertFalse(response_8.is_valid(request_data)) except Exception as e: @@ -841,7 +842,7 @@ def testIsInValidSignIssues(self): def testIsInValidEncIssues(self): """ - Tests the is_valid method of the OneLogin_Saml2_Response class + Tests the is_valid method of the OneLogin_Saml2_Response_Post class Case Invalid Response, Invalid encryptation issues """ settings_info = self.loadSettingsJSON() @@ -857,20 +858,20 @@ def testIsInValidEncIssues(self): settings_info['security']['wantAssertionsEncrypted'] = True settings = OneLogin_Saml2_Settings(settings_info) - response = OneLogin_Saml2_Response(settings, message) + response = OneLogin_Saml2_Response_Post(settings, message) response.is_valid(request_data) self.assertEqual('No Signature found. SAML Response rejected', response.get_error()) settings_info['strict'] = True settings_info['security']['wantAssertionsEncrypted'] = False settings = OneLogin_Saml2_Settings(settings_info) - response_2 = OneLogin_Saml2_Response(settings, message) + response_2 = OneLogin_Saml2_Response_Post(settings, message) response_2.is_valid(request_data) self.assertEqual('No Signature found. SAML Response rejected', response_2.get_error()) settings_info['security']['wantAssertionsEncrypted'] = True settings = OneLogin_Saml2_Settings(settings_info) - response_3 = OneLogin_Saml2_Response(settings, message) + response_3 = OneLogin_Saml2_Response_Post(settings, message) self.assertFalse(response_3.is_valid(request_data)) self.assertEqual('The assertion of the Response is not encrypted and the SP require it', response_3.get_error()) @@ -879,13 +880,13 @@ def testIsInValidEncIssues(self): settings_info['security']['wantNameIdEncrypted'] = True settings_info['strict'] = False settings = OneLogin_Saml2_Settings(settings_info) - response_4 = OneLogin_Saml2_Response(settings, message) + response_4 = OneLogin_Saml2_Response_Post(settings, message) response_4.is_valid(request_data) self.assertEqual('No Signature found. SAML Response rejected', response_4.get_error()) settings_info['strict'] = True settings = OneLogin_Saml2_Settings(settings_info) - response_5 = OneLogin_Saml2_Response(settings, message) + response_5 = OneLogin_Saml2_Response_Post(settings, message) self.assertFalse(response_5.is_valid(request_data)) self.assertEqual('The NameID of the Response is not encrypted and the SP require it', response_5.get_error()) @@ -903,20 +904,20 @@ def testIsInValidEncIssues(self): } message_2 = self.file_contents(join(self.data_path, 'responses', 'valid_encrypted_assertion_encrypted_nameid.xml.base64')) - response_6 = OneLogin_Saml2_Response(settings_2, message_2) + response_6 = OneLogin_Saml2_Response_Post(settings_2, message_2) self.assertFalse(response_6.is_valid(request_data)) self.assertEqual('The attributes have expired, based on the SessionNotOnOrAfter of the AttributeStatement of this Response', response_6.get_error()) def testIsInValidCert(self): """ - Tests the is_valid method of the OneLogin_Saml2_Response + Tests the is_valid method of the OneLogin_Saml2_Response_Post Case invalid cert """ settings_info = self.loadSettingsJSON() settings_info['idp']['x509cert'] = 'NotValidCert' settings = OneLogin_Saml2_Settings(settings_info) xml = self.file_contents(join(self.data_path, 'responses', 'valid_response.xml.base64')) - response = OneLogin_Saml2_Response(settings, xml) + response = OneLogin_Saml2_Response_Post(settings, xml) try: self.assertFalse(response.is_valid(self.get_request_data())) @@ -925,31 +926,31 @@ def testIsInValidCert(self): def testIsInValidCert2(self): """ - Tests the is_valid method of the OneLogin_Saml2_Response + Tests the is_valid method of the OneLogin_Saml2_Response_Post Case invalid cert2 """ settings_info = self.loadSettingsJSON() settings_info['idp']['x509cert'] = 'MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290 MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9 uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0 WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0 Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5 6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=' settings = OneLogin_Saml2_Settings(settings_info) xml = self.file_contents(join(self.data_path, 'responses', 'valid_response.xml.base64')) - response = OneLogin_Saml2_Response(settings, xml) + response = OneLogin_Saml2_Response_Post(settings, xml) self.assertFalse(response.is_valid(self.get_request_data())) def testIsValid(self): """ - Tests the is_valid method of the OneLogin_Saml2_Response + Tests the is_valid method of the OneLogin_Saml2_Response_Post Case valid unsigned response """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) xml = self.file_contents(join(self.data_path, 'responses', 'valid_unsigned_response.xml.base64')) - response = OneLogin_Saml2_Response(settings, xml) + response = OneLogin_Saml2_Response_Post(settings, xml) response.is_valid(self.get_request_data()) self.assertEqual('No Signature found. SAML Response rejected', response.get_error()) def testIsValid2(self): """ - Tests the is_valid method of the OneLogin_Saml2_Response + Tests the is_valid method of the OneLogin_Saml2_Response_Post Case valid response2 """ settings_info = self.loadSettingsJSON() @@ -957,14 +958,14 @@ def testIsValid2(self): # expired cert xml = self.file_contents(join(self.data_path, 'responses', 'valid_response.xml.base64')) - response = OneLogin_Saml2_Response(settings, xml) + response = OneLogin_Saml2_Response_Post(settings, xml) self.assertTrue(response.is_valid(self.get_request_data())) settings_info_2 = self.loadSettingsJSON('settings2.json') settings_2 = OneLogin_Saml2_Settings(settings_info_2) xml_2 = self.file_contents(join(self.data_path, 'responses', 'valid_response2.xml.base64')) - response_2 = OneLogin_Saml2_Response(settings_2, xml_2) + response_2 = OneLogin_Saml2_Response_Post(settings_2, xml_2) self.assertTrue(response_2.is_valid(self.get_request_data())) settings_info_3 = self.loadSettingsJSON('settings2.json') @@ -972,27 +973,27 @@ def testIsValid2(self): settings_info_3['idp']['certFingerprint'] = OneLogin_Saml2_Utils.calculate_x509_fingerprint(idp_cert) settings_info_3['idp']['x509cert'] = '' settings_3 = OneLogin_Saml2_Settings(settings_info_3) - response_3 = OneLogin_Saml2_Response(settings_3, xml_2) + response_3 = OneLogin_Saml2_Response_Post(settings_3, xml_2) self.assertTrue(response_3.is_valid(self.get_request_data())) settings_info_3['idp']['certFingerprintAlgorithm'] = 'sha1' settings_4 = OneLogin_Saml2_Settings(settings_info_3) - response_4 = OneLogin_Saml2_Response(settings_4, xml_2) + response_4 = OneLogin_Saml2_Response_Post(settings_4, xml_2) self.assertTrue(response_4.is_valid(self.get_request_data())) settings_info_3['idp']['certFingerprintAlgorithm'] = 'sha256' settings_5 = OneLogin_Saml2_Settings(settings_info_3) - response_5 = OneLogin_Saml2_Response(settings_5, xml_2) + response_5 = OneLogin_Saml2_Response_Post(settings_5, xml_2) self.assertFalse(response_5.is_valid(self.get_request_data())) settings_info_3['idp']['certFingerprint'] = OneLogin_Saml2_Utils.calculate_x509_fingerprint(idp_cert, 'sha256') settings_6 = OneLogin_Saml2_Settings(settings_info_3) - response_6 = OneLogin_Saml2_Response(settings_6, xml_2) + response_6 = OneLogin_Saml2_Response_Post(settings_6, xml_2) self.assertTrue(response_6.is_valid(self.get_request_data())) def testIsValidEnc(self): """ - Tests the is_valid method of the OneLogin_Saml2_Response + Tests the is_valid method of the OneLogin_Saml2_Response_Post Case valid encrypted assertion Signed data can't be modified, so Destination will always fail in @@ -1002,28 +1003,28 @@ def testIsValidEnc(self): # expired cert xml = self.file_contents(join(self.data_path, 'responses', 'double_signed_encrypted_assertion.xml.base64')) - response = OneLogin_Saml2_Response(settings, xml) + response = OneLogin_Saml2_Response_Post(settings, xml) self.assertTrue(response.is_valid(self.get_request_data())) xml_2 = self.file_contents(join(self.data_path, 'responses', 'signed_encrypted_assertion.xml.base64')) - response_2 = OneLogin_Saml2_Response(settings, xml_2) + response_2 = OneLogin_Saml2_Response_Post(settings, xml_2) self.assertTrue(response_2.is_valid(self.get_request_data())) xml_3 = self.file_contents(join(self.data_path, 'responses', 'signed_message_encrypted_assertion.xml.base64')) - response_3 = OneLogin_Saml2_Response(settings, xml_3) + response_3 = OneLogin_Saml2_Response_Post(settings, xml_3) self.assertTrue(response_3.is_valid(self.get_request_data())) settings_2 = OneLogin_Saml2_Settings(self.loadSettingsJSON('settings2.json')) xml_4 = self.file_contents(join(self.data_path, 'responses', 'double_signed_encrypted_assertion2.xml.base64')) - response_4 = OneLogin_Saml2_Response(settings_2, xml_4) + response_4 = OneLogin_Saml2_Response_Post(settings_2, xml_4) self.assertTrue(response_4.is_valid(self.get_request_data())) xml_5 = self.file_contents(join(self.data_path, 'responses', 'signed_encrypted_assertion2.xml.base64')) - response_5 = OneLogin_Saml2_Response(settings_2, xml_5) + response_5 = OneLogin_Saml2_Response_Post(settings_2, xml_5) self.assertTrue(response_5.is_valid(self.get_request_data())) xml_6 = self.file_contents(join(self.data_path, 'responses', 'signed_message_encrypted_assertion2.xml.base64')) - response_6 = OneLogin_Saml2_Response(settings_2, xml_6) + response_6 = OneLogin_Saml2_Response_Post(settings_2, xml_6) self.assertTrue(response_6.is_valid(self.get_request_data())) settings.set_strict(True) @@ -1037,13 +1038,13 @@ def testIsValidEnc(self): current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/acs.php', current_url) message = b64encode(plain_message) - response_7 = OneLogin_Saml2_Response(settings, message) + response_7 = OneLogin_Saml2_Response_Post(settings, message) response_7.is_valid(request_data) self.assertEqual('No Signature found. SAML Response rejected', response_7.get_error()) def testIsValidSign(self): """ - Tests the is_valid method of the OneLogin_Saml2_Response + Tests the is_valid method of the OneLogin_Saml2_Response_Post Case valid sign response / sign assertion / both signed Strict mode will always fail due destination problem, if we manipulate @@ -1053,47 +1054,47 @@ def testIsValidSign(self): # expired cert xml = self.file_contents(join(self.data_path, 'responses', 'signed_message_response.xml.base64')) - response = OneLogin_Saml2_Response(settings, xml) + response = OneLogin_Saml2_Response_Post(settings, xml) self.assertTrue(response.is_valid(self.get_request_data())) xml_2 = self.file_contents(join(self.data_path, 'responses', 'signed_assertion_response.xml.base64')) - response_2 = OneLogin_Saml2_Response(settings, xml_2) + response_2 = OneLogin_Saml2_Response_Post(settings, xml_2) self.assertTrue(response_2.is_valid(self.get_request_data())) xml_3 = self.file_contents(join(self.data_path, 'responses', 'double_signed_response.xml.base64')) - response_3 = OneLogin_Saml2_Response(settings, xml_3) + response_3 = OneLogin_Saml2_Response_Post(settings, xml_3) self.assertTrue(response_3.is_valid(self.get_request_data())) settings_2 = OneLogin_Saml2_Settings(self.loadSettingsJSON('settings2.json')) xml_4 = self.file_contents(join(self.data_path, 'responses', 'signed_message_response2.xml.base64')) - response_4 = OneLogin_Saml2_Response(settings_2, xml_4) + response_4 = OneLogin_Saml2_Response_Post(settings_2, xml_4) self.assertTrue(response_4.is_valid(self.get_request_data())) xml_5 = self.file_contents(join(self.data_path, 'responses', 'signed_assertion_response2.xml.base64')) - response_5 = OneLogin_Saml2_Response(settings_2, xml_5) + response_5 = OneLogin_Saml2_Response_Post(settings_2, xml_5) self.assertTrue(response_5.is_valid(self.get_request_data())) xml_6 = self.file_contents(join(self.data_path, 'responses', 'double_signed_response2.xml.base64')) - response_6 = OneLogin_Saml2_Response(settings_2, xml_6) + response_6 = OneLogin_Saml2_Response_Post(settings_2, xml_6) self.assertTrue(response_6.is_valid(self.get_request_data())) dom = parseString(b64decode(xml_4)) dom.firstChild.firstChild.firstChild.nodeValue = 'https://example.com/other-idp' xml_7 = b64encode(dom.toxml()) - response_7 = OneLogin_Saml2_Response(settings, xml_7) + response_7 = OneLogin_Saml2_Response_Post(settings, xml_7) # Modified message self.assertFalse(response_7.is_valid(self.get_request_data())) dom_2 = parseString(b64decode(xml_5)) dom_2.firstChild.firstChild.firstChild.nodeValue = 'https://example.com/other-idp' xml_8 = b64encode(dom_2.toxml()) - response_8 = OneLogin_Saml2_Response(settings, xml_8) + response_8 = OneLogin_Saml2_Response_Post(settings, xml_8) # Modified message self.assertFalse(response_8.is_valid(self.get_request_data())) dom_3 = parseString(b64decode(xml_6)) dom_3.firstChild.firstChild.firstChild.nodeValue = 'https://example.com/other-idp' xml_9 = b64encode(dom_3.toxml()) - response_9 = OneLogin_Saml2_Response(settings, xml_9) + response_9 = OneLogin_Saml2_Response_Post(settings, xml_9) # Modified message self.assertFalse(response_9.is_valid(self.get_request_data())) diff --git a/tests/src/OneLogin/saml2_tests/signed_response_test.py b/tests/src/OneLogin/saml2_tests/signed_response_test.py index 0b7860ae..b29da708 100644 --- a/tests/src/OneLogin/saml2_tests/signed_response_test.py +++ b/tests/src/OneLogin/saml2_tests/signed_response_test.py @@ -8,7 +8,7 @@ from os.path import dirname, join, exists import unittest -from onelogin.saml2.response import OneLogin_Saml2_Response +from onelogin.saml2.response import OneLogin_Saml2_Response_Post from onelogin.saml2.settings import OneLogin_Saml2_Settings @@ -38,7 +38,7 @@ def testResponseSignedAssertionNot(self): """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) message = self.file_contents(join(self.data_path, 'responses', 'open_saml_response.xml')) - response = OneLogin_Saml2_Response(settings, b64encode(message)) + response = OneLogin_Saml2_Response_Post(settings, b64encode(message)) self.assertEquals('someone@example.org', response.get_nameid()) @@ -49,6 +49,6 @@ def testResponseAndAssertionSigned(self): """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) message = self.file_contents(join(self.data_path, 'responses', 'simple_saml_php.xml')) - response = OneLogin_Saml2_Response(settings, b64encode(message)) + response = OneLogin_Saml2_Response_Post(settings, b64encode(message)) self.assertEquals('someone@example.com', response.get_nameid()) From 60a2e6d0ce66a5fb82c58c2e59f62a6a2e816a67 Mon Sep 17 00:00:00 2001 From: Sergio Pulgarin Date: Tue, 12 May 2015 19:17:21 -0500 Subject: [PATCH 07/10] Removed unused import --- tests/src/OneLogin/saml2_tests/response_test.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/src/OneLogin/saml2_tests/response_test.py b/tests/src/OneLogin/saml2_tests/response_test.py index 9b3e4669..705ae67e 100644 --- a/tests/src/OneLogin/saml2_tests/response_test.py +++ b/tests/src/OneLogin/saml2_tests/response_test.py @@ -9,8 +9,7 @@ import unittest from xml.dom.minidom import parseString -from onelogin.saml2.response import ( - OneLogin_Saml2_Response_Post, OneLogin_Saml2_Response_Redirect) +from onelogin.saml2.response import OneLogin_Saml2_Response_Post from onelogin.saml2.settings import OneLogin_Saml2_Settings from onelogin.saml2.utils import OneLogin_Saml2_Utils From 54c4cfde800735003c6b5ebdbbf4d470894e28ca Mon Sep 17 00:00:00 2001 From: Sergio Pulgarin Date: Thu, 14 May 2015 00:28:55 -0500 Subject: [PATCH 08/10] Perform appropiate validation per class --- src/onelogin/saml2/response.py | 413 +++++++++++++++++++++------------ 1 file changed, 261 insertions(+), 152 deletions(-) diff --git a/src/onelogin/saml2/response.py b/src/onelogin/saml2/response.py index ef8b3a85..6b867445 100644 --- a/src/onelogin/saml2/response.py +++ b/src/onelogin/saml2/response.py @@ -14,6 +14,7 @@ from lxml import etree from defusedxml.lxml import fromstring from xml.dom.minidom import Document +from urllib import quote_plus from onelogin.saml2.constants import OneLogin_Saml2_Constants from onelogin.saml2.utils import OneLogin_Saml2_Utils @@ -44,31 +45,11 @@ def __init__(self, settings, response): self.decrypted_document = None self.encrypted = None - # Quick check for the presence of EncryptedAssertion - encrypted_assertion_nodes = self.__query('/samlp:Response/saml:EncryptedAssertion') - if encrypted_assertion_nodes: - decrypted_document = deepcopy(self.document) - self.encrypted = True - self.decrypted_document = self.__decrypt_assertion(decrypted_document) - @staticmethod def decode_response(response): raise NotImplementedError() - def is_valid(self, request_data, request_id=None): - """ - Validates the response object. - - :param request_data: Request Data - :type request_data: dict - - :param request_id: Optional argument. The ID of the AuthNRequest sent by this SP to the IdP - :type request_id: string - - :returns: True if the SAML Response is valid, False if not - :rtype: bool - """ - self.__error = None + def is_valid(self, request_data, response_id=None): try: # Checks SAML version if self.document.get('Version', None) != '2.0': @@ -85,137 +66,7 @@ def is_valid(self, request_data, request_id=None): # Checks that the response has the SUCCESS status self.check_status() - idp_data = self.__settings.get_idp_data() - idp_entity_id = idp_data.get('entityId', '') - sp_data = self.__settings.get_sp_data() - sp_entity_id = sp_data.get('entityId', '') - - sign_nodes = self.__query('//ds:Signature') - - signed_elements = [] - for sign_node in sign_nodes: - signed_elements.append(sign_node.getparent().tag) - - if self.__settings.is_strict(): - res = OneLogin_Saml2_Utils.validate_xml(etree.tostring(self.document), 'saml-schema-protocol-2.0.xsd', self.__settings.is_debug_active()) - if not isinstance(res, Document): - raise Exception('Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd') - - security = self.__settings.get_security_data() - current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) - - # Check if the InResponseTo of the Response matchs the ID of the AuthNRequest (requestId) if provided - in_response_to = self.document.get('InResponseTo', None) - if in_response_to and request_id: - if in_response_to != request_id: - raise Exception('The InResponseTo of the Response: %s, does not match the ID of the AuthNRequest sent by the SP: %s' % (in_response_to, request_id)) - - if not self.encrypted and security.get('wantAssertionsEncrypted', False): - raise Exception('The assertion of the Response is not encrypted and the SP require it') - - if security.get('wantNameIdEncrypted', False): - encrypted_nameid_nodes = self.__query_assertion('/saml:Subject/saml:EncryptedID/xenc:EncryptedData') - if len(encrypted_nameid_nodes) == 0: - raise Exception('The NameID of the Response is not encrypted and the SP require it') - - # Checks that there is at least one AttributeStatement - attribute_statement_nodes = self.__query_assertion('/saml:AttributeStatement') - if not attribute_statement_nodes: - raise Exception('There is no AttributeStatement on the Response') - - # Validates Asserion timestamps - if not self.validate_timestamps(): - raise Exception('Timing issues (please check your clock settings)') - - encrypted_attributes_nodes = self.__query_assertion('/saml:AttributeStatement/saml:EncryptedAttribute') - if encrypted_attributes_nodes: - raise Exception('There is an EncryptedAttribute in the Response and this SP not support them') - - # Checks destination - destination = self.document.get('Destination', '') - if destination: - if not destination.startswith(current_url): - # TODO: Review if following lines are required, since we can control the - # request_data - # current_url_routed = OneLogin_Saml2_Utils.get_self_routed_url_no_query(request_data) - # if not destination.startswith(current_url_routed): - raise Exception('The response was received at %s instead of %s' % (current_url, destination)) - - # Checks audience - valid_audiences = self.get_audiences() - if valid_audiences and sp_entity_id not in valid_audiences: - raise Exception('%s is not a valid audience for this Response' % sp_entity_id) - - # Checks the issuers - issuers = self.get_issuers() - for issuer in issuers: - if issuer is None or issuer != idp_entity_id: - raise Exception('Invalid issuer in the Assertion/Response') - - # Checks the session Expiration - session_expiration = self.get_session_not_on_or_after() - if session_expiration and session_expiration <= OneLogin_Saml2_Utils.now(): - raise Exception('The attributes have expired, based on the SessionNotOnOrAfter of the AttributeStatement of this Response') - - # Checks the SubjectConfirmation, at least one SubjectConfirmation must be valid - any_subject_confirmation = False - subject_confirmation_nodes = self.__query_assertion('/saml:Subject/saml:SubjectConfirmation') - - for scn in subject_confirmation_nodes: - method = scn.get('Method', None) - if method and method != OneLogin_Saml2_Constants.CM_BEARER: - continue - sc_data = scn.find('saml:SubjectConfirmationData', namespaces=OneLogin_Saml2_Constants.NSMAP) - if sc_data is None: - continue - else: - irt = sc_data.get('InResponseTo', None) - if irt != in_response_to: - continue - recipient = sc_data.get('Recipient', None) - if recipient and current_url not in recipient: - continue - nooa = sc_data.get('NotOnOrAfter', None) - if nooa: - parsed_nooa = OneLogin_Saml2_Utils.parse_SAML_to_time(nooa) - if parsed_nooa <= OneLogin_Saml2_Utils.now(): - continue - nb = sc_data.get('NotBefore', None) - if nb: - parsed_nb = OneLogin_Saml2_Utils.parse_SAML_to_time(nb) - if parsed_nb > OneLogin_Saml2_Utils.now(): - continue - any_subject_confirmation = True - break - - if not any_subject_confirmation: - raise Exception('A valid SubjectConfirmation was not found on this Response') - - if security.get('wantAssertionsSigned', False) and ('{%s}Assertion' % OneLogin_Saml2_Constants.NS_SAML) not in signed_elements: - raise Exception('The Assertion of the Response is not signed and the SP require it') - - if security.get('wantMessagesSigned', False) and ('{%s}Response' % OneLogin_Saml2_Constants.NS_SAMLP) not in signed_elements: - raise Exception('The Message of the Response is not signed and the SP require it') - - if len(signed_elements) > 0: - cert = idp_data.get('x509cert', None) - fingerprint = idp_data.get('certFingerprint', None) - fingerprintalg = idp_data.get('certFingerprintAlgorithm', None) - - # Only validates the first sign found - if '{%s}Response' % OneLogin_Saml2_Constants.NS_SAMLP in signed_elements: - document_to_validate = self.document - else: - if self.encrypted: - document_to_validate = self.decrypted_document - else: - document_to_validate = self.document - if not OneLogin_Saml2_Utils.validate_sign(document_to_validate, cert, fingerprint, fingerprintalg): - raise Exception('Signature validation failed. SAML Response rejected') - else: - raise Exception('No Signature found. SAML Response rejected') - - return True + return self.validate_response(request_data, response_id) except Exception as err: self.__error = err.__str__() debug = self.__settings.is_debug_active() @@ -223,6 +74,9 @@ def is_valid(self, request_data, request_id=None): print err.__str__() return False + def validate_response(self, request_data, response_id=None): + raise NotImplementedError() + def check_status(self): """ Check if the status of the response is success or not @@ -466,6 +320,28 @@ def get_error(self): class OneLogin_Saml2_Response_Post(OneLogin_Saml2_Response): + def __init__(self, settings, response): + """ + Constructs the response object. + + :param settings: The setting info + :type settings: OneLogin_Saml2_Setting object + + :param response: The base64 encoded, XML string containing the samlp:Response + :type response: string + """ + OneLogin_Saml2_Response.__init__(self, settings, response) + # Reset these given the meaning of double underscore in Python. + self.__settings = settings + self.__error = None + + # Quick check for the presence of EncryptedAssertion + encrypted_assertion_nodes = self.__query('/samlp:Response/saml:EncryptedAssertion') + if encrypted_assertion_nodes: + decrypted_document = deepcopy(self.document) + self.encrypted = True + self.decrypted_document = self.__decrypt_assertion(decrypted_document) + @staticmethod def decode_response(response): """ @@ -477,9 +353,170 @@ def decode_response(response): """ return b64decode(response) + def validate_response(self, request_data, request_id=None): + """ + Validates the response object. + + :param request_data: Request Data + :type request_data: dict + + :param request_id: Optional argument. The ID of the AuthNRequest sent by this SP to the IdP + :type request_id: string + + :returns: True if the SAML Response is valid, False if not + :rtype: bool + """ + self.__error = None + idp_data = self.__settings.get_idp_data() + idp_entity_id = idp_data.get('entityId', '') + sp_data = self.__settings.get_sp_data() + sp_entity_id = sp_data.get('entityId', '') + + sign_nodes = self.__query('//ds:Signature') + + signed_elements = [] + for sign_node in sign_nodes: + signed_elements.append(sign_node.getparent().tag) + + if self.__settings.is_strict(): + res = OneLogin_Saml2_Utils.validate_xml(etree.tostring(self.document), 'saml-schema-protocol-2.0.xsd', self.__settings.is_debug_active()) + if not isinstance(res, Document): + raise Exception('Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd') + + security = self.__settings.get_security_data() + current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) + + # Check if the InResponseTo of the Response matchs the ID of the AuthNRequest (requestId) if provided + in_response_to = self.document.get('InResponseTo', None) + if in_response_to and request_id: + if in_response_to != request_id: + raise Exception('The InResponseTo of the Response: %s, does not match the ID of the AuthNRequest sent by the SP: %s' % (in_response_to, request_id)) + + if not self.encrypted and security.get('wantAssertionsEncrypted', False): + raise Exception('The assertion of the Response is not encrypted and the SP require it') + + if security.get('wantNameIdEncrypted', False): + encrypted_nameid_nodes = self.__query_assertion('/saml:Subject/saml:EncryptedID/xenc:EncryptedData') + if len(encrypted_nameid_nodes) == 0: + raise Exception('The NameID of the Response is not encrypted and the SP require it') + + # Checks that there is at least one AttributeStatement + attribute_statement_nodes = self.__query_assertion('/saml:AttributeStatement') + if not attribute_statement_nodes: + raise Exception('There is no AttributeStatement on the Response') + + # Validates Asserion timestamps + if not self.validate_timestamps(): + raise Exception('Timing issues (please check your clock settings)') + + encrypted_attributes_nodes = self.__query_assertion('/saml:AttributeStatement/saml:EncryptedAttribute') + if encrypted_attributes_nodes: + raise Exception('There is an EncryptedAttribute in the Response and this SP not support them') + + # Checks destination + destination = self.document.get('Destination', '') + if destination: + if not destination.startswith(current_url): + # TODO: Review if following lines are required, since we can control the + # request_data + # current_url_routed = OneLogin_Saml2_Utils.get_self_routed_url_no_query(request_data) + # if not destination.startswith(current_url_routed): + raise Exception('The response was received at %s instead of %s' % (current_url, destination)) + + # Checks audience + valid_audiences = self.get_audiences() + if valid_audiences and sp_entity_id not in valid_audiences: + raise Exception('%s is not a valid audience for this Response' % sp_entity_id) + + # Checks the issuers + issuers = self.get_issuers() + for issuer in issuers: + if issuer is None or issuer != idp_entity_id: + raise Exception('Invalid issuer in the Assertion/Response') + + # Checks the session Expiration + session_expiration = self.get_session_not_on_or_after() + if session_expiration and session_expiration <= OneLogin_Saml2_Utils.now(): + raise Exception('The attributes have expired, based on the SessionNotOnOrAfter of the AttributeStatement of this Response') + + # Checks the SubjectConfirmation, at least one SubjectConfirmation must be valid + any_subject_confirmation = False + subject_confirmation_nodes = self.__query_assertion('/saml:Subject/saml:SubjectConfirmation') + + for scn in subject_confirmation_nodes: + method = scn.get('Method', None) + if method and method != OneLogin_Saml2_Constants.CM_BEARER: + continue + sc_data = scn.find('saml:SubjectConfirmationData', namespaces=OneLogin_Saml2_Constants.NSMAP) + if sc_data is None: + continue + else: + irt = sc_data.get('InResponseTo', None) + if irt != in_response_to: + continue + recipient = sc_data.get('Recipient', None) + if recipient and current_url not in recipient: + continue + nooa = sc_data.get('NotOnOrAfter', None) + if nooa: + parsed_nooa = OneLogin_Saml2_Utils.parse_SAML_to_time(nooa) + if parsed_nooa <= OneLogin_Saml2_Utils.now(): + continue + nb = sc_data.get('NotBefore', None) + if nb: + parsed_nb = OneLogin_Saml2_Utils.parse_SAML_to_time(nb) + if parsed_nb > OneLogin_Saml2_Utils.now(): + continue + any_subject_confirmation = True + break + + if not any_subject_confirmation: + raise Exception('A valid SubjectConfirmation was not found on this Response') + + if security.get('wantAssertionsSigned', False) and ('{%s}Assertion' % OneLogin_Saml2_Constants.NS_SAML) not in signed_elements: + raise Exception('The Assertion of the Response is not signed and the SP require it') + + if security.get('wantMessagesSigned', False) and ('{%s}Response' % OneLogin_Saml2_Constants.NS_SAMLP) not in signed_elements: + raise Exception('The Message of the Response is not signed and the SP require it') + + if len(signed_elements) > 0: + cert = idp_data.get('x509cert', None) + fingerprint = idp_data.get('certFingerprint', None) + fingerprintalg = idp_data.get('certFingerprintAlgorithm', None) + + # Only validates the first sign found + if '{%s}Response' % OneLogin_Saml2_Constants.NS_SAMLP in signed_elements: + document_to_validate = self.document + else: + if self.encrypted: + document_to_validate = self.decrypted_document + else: + document_to_validate = self.document + if not OneLogin_Saml2_Utils.validate_sign(document_to_validate, cert, fingerprint, fingerprintalg): + raise Exception('Signature validation failed. SAML Response rejected') + else: + raise Exception('No Signature found. SAML Response rejected') + + return True + class OneLogin_Saml2_Response_Redirect(OneLogin_Saml2_Response): + def __init__(self, settings, response): + """ + Constructs the response object. + + :param settings: The setting info + :type settings: OneLogin_Saml2_Setting object + + :param response: The base64 encoded, XML string containing the samlp:Response + :type response: string + """ + OneLogin_Saml2_Response.__init__(self, settings, response) + # Reset these given the meaning of double underscore in Python. + self.__settings = settings + self.__error = None + @staticmethod def decode_response(response): """ @@ -490,3 +527,75 @@ def decode_response(response): :rtype: String """ return OneLogin_Saml2_Utils.decode_base64_and_inflate(response) + + def validate_response(self, request_data, request_id=None): + """ + Validates the response object. + + :param request_data: Request Data + :type request_data: dict + + :param request_id: Optional argument. The ID of the AuthNRequest sent by this SP to the IdP + :type request_id: string + + :returns: True if the SAML Response is valid, False if not + :rtype: bool + """ + self.__error = None + idp_data = self.__settings.get_idp_data() + idp_entity_id = idp_data['entityId'] + get_data = request_data['get_data'] + + if self.__settings.is_strict(): + res = OneLogin_Saml2_Utils.validate_xml(self.document, 'saml-schema-protocol-2.0.xsd', self.__settings.is_debug_active()) + if not isinstance(res, Document): + raise Exception('Invalid SAML Logout Request. Not match the saml-schema-protocol-2.0.xsd') + + security = self.__settings.get_security_data() + + # Check if the InResponseTo of the Logout Response matchs the ID of the Logout Request (requestId) if provided + if request_id is not None and self.document.documentElement.hasAttribute('InResponseTo'): + in_response_to = self.document.documentElement.getAttribute('InResponseTo') + if request_id != in_response_to: + raise Exception('The InResponseTo of the Logout Response: %s, does not match the ID of the Logout request sent by the SP: %s' % (in_response_to, request_id)) + + # Check issuer + issuer = self.get_issuer() + if issuer is not None and issuer != idp_entity_id: + raise Exception('Invalid issuer in the Logout Request') + + current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) + + # Check destination + if self.document.documentElement.hasAttribute('Destination'): + destination = self.document.documentElement.getAttribute('Destination') + if destination != '': + if current_url not in destination: + raise Exception('The LogoutRequest was received at $currentURL instead of $destination') + + if security['wantMessagesSigned']: + if 'Signature' not in get_data: + raise Exception('The Message of the Logout Response is not signed and the SP require it') + + if 'Signature' in get_data: + if 'SigAlg' not in get_data: + sign_alg = OneLogin_Saml2_Constants.RSA_SHA1 + else: + sign_alg = get_data['SigAlg'] + + if sign_alg != OneLogin_Saml2_Constants.RSA_SHA1: + raise Exception('Invalid signAlg in the recieved Logout Response') + + signed_query = 'SAMLResponse=%s' % quote_plus(get_data['SAMLResponse']) + if 'RelayState' in get_data: + signed_query = '%s&RelayState=%s' % (signed_query, quote_plus(get_data['RelayState'])) + signed_query = '%s&SigAlg=%s' % (signed_query, quote_plus(sign_alg)) + + if 'x509cert' not in idp_data or idp_data['x509cert'] is None: + raise Exception('In order to validate the sign on the Logout Response, the x509cert of the IdP is required') + cert = idp_data['x509cert'] + + if not OneLogin_Saml2_Utils.validate_binary_sign(signed_query, b64decode(get_data['Signature']), cert): + raise Exception('Signature validation failed. Logout Response rejected') + + return True From 0a33cd640669dd279f561eda4b7d5f321c5afb0a Mon Sep 17 00:00:00 2001 From: Sergio Pulgarin Date: Thu, 14 May 2015 11:53:54 -0500 Subject: [PATCH 09/10] Changing private attributes to protected --- src/onelogin/saml2/response.py | 96 +++++++++++++++++----------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/src/onelogin/saml2/response.py b/src/onelogin/saml2/response.py index 6b867445..12bf8d49 100644 --- a/src/onelogin/saml2/response.py +++ b/src/onelogin/saml2/response.py @@ -38,8 +38,8 @@ def __init__(self, settings, response): :param response: The base64 encoded, XML string containing the samlp:Response :type response: string """ - self.__settings = settings - self.__error = None + self._settings = settings + self._error = None self.response = self.__class__.decode_response(response) self.document = fromstring(self.response) self.decrypted_document = None @@ -68,8 +68,8 @@ def is_valid(self, request_data, response_id=None): return self.validate_response(request_data, response_id) except Exception as err: - self.__error = err.__str__() - debug = self.__settings.is_debug_active() + self._error = err.__str__() + debug = self._settings.is_debug_active() if debug: print err.__str__() return False @@ -103,7 +103,7 @@ def get_audiences(self): """ audiences = [] - audience_nodes = self.__query_assertion('/saml:Conditions/saml:AudienceRestriction/saml:Audience') + audience_nodes = self._query_assertion('/saml:Conditions/saml:AudienceRestriction/saml:Audience') for audience_node in audience_nodes: audiences.append(audience_node.text) return audiences @@ -117,11 +117,11 @@ def get_issuers(self): """ issuers = [] - message_issuer_nodes = self.__query('/samlp:Response/saml:Issuer') + message_issuer_nodes = self._query('/samlp:Response/saml:Issuer') if message_issuer_nodes: issuers.append(message_issuer_nodes[0].text) - assertion_issuer_nodes = self.__query_assertion('/saml:Issuer') + assertion_issuer_nodes = self._query_assertion('/saml:Issuer') if assertion_issuer_nodes: issuers.append(assertion_issuer_nodes[0].text) @@ -135,13 +135,13 @@ def get_nameid_data(self): :rtype: dict """ nameid = None - encrypted_id_data_nodes = self.__query_assertion('/saml:Subject/saml:EncryptedID/xenc:EncryptedData') + encrypted_id_data_nodes = self._query_assertion('/saml:Subject/saml:EncryptedID/xenc:EncryptedData') if encrypted_id_data_nodes: encrypted_data = encrypted_id_data_nodes[0] - key = self.__settings.get_sp_key() + key = self._settings.get_sp_key() nameid = OneLogin_Saml2_Utils.decrypt_element(encrypted_data, key) else: - nameid_nodes = self.__query_assertion('/saml:Subject/saml:NameID') + nameid_nodes = self._query_assertion('/saml:Subject/saml:NameID') if nameid_nodes: nameid = nameid_nodes[0] if nameid is None: @@ -173,7 +173,7 @@ def get_session_not_on_or_after(self): :rtype: time|None """ not_on_or_after = None - authn_statement_nodes = self.__query_assertion('/saml:AuthnStatement[@SessionNotOnOrAfter]') + authn_statement_nodes = self._query_assertion('/saml:AuthnStatement[@SessionNotOnOrAfter]') if authn_statement_nodes: not_on_or_after = OneLogin_Saml2_Utils.parse_SAML_to_time(authn_statement_nodes[0].get('SessionNotOnOrAfter')) return not_on_or_after @@ -189,7 +189,7 @@ def get_session_index(self): :rtype: string|None """ session_index = None - authn_statement_nodes = self.__query_assertion('/saml:AuthnStatement[@SessionIndex]') + authn_statement_nodes = self._query_assertion('/saml:AuthnStatement[@SessionIndex]') if authn_statement_nodes: session_index = authn_statement_nodes[0].get('SessionIndex') return session_index @@ -200,7 +200,7 @@ def get_attributes(self): EncryptedAttributes are not supported """ attributes = {} - attribute_nodes = self.__query_assertion('/saml:AttributeStatement/saml:Attribute') + attribute_nodes = self._query_assertion('/saml:AttributeStatement/saml:Attribute') for attribute_node in attribute_nodes: attr_name = attribute_node.get('Name') values = [] @@ -216,8 +216,8 @@ def validate_num_assertions(self): :returns: True if only 1 assertion encrypted or not :rtype: bool """ - encrypted_assertion_nodes = self.__query('/samlp:Response/saml:EncryptedAssertion') - assertion_nodes = self.__query('/samlp:Response/saml:Assertion') + encrypted_assertion_nodes = self._query('/samlp:Response/saml:EncryptedAssertion') + assertion_nodes = self._query('/samlp:Response/saml:Assertion') return (len(encrypted_assertion_nodes) + len(assertion_nodes)) == 1 def validate_timestamps(self): @@ -227,7 +227,7 @@ def validate_timestamps(self): :returns: True if the condition is valid, False otherwise :rtype: bool """ - conditions_nodes = self.__query_assertion('/saml:Conditions') + conditions_nodes = self._query_assertion('/saml:Conditions') for conditions_node in conditions_nodes: nb_attr = conditions_node.get('NotBefore') @@ -238,7 +238,7 @@ def validate_timestamps(self): return False return True - def __query_assertion(self, xpath_expr): + def _query_assertion(self, xpath_expr): """ Extracts nodes that match the query from the Assertion @@ -254,12 +254,12 @@ def __query_assertion(self, xpath_expr): assertion_expr = '/saml:Assertion' signature_expr = '/ds:Signature/ds:SignedInfo/ds:Reference' signed_assertion_query = '/samlp:Response' + assertion_expr + signature_expr - assertion_reference_nodes = self.__query(signed_assertion_query) + assertion_reference_nodes = self._query(signed_assertion_query) if not assertion_reference_nodes: # Check if the message is signed signed_message_query = '/samlp:Response' + signature_expr - message_reference_nodes = self.__query(signed_message_query) + message_reference_nodes = self._query(signed_message_query) if message_reference_nodes: message_id = message_reference_nodes[0].get('URI') final_query = "/samlp:Response[@ID='%s']/" % message_id[1:] @@ -270,9 +270,9 @@ def __query_assertion(self, xpath_expr): assertion_id = assertion_reference_nodes[0].get('URI') final_query = '/samlp:Response' + assertion_expr + "[@ID='%s']" % assertion_id[1:] final_query += xpath_expr - return self.__query(final_query) + return self._query(final_query) - def __query(self, query): + def _query(self, query): """ Extracts nodes that match the query from the Response @@ -288,7 +288,7 @@ def __query(self, query): document = self.document return OneLogin_Saml2_Utils.query(document, query) - def __decrypt_assertion(self, dom): + def _decrypt_assertion(self, dom): """ Decrypts the Assertion @@ -298,7 +298,7 @@ def __decrypt_assertion(self, dom): :returns: Decrypted Assertion :rtype: Element """ - key = self.__settings.get_sp_key() + key = self._settings.get_sp_key() if not key: raise Exception('No private key available, check settings') @@ -315,7 +315,7 @@ def get_error(self): """ After execute a validation process, if fails this method returns the cause """ - return self.__error + return self._error class OneLogin_Saml2_Response_Post(OneLogin_Saml2_Response): @@ -331,16 +331,13 @@ def __init__(self, settings, response): :type response: string """ OneLogin_Saml2_Response.__init__(self, settings, response) - # Reset these given the meaning of double underscore in Python. - self.__settings = settings - self.__error = None # Quick check for the presence of EncryptedAssertion - encrypted_assertion_nodes = self.__query('/samlp:Response/saml:EncryptedAssertion') + encrypted_assertion_nodes = self._query('/samlp:Response/saml:EncryptedAssertion') if encrypted_assertion_nodes: decrypted_document = deepcopy(self.document) self.encrypted = True - self.decrypted_document = self.__decrypt_assertion(decrypted_document) + self.decrypted_document = self._decrypt_assertion(decrypted_document) @staticmethod def decode_response(response): @@ -366,24 +363,24 @@ def validate_response(self, request_data, request_id=None): :returns: True if the SAML Response is valid, False if not :rtype: bool """ - self.__error = None - idp_data = self.__settings.get_idp_data() + self._error = None + idp_data = self._settings.get_idp_data() idp_entity_id = idp_data.get('entityId', '') - sp_data = self.__settings.get_sp_data() + sp_data = self._settings.get_sp_data() sp_entity_id = sp_data.get('entityId', '') - sign_nodes = self.__query('//ds:Signature') + sign_nodes = self._query('//ds:Signature') signed_elements = [] for sign_node in sign_nodes: signed_elements.append(sign_node.getparent().tag) - if self.__settings.is_strict(): - res = OneLogin_Saml2_Utils.validate_xml(etree.tostring(self.document), 'saml-schema-protocol-2.0.xsd', self.__settings.is_debug_active()) + if self._settings.is_strict(): + res = OneLogin_Saml2_Utils.validate_xml(etree.tostring(self.document), 'saml-schema-protocol-2.0.xsd', self._settings.is_debug_active()) if not isinstance(res, Document): raise Exception('Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd') - security = self.__settings.get_security_data() + security = self._settings.get_security_data() current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) # Check if the InResponseTo of the Response matchs the ID of the AuthNRequest (requestId) if provided @@ -396,12 +393,12 @@ def validate_response(self, request_data, request_id=None): raise Exception('The assertion of the Response is not encrypted and the SP require it') if security.get('wantNameIdEncrypted', False): - encrypted_nameid_nodes = self.__query_assertion('/saml:Subject/saml:EncryptedID/xenc:EncryptedData') + encrypted_nameid_nodes = self._query_assertion('/saml:Subject/saml:EncryptedID/xenc:EncryptedData') if len(encrypted_nameid_nodes) == 0: raise Exception('The NameID of the Response is not encrypted and the SP require it') # Checks that there is at least one AttributeStatement - attribute_statement_nodes = self.__query_assertion('/saml:AttributeStatement') + attribute_statement_nodes = self._query_assertion('/saml:AttributeStatement') if not attribute_statement_nodes: raise Exception('There is no AttributeStatement on the Response') @@ -409,7 +406,7 @@ def validate_response(self, request_data, request_id=None): if not self.validate_timestamps(): raise Exception('Timing issues (please check your clock settings)') - encrypted_attributes_nodes = self.__query_assertion('/saml:AttributeStatement/saml:EncryptedAttribute') + encrypted_attributes_nodes = self._query_assertion('/saml:AttributeStatement/saml:EncryptedAttribute') if encrypted_attributes_nodes: raise Exception('There is an EncryptedAttribute in the Response and this SP not support them') @@ -441,7 +438,7 @@ def validate_response(self, request_data, request_id=None): # Checks the SubjectConfirmation, at least one SubjectConfirmation must be valid any_subject_confirmation = False - subject_confirmation_nodes = self.__query_assertion('/saml:Subject/saml:SubjectConfirmation') + subject_confirmation_nodes = self._query_assertion('/saml:Subject/saml:SubjectConfirmation') for scn in subject_confirmation_nodes: method = scn.get('Method', None) @@ -513,9 +510,6 @@ def __init__(self, settings, response): :type response: string """ OneLogin_Saml2_Response.__init__(self, settings, response) - # Reset these given the meaning of double underscore in Python. - self.__settings = settings - self.__error = None @staticmethod def decode_response(response): @@ -541,17 +535,17 @@ def validate_response(self, request_data, request_id=None): :returns: True if the SAML Response is valid, False if not :rtype: bool """ - self.__error = None - idp_data = self.__settings.get_idp_data() + self._error = None + idp_data = self._settings.get_idp_data() idp_entity_id = idp_data['entityId'] get_data = request_data['get_data'] - if self.__settings.is_strict(): - res = OneLogin_Saml2_Utils.validate_xml(self.document, 'saml-schema-protocol-2.0.xsd', self.__settings.is_debug_active()) + if self._settings.is_strict(): + res = OneLogin_Saml2_Utils.validate_xml(self.document, 'saml-schema-protocol-2.0.xsd', self._settings.is_debug_active()) if not isinstance(res, Document): raise Exception('Invalid SAML Logout Request. Not match the saml-schema-protocol-2.0.xsd') - security = self.__settings.get_security_data() + security = self._settings.get_security_data() # Check if the InResponseTo of the Logout Response matchs the ID of the Logout Request (requestId) if provided if request_id is not None and self.document.documentElement.hasAttribute('InResponseTo'): @@ -595,6 +589,12 @@ def validate_response(self, request_data, request_id=None): raise Exception('In order to validate the sign on the Logout Response, the x509cert of the IdP is required') cert = idp_data['x509cert'] + print '////////////' + print cert + print get_data + print signed_query + print '////////////' + if not OneLogin_Saml2_Utils.validate_binary_sign(signed_query, b64decode(get_data['Signature']), cert): raise Exception('Signature validation failed. Logout Response rejected') From 1eb75baf8ea3b68d6b24ea515f9c18f2763f8911 Mon Sep 17 00:00:00 2001 From: Sergio Pulgarin Date: Thu, 14 May 2015 12:55:02 -0500 Subject: [PATCH 10/10] Fixed bug in string formatting. --- src/onelogin/saml2/auth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/onelogin/saml2/auth.py b/src/onelogin/saml2/auth.py index 9a0a72f5..43dc5a15 100644 --- a/src/onelogin/saml2/auth.py +++ b/src/onelogin/saml2/auth.py @@ -117,7 +117,7 @@ def process_response(self, request_id=None): else: self.__errors.append('invalid_binding') raise OneLogin_Saml2_Error( - 'SAML Response not found for binding %s'.format(acs_binding.split(':')[-1]), + 'SAML Response not found for binding %s' % acs_binding.split(':')[-1], OneLogin_Saml2_Error.SAML_RESPONSE_NOT_FOUND )