From 5d827674714212ad2536e54ac964791c8126024d Mon Sep 17 00:00:00 2001 From: Alex Bublichenko Date: Thu, 23 May 2019 19:21:14 -0700 Subject: Parse assertions with Holder-of-Key profile Problem: Holder-of-Key assertions are used to achieve higher levels of federation security, compared to bearer assertions, by having Relying Party challenge subscriber to prove possession of the key specified in the assertion that represents subscriber in addition to verifying the assertion itself signed by Identity Provider. More information about it can be found in https://pages.nist.gov/800-63-3/sp800-63c.html This library fails to parase SAML respones containing assertions with Holder-of-Key profile, for example: ``` MIICITCCAYoCAQEwDQYJKoZIhvcNAQELBQAwWDELMAkGA1UEBhMCenoxCzAJBgNVBAgMAnp6MQ0wCwYDVQQHDAR6enp6MQ4wDAYDVQQKDAVaenp6ejEOMAwGA1UECwwFWnp6enoxDTALBgNVBAMMBHRlc3QwIBcNMTkwNDEyMTk1MDM0WhgPMzAxODA4MTMxOTUwMzRaMFgxCzAJBgNVBAYTAnp6MQswCQYDVQQIDAJ6ejENMAsGA1UEBwwEenp6ejEOMAwGA1UECgwFWnp6enoxDjAMBgNVBAsMBVp6enp6MQ0wCwYDVQQDDAR0ZXN0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDHcj80WU/XBsd9FlyQmfjPUdfmedhCFDd6TEQmZNNqP/UG+VkGa+BXjRIHMfic/WxPTbGhCjv68ci0UDNomUXagFexLGNpkwa7+CRVtoc/1xgq+ySE6M4nhcCutScoxNvWNn5eSQ66i3U0sTv91MgsXxqEdTaiZg0BIufEc3dueQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAGUV5B+USHvaRa8kgCNJSuNpo6ARlv0ekrk8bbdNRBiEUdCMyoGJFfuM9K0zybX6Vr25wai3nvaog294Vx/jWjX2g5SDbjItH6VGy6C9GCGf1A07VxFRCfJn5tA9HuJjPKiE+g/BmrV5N4CealzFxPHWYkNOzoRU8qI7OqUai1kL ``` fails to be parsed with the following error: ``` ERROR saml2.response:response.py:836 get subject Traceback (most recent call last): File "/home/abublich/repos/abliqo-pysaml2/venv/local/lib/python2.7/site-packages/pysaml2-4.7.0-py2.7.egg/saml2/response.py", line 828, in _assertion self.get_subject() File "/home/abublich/repos/abliqo-pysaml2/venv/local/lib/python2.7/site-packages/pysaml2-4.7.0-py2.7.egg/saml2/response.py", line 753, in get_subject if not self._holder_of_key_confirmed(_data): File "/home/abublich/repos/abliqo-pysaml2/venv/local/lib/python2.7/site-packages/pysaml2-4.7.0-py2.7.egg/saml2/response.py", line 730, in _holder_of_key_confirmed [samlp, saml, xenc, ds]): File "/home/abublich/repos/abliqo-pysaml2/venv/local/lib/python2.7/site-packages/pysaml2-4.7.0-py2.7.egg/saml2/__init__.py", line 1004, in extension_elements_to_elements for extension_element in extension_elements: TypeError: 'SubjectConfirmationData' object is not iterable ``` The root cause is two-fold: 1. The type SubjectConfirmationDataType_ does not declare KeyInfo as child element. 2. The bug in function _holder_of_key_confirmed: it should check KeyInfo child element of SubjectConfirmationData instead of SubjectConfirmationData itself. Solution: Fixed the root cause and added new unit tests that verify successful parsing of Holder-of-Key assertions. --- .gitignore | 3 +++ src/saml2/response.py | 4 ++-- src/saml2/saml.py | 5 +++++ tests/saml2_data.py | 30 +++++++++++++++++++++++++++++ tests/saml_hok.xml | 45 +++++++++++++++++++++++++++++++++++++++++++ tests/test_02_saml.py | 50 ++++++++++++++++++++++++++++++++---------------- tests/test_93_hok.py | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 172 insertions(+), 18 deletions(-) create mode 100644 tests/saml_hok.xml create mode 100644 tests/test_93_hok.py diff --git a/.gitignore b/.gitignore index a1e06506..7db203ec 100644 --- a/.gitignore +++ b/.gitignore @@ -114,6 +114,9 @@ venv.bak/ # Rope project settings .ropeproject +# Visual Studio Code files +.vscode/ + # mkdocs documentation /site diff --git a/src/saml2/response.py b/src/saml2/response.py index 2660e738..c16be47f 100644 --- a/src/saml2/response.py +++ b/src/saml2/response.py @@ -722,11 +722,11 @@ class AuthnResponse(StatusResponse): return True def _holder_of_key_confirmed(self, data): - if not data: + if not data or not data.key_info: return False has_keyinfo = False - for element in extension_elements_to_elements(data, + for element in extension_elements_to_elements(data.key_info, [samlp, saml, xenc, ds]): if isinstance(element, ds.KeyInfo): has_keyinfo = True diff --git a/src/saml2/saml.py b/src/saml2/saml.py index bdb1ec60..0d6728e5 100644 --- a/src/saml2/saml.py +++ b/src/saml2/saml.py @@ -482,8 +482,12 @@ class SubjectConfirmationDataType_(SamlBase): c_any = {"namespace": "##any", "processContents": "lax", "minOccurs": "0", "maxOccurs": "unbounded"} c_any_attribute = {"namespace": "##other", "processContents": "lax"} + c_children['{http://www.w3.org/2000/09/xmldsig#}KeyInfo'] = ('key_info', + [ds.KeyInfo]) + c_cardinality['key_info'] = {"min": 0, "max": 1} def __init__(self, + key_info=None, not_before=None, not_on_or_after=None, recipient=None, @@ -496,6 +500,7 @@ class SubjectConfirmationDataType_(SamlBase): text=text, extension_elements=extension_elements, extension_attributes=extension_attributes) + self.key_info = key_info self.not_before = not_before self.not_on_or_after = not_on_or_after self.recipient = recipient diff --git a/tests/saml2_data.py b/tests/saml2_data.py index fe650e7b..f81f2ce5 100644 --- a/tests/saml2_data.py +++ b/tests/saml2_data.py @@ -123,6 +123,36 @@ TEST_SUBJECT_CONFIRMATION = """ """ +TEST_HOLDER_OF_KEY_SUBJECT_CONFIRMATION = """ + + + + + +MIICITCCAYoCAQEwDQYJKoZIhvcNAQELBQAwWDELMAkGA1UEBhMCenoxCzAJBgNV +BAgMAnp6MQ0wCwYDVQQHDAR6enp6MQ4wDAYDVQQKDAVaenp6ejEOMAwGA1UECwwF +Wnp6enoxDTALBgNVBAMMBHRlc3QwIBcNMTkwNDEyMTk1MDM0WhgPMzAxODA4MTMx +OTUwMzRaMFgxCzAJBgNVBAYTAnp6MQswCQYDVQQIDAJ6ejENMAsGA1UEBwwEenp6 +ejEOMAwGA1UECgwFWnp6enoxDjAMBgNVBAsMBVp6enp6MQ0wCwYDVQQDDAR0ZXN0 +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDHcj80WU/XBsd9FlyQmfjPUdfm +edhCFDd6TEQmZNNqP/UG+VkGa+BXjRIHMfic/WxPTbGhCjv68ci0UDNomUXagFex +LGNpkwa7+CRVtoc/1xgq+ySE6M4nhcCutScoxNvWNn5eSQ66i3U0sTv91MgsXxqE +dTaiZg0BIufEc3dueQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAGUV5B+USHvaRa8k +gCNJSuNpo6ARlv0ekrk8bbdNRBiEUdCMyoGJFfuM9K0zybX6Vr25wai3nvaog294 +Vx/jWjX2g5SDbjItH6VGy6C9GCGf1A07VxFRCfJn5tA9HuJjPKiE+g/BmrV5N4Ce +alzFxPHWYkNOzoRU8qI7OqUai1kL + + + + + +""" + TEST_SUBJECT = """ + + + https://idp:8443 + + + + + https://idp:8443 + + 57a0a35eefdb29ca8b4ab78d5a118117 + + + + + MIICITCCAYoCAQEwDQYJKoZIhvcNAQELBQAwWDELMAkGA1UEBhMCenoxCzAJBgNVBAgMAnp6MQ0wCwYDVQQHDAR6enp6MQ4wDAYDVQQKDAVaenp6ejEOMAwGA1UECwwFWnp6enoxDTALBgNVBAMMBHRlc3QwIBcNMTkwNDEyMTk1MDM0WhgPMzAxODA4MTMxOTUwMzRaMFgxCzAJBgNVBAYTAnp6MQswCQYDVQQIDAJ6ejENMAsGA1UEBwwEenp6ejEOMAwGA1UECgwFWnp6enoxDjAMBgNVBAsMBVp6enp6MQ0wCwYDVQQDDAR0ZXN0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDHcj80WU/XBsd9FlyQmfjPUdfmedhCFDd6TEQmZNNqP/UG+VkGa+BXjRIHMfic/WxPTbGhCjv68ci0UDNomUXagFexLGNpkwa7+CRVtoc/1xgq+ySE6M4nhcCutScoxNvWNn5eSQ66i3U0sTv91MgsXxqEdTaiZg0BIufEc3dueQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAGUV5B+USHvaRa8kgCNJSuNpo6ARlv0ekrk8bbdNRBiEUdCMyoGJFfuM9K0zybX6Vr25wai3nvaog294Vx/jWjX2g5SDbjItH6VGy6C9GCGf1A07VxFRCfJn5tA9HuJjPKiE+g/BmrV5N4CealzFxPHWYkNOzoRU8qI7OqUai1kL + + + + + + + + + MIICITCCAYoCAQEwDQYJKoZIhvcNAQELBQAwWDELMAkGA1UEBhMCenoxCzAJBgNVBAgMAnp6MQ0wCwYDVQQHDAR6enp6MQ4wDAYDVQQKDAVaenp6ejEOMAwGA1UECwwFWnp6enoxDTALBgNVBAMMBHRlc3QwIBcNMTkwNDEyMTk1MDM0WhgPMzAxODA4MTMxOTUwMzRaMFgxCzAJBgNVBAYTAnp6MQswCQYDVQQIDAJ6ejENMAsGA1UEBwwEenp6ejEOMAwGA1UECgwFWnp6enoxDjAMBgNVBAsMBVp6enp6MQ0wCwYDVQQDDAR0ZXN0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDjW0kJM+4baWKtvO24ZsGXNvNKKkwTMz7OW5Z6BRqhSOq2WA0c5NCpMk6rD8Z2OTFEolPojEjf8dVyd/Ds/hrjFKQv8wQgbdXLN51YTIsgd6h+hBJO+vzhl0PT4aT7M0JKo5ALtS6qk4tsworW2BnwyvsGSAinwfeWt4t/b1J3kwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAFtj7WArQQBugmh/KQjjlfTQ5A052QeXfgTyO9vv1S6MRIi7qgiaEv49cGXnJv/TWbySkMKObPMUApjg6z8PqcxuShew5FCTkNvwhABFPiyu0fUj3e2FEPHfsBu76jz4ugtmhUqjqhzwFY9ctnWRkkl6J0AjM3LnHOSgjNIclDZG + + + + + + + + urn:oasis:names:tc:SAML:2.0:ac:classes:Password + + + + + testuser + + + + diff --git a/tests/test_02_saml.py b/tests/test_02_saml.py index 7ff64885..2cdb5064 100644 --- a/tests/test_02_saml.py +++ b/tests/test_02_saml.py @@ -867,35 +867,53 @@ class TestSubjectConfirmation: self.sc.subject_confirmation_data = saml.subject_confirmation_data_from_string( saml2_data.TEST_SUBJECT_CONFIRMATION_DATA) new_sc = saml.subject_confirmation_from_string(self.sc.to_string()) - assert new_sc.name_id.sp_provided_id == "sp provided id" - assert new_sc.method == saml.SCM_BEARER - assert new_sc.subject_confirmation_data.not_before == \ - "2007-08-31T01:05:02Z" - assert new_sc.subject_confirmation_data.not_on_or_after == \ - "2007-09-14T01:05:02Z" - assert new_sc.subject_confirmation_data.recipient == "recipient" - assert new_sc.subject_confirmation_data.in_response_to == "responseID" - assert new_sc.subject_confirmation_data.address == "127.0.0.1" - - def testUsingTestData(self): - """Test subject_confirmation_from_string() using test data""" + self._assertBearer(new_sc) + def testBearerUsingTestData(self): + """Test subject_confirmation_from_string() using test data for 'bearer' SubjectConfirmation""" sc = saml.subject_confirmation_from_string( saml2_data.TEST_SUBJECT_CONFIRMATION) + assert sc.verify() + self._assertBearer(sc) + + def _assertBearer(self, sc): + """Asserts SubjectConfirmation that has method 'bearer'""" assert sc.name_id.sp_provided_id == "sp provided id" assert sc.method == saml.SCM_BEARER + assert sc.subject_confirmation_data is not None assert sc.subject_confirmation_data.not_before == "2007-08-31T01:05:02Z" assert sc.subject_confirmation_data.not_on_or_after == "2007-09-14T01:05:02Z" assert sc.subject_confirmation_data.recipient == "recipient" assert sc.subject_confirmation_data.in_response_to == "responseID" assert sc.subject_confirmation_data.address == "127.0.0.1" + assert sc.subject_confirmation_data.key_info is None - def testVerify(self): - """Test SubjectConfirmation verify""" - + def testHolderOfKeyUsingTestData(self): + """Test subject_confirmation_from_string() using test data for 'holder-of-key' SubjectConfirmation""" sc = saml.subject_confirmation_from_string( - saml2_data.TEST_SUBJECT_CONFIRMATION) + saml2_data.TEST_HOLDER_OF_KEY_SUBJECT_CONFIRMATION) assert sc.verify() + assert sc.method == saml.SCM_HOLDER_OF_KEY + assert sc.subject_confirmation_data is not None + assert sc.subject_confirmation_data.not_on_or_after == "2007-09-14T01:05:02Z" + assert sc.subject_confirmation_data.recipient == "recipient" + assert sc.subject_confirmation_data.in_response_to == "responseID" + key_info = sc.subject_confirmation_data.key_info + assert len(key_info) == 1 + assert len(key_info[0].x509_data) == 1 + assert key_info[0].x509_data[0].x509_certificate.text.strip() == """ +MIICITCCAYoCAQEwDQYJKoZIhvcNAQELBQAwWDELMAkGA1UEBhMCenoxCzAJBgNV +BAgMAnp6MQ0wCwYDVQQHDAR6enp6MQ4wDAYDVQQKDAVaenp6ejEOMAwGA1UECwwF +Wnp6enoxDTALBgNVBAMMBHRlc3QwIBcNMTkwNDEyMTk1MDM0WhgPMzAxODA4MTMx +OTUwMzRaMFgxCzAJBgNVBAYTAnp6MQswCQYDVQQIDAJ6ejENMAsGA1UEBwwEenp6 +ejEOMAwGA1UECgwFWnp6enoxDjAMBgNVBAsMBVp6enp6MQ0wCwYDVQQDDAR0ZXN0 +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDHcj80WU/XBsd9FlyQmfjPUdfm +edhCFDd6TEQmZNNqP/UG+VkGa+BXjRIHMfic/WxPTbGhCjv68ci0UDNomUXagFex +LGNpkwa7+CRVtoc/1xgq+ySE6M4nhcCutScoxNvWNn5eSQ66i3U0sTv91MgsXxqE +dTaiZg0BIufEc3dueQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAGUV5B+USHvaRa8k +gCNJSuNpo6ARlv0ekrk8bbdNRBiEUdCMyoGJFfuM9K0zybX6Vr25wai3nvaog294 +Vx/jWjX2g5SDbjItH6VGy6C9GCGf1A07VxFRCfJn5tA9HuJjPKiE+g/BmrV5N4Ce +alzFxPHWYkNOzoRU8qI7OqUai1kL""".strip() class TestSubject: diff --git a/tests/test_93_hok.py b/tests/test_93_hok.py new file mode 100644 index 00000000..085c930d --- /dev/null +++ b/tests/test_93_hok.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +from contextlib import closing +from datetime import datetime +from dateutil import parser +from string import translate, whitespace +from saml2.authn_context import INTERNETPROTOCOLPASSWORD + +from saml2.server import Server +from saml2.response import authn_response +from saml2.config import config_factory + +from pathutils import dotname, full_path + +# Example SAML response iwth 'holder-of-key' subject confirmtaions +# containing DER-base64 copies (without PEM enclosure) of test_1.crt and test_2.crt +HOLDER_OF_KEY_RESPONSE_FILE = full_path("saml_hok.xml") + +TEST_CERT_1 = full_path("test_1.crt") +TEST_CERT_2 = full_path("test_2.crt") + + +class TestHolderOfKeyResponse: + def test_hok_response_is_parsed(self): + """Verifies that response with 'holder-of-key' subject confirmations is parsed successfully.""" + conf = config_factory("idp", dotname("server_conf")) + resp = authn_response(conf, "https://sp:443/.auth/saml/login", asynchop=False, allow_unsolicited=True) + with open(HOLDER_OF_KEY_RESPONSE_FILE, 'r') as fp: + authn_response_xml = fp.read() + resp.loads(authn_response_xml, False) + resp.do_not_verify = True + + resp.parse_assertion() + + assert resp.get_subject() is not None + assert len(resp.assertion.subject.subject_confirmation) == 2 + actual_certs = [sc.subject_confirmation_data.key_info[0].x509_data[0].x509_certificate.text.strip() + for sc in resp.assertion.subject.subject_confirmation] + expected_certs = [self._read_cert_without_pem_enclosure(TEST_CERT_1), + self._read_cert_without_pem_enclosure(TEST_CERT_2)] + assert actual_certs == expected_certs + + def _read_cert_without_pem_enclosure(self, path): + with open(path, 'r') as fp: + lines = fp.readlines() + lines_without_enclosure = lines[1:-1] + return ''.join(lines_without_enclosure).translate(None, whitespace) + + +if __name__ == "__main__": + t = TestHolderOfKeyResponse() + t.setup_class() + t.test_hok_response_is_parsed() -- cgit v1.2.1 From 65af4be7a2002cb427170adc245d936c65feaa4e Mon Sep 17 00:00:00 2001 From: Alex Bublichenko Date: Fri, 24 May 2019 11:14:00 -0700 Subject: Clean up and fix tests for python 3.* versions --- tests/test_02_saml.py | 27 +++++++++++++------------- tests/test_93_hok.py | 54 ++++++++++++++++++++++++++++++--------------------- 2 files changed, 46 insertions(+), 35 deletions(-) diff --git a/tests/test_02_saml.py b/tests/test_02_saml.py index 2cdb5064..ec5f15d6 100644 --- a/tests/test_02_saml.py +++ b/tests/test_02_saml.py @@ -901,19 +901,20 @@ class TestSubjectConfirmation: key_info = sc.subject_confirmation_data.key_info assert len(key_info) == 1 assert len(key_info[0].x509_data) == 1 - assert key_info[0].x509_data[0].x509_certificate.text.strip() == """ -MIICITCCAYoCAQEwDQYJKoZIhvcNAQELBQAwWDELMAkGA1UEBhMCenoxCzAJBgNV -BAgMAnp6MQ0wCwYDVQQHDAR6enp6MQ4wDAYDVQQKDAVaenp6ejEOMAwGA1UECwwF -Wnp6enoxDTALBgNVBAMMBHRlc3QwIBcNMTkwNDEyMTk1MDM0WhgPMzAxODA4MTMx -OTUwMzRaMFgxCzAJBgNVBAYTAnp6MQswCQYDVQQIDAJ6ejENMAsGA1UEBwwEenp6 -ejEOMAwGA1UECgwFWnp6enoxDjAMBgNVBAsMBVp6enp6MQ0wCwYDVQQDDAR0ZXN0 -MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDHcj80WU/XBsd9FlyQmfjPUdfm -edhCFDd6TEQmZNNqP/UG+VkGa+BXjRIHMfic/WxPTbGhCjv68ci0UDNomUXagFex -LGNpkwa7+CRVtoc/1xgq+ySE6M4nhcCutScoxNvWNn5eSQ66i3U0sTv91MgsXxqE -dTaiZg0BIufEc3dueQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAGUV5B+USHvaRa8k -gCNJSuNpo6ARlv0ekrk8bbdNRBiEUdCMyoGJFfuM9K0zybX6Vr25wai3nvaog294 -Vx/jWjX2g5SDbjItH6VGy6C9GCGf1A07VxFRCfJn5tA9HuJjPKiE+g/BmrV5N4Ce -alzFxPHWYkNOzoRU8qI7OqUai1kL""".strip() + + expected_cert = """MIICITCCAYoCAQEwDQYJKoZIhvcNAQELBQAwWDELMAkGA1UEBhMCenoxCzAJBgNV + BAgMAnp6MQ0wCwYDVQQHDAR6enp6MQ4wDAYDVQQKDAVaenp6ejEOMAwGA1UECwwF + Wnp6enoxDTALBgNVBAMMBHRlc3QwIBcNMTkwNDEyMTk1MDM0WhgPMzAxODA4MTMx + OTUwMzRaMFgxCzAJBgNVBAYTAnp6MQswCQYDVQQIDAJ6ejENMAsGA1UEBwwEenp6 + ejEOMAwGA1UECgwFWnp6enoxDjAMBgNVBAsMBVp6enp6MQ0wCwYDVQQDDAR0ZXN0 + MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDHcj80WU/XBsd9FlyQmfjPUdfm + edhCFDd6TEQmZNNqP/UG+VkGa+BXjRIHMfic/WxPTbGhCjv68ci0UDNomUXagFex + LGNpkwa7+CRVtoc/1xgq+ySE6M4nhcCutScoxNvWNn5eSQ66i3U0sTv91MgsXxqE + dTaiZg0BIufEc3dueQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAGUV5B+USHvaRa8k + gCNJSuNpo6ARlv0ekrk8bbdNRBiEUdCMyoGJFfuM9K0zybX6Vr25wai3nvaog294 + Vx/jWjX2g5SDbjItH6VGy6C9GCGf1A07VxFRCfJn5tA9HuJjPKiE+g/BmrV5N4Ce + alzFxPHWYkNOzoRU8qI7OqUai1kL""".replace(' ', '') + assert key_info[0].x509_data[0].x509_certificate.text.strip() == expected_cert class TestSubject: diff --git a/tests/test_93_hok.py b/tests/test_93_hok.py index 085c930d..df740722 100644 --- a/tests/test_93_hok.py +++ b/tests/test_93_hok.py @@ -1,24 +1,12 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from contextlib import closing -from datetime import datetime -from dateutil import parser -from string import translate, whitespace -from saml2.authn_context import INTERNETPROTOCOLPASSWORD - -from saml2.server import Server from saml2.response import authn_response from saml2.config import config_factory from pathutils import dotname, full_path -# Example SAML response iwth 'holder-of-key' subject confirmtaions -# containing DER-base64 copies (without PEM enclosure) of test_1.crt and test_2.crt HOLDER_OF_KEY_RESPONSE_FILE = full_path("saml_hok.xml") -TEST_CERT_1 = full_path("test_1.crt") -TEST_CERT_2 = full_path("test_2.crt") - class TestHolderOfKeyResponse: def test_hok_response_is_parsed(self): @@ -34,17 +22,39 @@ class TestHolderOfKeyResponse: assert resp.get_subject() is not None assert len(resp.assertion.subject.subject_confirmation) == 2 - actual_certs = [sc.subject_confirmation_data.key_info[0].x509_data[0].x509_certificate.text.strip() + actual_hok_certs = [sc.subject_confirmation_data.key_info[0].x509_data[0].x509_certificate.text.strip() for sc in resp.assertion.subject.subject_confirmation] - expected_certs = [self._read_cert_without_pem_enclosure(TEST_CERT_1), - self._read_cert_without_pem_enclosure(TEST_CERT_2)] - assert actual_certs == expected_certs - - def _read_cert_without_pem_enclosure(self, path): - with open(path, 'r') as fp: - lines = fp.readlines() - lines_without_enclosure = lines[1:-1] - return ''.join(lines_without_enclosure).translate(None, whitespace) + assert actual_hok_certs == self._expected_hok_certs() + + def _expected_hok_certs(self): + certs = ["""MIICITCCAYoCAQEwDQYJKoZIhvcNAQELBQAwWDELMAkGA1UEBhMCenoxCzAJBgNV + BAgMAnp6MQ0wCwYDVQQHDAR6enp6MQ4wDAYDVQQKDAVaenp6ejEOMAwGA1UECwwF + Wnp6enoxDTALBgNVBAMMBHRlc3QwIBcNMTkwNDEyMTk1MDM0WhgPMzAxODA4MTMx + OTUwMzRaMFgxCzAJBgNVBAYTAnp6MQswCQYDVQQIDAJ6ejENMAsGA1UEBwwEenp6 + ejEOMAwGA1UECgwFWnp6enoxDjAMBgNVBAsMBVp6enp6MQ0wCwYDVQQDDAR0ZXN0 + MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDHcj80WU/XBsd9FlyQmfjPUdfm + edhCFDd6TEQmZNNqP/UG+VkGa+BXjRIHMfic/WxPTbGhCjv68ci0UDNomUXagFex + LGNpkwa7+CRVtoc/1xgq+ySE6M4nhcCutScoxNvWNn5eSQ66i3U0sTv91MgsXxqE + dTaiZg0BIufEc3dueQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAGUV5B+USHvaRa8k + gCNJSuNpo6ARlv0ekrk8bbdNRBiEUdCMyoGJFfuM9K0zybX6Vr25wai3nvaog294 + Vx/jWjX2g5SDbjItH6VGy6C9GCGf1A07VxFRCfJn5tA9HuJjPKiE+g/BmrV5N4Ce + alzFxPHWYkNOzoRU8qI7OqUai1kL""", + """MIICITCCAYoCAQEwDQYJKoZIhvcNAQELBQAwWDELMAkGA1UEBhMCenoxCzAJBgNV + BAgMAnp6MQ0wCwYDVQQHDAR6enp6MQ4wDAYDVQQKDAVaenp6ejEOMAwGA1UECwwF + Wnp6enoxDTALBgNVBAMMBHRlc3QwIBcNMTkwNDEyMTk1MDM0WhgPMzAxODA4MTMx + OTUwMzRaMFgxCzAJBgNVBAYTAnp6MQswCQYDVQQIDAJ6ejENMAsGA1UEBwwEenp6 + ejEOMAwGA1UECgwFWnp6enoxDjAMBgNVBAsMBVp6enp6MQ0wCwYDVQQDDAR0ZXN0 + MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDjW0kJM+4baWKtvO24ZsGXNvNK + KkwTMz7OW5Z6BRqhSOq2WA0c5NCpMk6rD8Z2OTFEolPojEjf8dVyd/Ds/hrjFKQv + 8wQgbdXLN51YTIsgd6h+hBJO+vzhl0PT4aT7M0JKo5ALtS6qk4tsworW2BnwyvsG + SAinwfeWt4t/b1J3kwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAFtj7WArQQBugmh/ + KQjjlfTQ5A052QeXfgTyO9vv1S6MRIi7qgiaEv49cGXnJv/TWbySkMKObPMUApjg + 6z8PqcxuShew5FCTkNvwhABFPiyu0fUj3e2FEPHfsBu76jz4ugtmhUqjqhzwFY9c + tnWRkkl6J0AjM3LnHOSgjNIclDZG"""] + for index, item in enumerate(certs): + item = item.replace(' ', '').replace('\n', '') + certs[index] = item + return certs if __name__ == "__main__": -- cgit v1.2.1 From 281d2e165b674b315529b60d145f1b57a7bdb08e Mon Sep 17 00:00:00 2001 From: Alex Bublichenko Date: Fri, 24 May 2019 15:29:28 -0700 Subject: Gracefully handle invalid HOK assertions --- src/saml2/response.py | 3 ++- tests/saml_hok_invalid.xml | 30 ++++++++++++++++++++++++++++++ tests/test_93_hok.py | 33 ++++++++++++++++++++++++--------- 3 files changed, 56 insertions(+), 10 deletions(-) create mode 100644 tests/saml_hok_invalid.xml diff --git a/src/saml2/response.py b/src/saml2/response.py index c16be47f..118f7fe0 100644 --- a/src/saml2/response.py +++ b/src/saml2/response.py @@ -726,7 +726,8 @@ class AuthnResponse(StatusResponse): return False has_keyinfo = False - for element in extension_elements_to_elements(data.key_info, + key_info = data.key_info or () + for element in extension_elements_to_elements(key_info, [samlp, saml, xenc, ds]): if isinstance(element, ds.KeyInfo): has_keyinfo = True diff --git a/tests/saml_hok_invalid.xml b/tests/saml_hok_invalid.xml new file mode 100644 index 00000000..53c9edb9 --- /dev/null +++ b/tests/saml_hok_invalid.xml @@ -0,0 +1,30 @@ + + + + https://idp:8443 + + + + + https://idp:8443 + + 57a0a35eefdb29ca8b4ab78d5a118117 + + + + + + + urn:oasis:names:tc:SAML:2.0:ac:classes:Password + + + + + testuser + + + + diff --git a/tests/test_93_hok.py b/tests/test_93_hok.py index df740722..dc6aac6e 100644 --- a/tests/test_93_hok.py +++ b/tests/test_93_hok.py @@ -1,23 +1,19 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from saml2.response import authn_response +from saml2.response import authn_response, VerificationError from saml2.config import config_factory from pathutils import dotname, full_path -HOLDER_OF_KEY_RESPONSE_FILE = full_path("saml_hok.xml") +HOLDER_OF_KEY_RESPONSE_FILE = full_path("saml_hok.xml") +INVALID_HOLDER_OF_KEY_RESPONSE_FILE = full_path("saml_hok_invalid.xml") class TestHolderOfKeyResponse: - def test_hok_response_is_parsed(self): + def test_valid_hok_response_is_parsed(self): """Verifies that response with 'holder-of-key' subject confirmations is parsed successfully.""" - conf = config_factory("idp", dotname("server_conf")) - resp = authn_response(conf, "https://sp:443/.auth/saml/login", asynchop=False, allow_unsolicited=True) - with open(HOLDER_OF_KEY_RESPONSE_FILE, 'r') as fp: - authn_response_xml = fp.read() - resp.loads(authn_response_xml, False) + resp = self._get_test_response(HOLDER_OF_KEY_RESPONSE_FILE) resp.do_not_verify = True - resp.parse_assertion() assert resp.get_subject() is not None @@ -56,6 +52,25 @@ class TestHolderOfKeyResponse: certs[index] = item return certs + def test_invalid_hok_response_fails_verification(self): + """Verifies that response with invalid 'holder-of-key' subject confirmations is parsed successfully.""" + resp = self._get_test_response(INVALID_HOLDER_OF_KEY_RESPONSE_FILE) + resp.do_not_verify = True + + try: + resp.parse_assertion() + assert False, "parse_assertion() did not fail as expected" + except VerificationError as e: + assert e is not None + + def _get_test_response(self, path): + conf = config_factory("idp", dotname("server_conf")) + resp = authn_response(conf, "https://sp:443/.auth/saml/login", asynchop=False, allow_unsolicited=True) + with open(path, 'r') as fp: + authn_response_xml = fp.read() + resp.loads(authn_response_xml, False) + return resp + if __name__ == "__main__": t = TestHolderOfKeyResponse() -- cgit v1.2.1 From a9037d21144d8d1659c1996c4bd237222301995b Mon Sep 17 00:00:00 2001 From: Alex Bublichenko Date: Tue, 28 May 2019 11:33:31 -0700 Subject: Use `extension_elements` to extract `KeyInfo` Instead of explicitly declaring `KeyInfo` as child of `SubjectConfirmationData`, use `extension_elements` to extract `KeyInfo` element(s). --- src/saml2/response.py | 5 ++--- src/saml2/saml.py | 5 ----- tests/test_02_saml.py | 5 +++-- tests/test_93_hok.py | 7 +++++-- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/saml2/response.py b/src/saml2/response.py index 118f7fe0..4c884c30 100644 --- a/src/saml2/response.py +++ b/src/saml2/response.py @@ -722,12 +722,11 @@ class AuthnResponse(StatusResponse): return True def _holder_of_key_confirmed(self, data): - if not data or not data.key_info: + if not data or not data.extension_elements: return False has_keyinfo = False - key_info = data.key_info or () - for element in extension_elements_to_elements(key_info, + for element in extension_elements_to_elements(data.extension_elements, [samlp, saml, xenc, ds]): if isinstance(element, ds.KeyInfo): has_keyinfo = True diff --git a/src/saml2/saml.py b/src/saml2/saml.py index 0d6728e5..bdb1ec60 100644 --- a/src/saml2/saml.py +++ b/src/saml2/saml.py @@ -482,12 +482,8 @@ class SubjectConfirmationDataType_(SamlBase): c_any = {"namespace": "##any", "processContents": "lax", "minOccurs": "0", "maxOccurs": "unbounded"} c_any_attribute = {"namespace": "##other", "processContents": "lax"} - c_children['{http://www.w3.org/2000/09/xmldsig#}KeyInfo'] = ('key_info', - [ds.KeyInfo]) - c_cardinality['key_info'] = {"min": 0, "max": 1} def __init__(self, - key_info=None, not_before=None, not_on_or_after=None, recipient=None, @@ -500,7 +496,6 @@ class SubjectConfirmationDataType_(SamlBase): text=text, extension_elements=extension_elements, extension_attributes=extension_attributes) - self.key_info = key_info self.not_before = not_before self.not_on_or_after = not_on_or_after self.recipient = recipient diff --git a/tests/test_02_saml.py b/tests/test_02_saml.py index ec5f15d6..b8083273 100644 --- a/tests/test_02_saml.py +++ b/tests/test_02_saml.py @@ -886,7 +886,8 @@ class TestSubjectConfirmation: assert sc.subject_confirmation_data.recipient == "recipient" assert sc.subject_confirmation_data.in_response_to == "responseID" assert sc.subject_confirmation_data.address == "127.0.0.1" - assert sc.subject_confirmation_data.key_info is None + key_info = sc.subject_confirmation_data.extensions_as_elements(ds.KeyInfo.c_tag, ds) + assert len(key_info) == 0 def testHolderOfKeyUsingTestData(self): """Test subject_confirmation_from_string() using test data for 'holder-of-key' SubjectConfirmation""" @@ -898,7 +899,7 @@ class TestSubjectConfirmation: assert sc.subject_confirmation_data.not_on_or_after == "2007-09-14T01:05:02Z" assert sc.subject_confirmation_data.recipient == "recipient" assert sc.subject_confirmation_data.in_response_to == "responseID" - key_info = sc.subject_confirmation_data.key_info + key_info = sc.subject_confirmation_data.extensions_as_elements(ds.KeyInfo.c_tag, ds) assert len(key_info) == 1 assert len(key_info[0].x509_data) == 1 diff --git a/tests/test_93_hok.py b/tests/test_93_hok.py index dc6aac6e..962d2be5 100644 --- a/tests/test_93_hok.py +++ b/tests/test_93_hok.py @@ -1,5 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +from saml2 import xmldsig as ds from saml2.response import authn_response, VerificationError from saml2.config import config_factory @@ -18,8 +19,10 @@ class TestHolderOfKeyResponse: assert resp.get_subject() is not None assert len(resp.assertion.subject.subject_confirmation) == 2 - actual_hok_certs = [sc.subject_confirmation_data.key_info[0].x509_data[0].x509_certificate.text.strip() - for sc in resp.assertion.subject.subject_confirmation] + key_infos = [sc.subject_confirmation_data.extensions_as_elements(ds.KeyInfo.c_tag, ds)[0] + for sc in resp.assertion.subject.subject_confirmation] + actual_hok_certs = [key_info_element.x509_data[0].x509_certificate.text.strip() + for key_info_element in key_infos] assert actual_hok_certs == self._expected_hok_certs() def _expected_hok_certs(self): -- cgit v1.2.1 From 477c4405575ab7ab5650743d73c9709a589f23b7 Mon Sep 17 00:00:00 2001 From: Ivan Kanakarakis Date: Wed, 29 May 2019 18:38:28 +0300 Subject: Format code Signed-off-by: Ivan Kanakarakis --- tests/test_02_saml.py | 34 +++++++++++-------- tests/test_93_hok.py | 92 +++++++++++++++++++++++++++++---------------------- 2 files changed, 72 insertions(+), 54 deletions(-) diff --git a/tests/test_02_saml.py b/tests/test_02_saml.py index b8083273..8ab6ca04 100644 --- a/tests/test_02_saml.py +++ b/tests/test_02_saml.py @@ -891,31 +891,37 @@ class TestSubjectConfirmation: def testHolderOfKeyUsingTestData(self): """Test subject_confirmation_from_string() using test data for 'holder-of-key' SubjectConfirmation""" + sc = saml.subject_confirmation_from_string( - saml2_data.TEST_HOLDER_OF_KEY_SUBJECT_CONFIRMATION) + saml2_data.TEST_HOLDER_OF_KEY_SUBJECT_CONFIRMATION + ) assert sc.verify() assert sc.method == saml.SCM_HOLDER_OF_KEY assert sc.subject_confirmation_data is not None assert sc.subject_confirmation_data.not_on_or_after == "2007-09-14T01:05:02Z" assert sc.subject_confirmation_data.recipient == "recipient" assert sc.subject_confirmation_data.in_response_to == "responseID" + key_info = sc.subject_confirmation_data.extensions_as_elements(ds.KeyInfo.c_tag, ds) assert len(key_info) == 1 assert len(key_info[0].x509_data) == 1 - expected_cert = """MIICITCCAYoCAQEwDQYJKoZIhvcNAQELBQAwWDELMAkGA1UEBhMCenoxCzAJBgNV - BAgMAnp6MQ0wCwYDVQQHDAR6enp6MQ4wDAYDVQQKDAVaenp6ejEOMAwGA1UECwwF - Wnp6enoxDTALBgNVBAMMBHRlc3QwIBcNMTkwNDEyMTk1MDM0WhgPMzAxODA4MTMx - OTUwMzRaMFgxCzAJBgNVBAYTAnp6MQswCQYDVQQIDAJ6ejENMAsGA1UEBwwEenp6 - ejEOMAwGA1UECgwFWnp6enoxDjAMBgNVBAsMBVp6enp6MQ0wCwYDVQQDDAR0ZXN0 - MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDHcj80WU/XBsd9FlyQmfjPUdfm - edhCFDd6TEQmZNNqP/UG+VkGa+BXjRIHMfic/WxPTbGhCjv68ci0UDNomUXagFex - LGNpkwa7+CRVtoc/1xgq+ySE6M4nhcCutScoxNvWNn5eSQ66i3U0sTv91MgsXxqE - dTaiZg0BIufEc3dueQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAGUV5B+USHvaRa8k - gCNJSuNpo6ARlv0ekrk8bbdNRBiEUdCMyoGJFfuM9K0zybX6Vr25wai3nvaog294 - Vx/jWjX2g5SDbjItH6VGy6C9GCGf1A07VxFRCfJn5tA9HuJjPKiE+g/BmrV5N4Ce - alzFxPHWYkNOzoRU8qI7OqUai1kL""".replace(' ', '') - assert key_info[0].x509_data[0].x509_certificate.text.strip() == expected_cert + expected_cert = ( + "MIICITCCAYoCAQEwDQYJKoZIhvcNAQELBQAwWDELMAkGA1UEBhMCenoxCzAJBgNV" + "BAgMAnp6MQ0wCwYDVQQHDAR6enp6MQ4wDAYDVQQKDAVaenp6ejEOMAwGA1UECwwF" + "Wnp6enoxDTALBgNVBAMMBHRlc3QwIBcNMTkwNDEyMTk1MDM0WhgPMzAxODA4MTMx" + "OTUwMzRaMFgxCzAJBgNVBAYTAnp6MQswCQYDVQQIDAJ6ejENMAsGA1UEBwwEenp6" + "ejEOMAwGA1UECgwFWnp6enoxDjAMBgNVBAsMBVp6enp6MQ0wCwYDVQQDDAR0ZXN0" + "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDHcj80WU/XBsd9FlyQmfjPUdfm" + "edhCFDd6TEQmZNNqP/UG+VkGa+BXjRIHMfic/WxPTbGhCjv68ci0UDNomUXagFex" + "LGNpkwa7+CRVtoc/1xgq+ySE6M4nhcCutScoxNvWNn5eSQ66i3U0sTv91MgsXxqE" + "dTaiZg0BIufEc3dueQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAGUV5B+USHvaRa8k" + "gCNJSuNpo6ARlv0ekrk8bbdNRBiEUdCMyoGJFfuM9K0zybX6Vr25wai3nvaog294" + "Vx/jWjX2g5SDbjItH6VGy6C9GCGf1A07VxFRCfJn5tA9HuJjPKiE+g/BmrV5N4Ce" + "alzFxPHWYkNOzoRU8qI7OqUai1kL" + ) + xcert = key_info[0].x509_data[0].x509_certificate + assert xcert.text.strip().replace("\n", "") == expected_cert class TestSubject: diff --git a/tests/test_93_hok.py b/tests/test_93_hok.py index 962d2be5..9d74f490 100644 --- a/tests/test_93_hok.py +++ b/tests/test_93_hok.py @@ -1,10 +1,14 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +from pathutils import dotname, full_path + +from pytest import raises + from saml2 import xmldsig as ds -from saml2.response import authn_response, VerificationError from saml2.config import config_factory +from saml2.response import VerificationError +from saml2.response import authn_response -from pathutils import dotname, full_path HOLDER_OF_KEY_RESPONSE_FILE = full_path("saml_hok.xml") INVALID_HOLDER_OF_KEY_RESPONSE_FILE = full_path("saml_hok_invalid.xml") @@ -16,43 +20,49 @@ class TestHolderOfKeyResponse: resp = self._get_test_response(HOLDER_OF_KEY_RESPONSE_FILE) resp.do_not_verify = True resp.parse_assertion() - assert resp.get_subject() is not None assert len(resp.assertion.subject.subject_confirmation) == 2 - key_infos = [sc.subject_confirmation_data.extensions_as_elements(ds.KeyInfo.c_tag, ds)[0] - for sc in resp.assertion.subject.subject_confirmation] - actual_hok_certs = [key_info_element.x509_data[0].x509_certificate.text.strip() - for key_info_element in key_infos] + + actual_hok_certs = [ + ki.x509_data[0].x509_certificate.text.strip() + for sc in resp.assertion.subject.subject_confirmation + for ki in sc.subject_confirmation_data.extensions_as_elements( + ds.KeyInfo.c_tag, ds + ) + ] assert actual_hok_certs == self._expected_hok_certs() def _expected_hok_certs(self): - certs = ["""MIICITCCAYoCAQEwDQYJKoZIhvcNAQELBQAwWDELMAkGA1UEBhMCenoxCzAJBgNV - BAgMAnp6MQ0wCwYDVQQHDAR6enp6MQ4wDAYDVQQKDAVaenp6ejEOMAwGA1UECwwF - Wnp6enoxDTALBgNVBAMMBHRlc3QwIBcNMTkwNDEyMTk1MDM0WhgPMzAxODA4MTMx - OTUwMzRaMFgxCzAJBgNVBAYTAnp6MQswCQYDVQQIDAJ6ejENMAsGA1UEBwwEenp6 - ejEOMAwGA1UECgwFWnp6enoxDjAMBgNVBAsMBVp6enp6MQ0wCwYDVQQDDAR0ZXN0 - MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDHcj80WU/XBsd9FlyQmfjPUdfm - edhCFDd6TEQmZNNqP/UG+VkGa+BXjRIHMfic/WxPTbGhCjv68ci0UDNomUXagFex - LGNpkwa7+CRVtoc/1xgq+ySE6M4nhcCutScoxNvWNn5eSQ66i3U0sTv91MgsXxqE - dTaiZg0BIufEc3dueQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAGUV5B+USHvaRa8k - gCNJSuNpo6ARlv0ekrk8bbdNRBiEUdCMyoGJFfuM9K0zybX6Vr25wai3nvaog294 - Vx/jWjX2g5SDbjItH6VGy6C9GCGf1A07VxFRCfJn5tA9HuJjPKiE+g/BmrV5N4Ce - alzFxPHWYkNOzoRU8qI7OqUai1kL""", - """MIICITCCAYoCAQEwDQYJKoZIhvcNAQELBQAwWDELMAkGA1UEBhMCenoxCzAJBgNV - BAgMAnp6MQ0wCwYDVQQHDAR6enp6MQ4wDAYDVQQKDAVaenp6ejEOMAwGA1UECwwF - Wnp6enoxDTALBgNVBAMMBHRlc3QwIBcNMTkwNDEyMTk1MDM0WhgPMzAxODA4MTMx - OTUwMzRaMFgxCzAJBgNVBAYTAnp6MQswCQYDVQQIDAJ6ejENMAsGA1UEBwwEenp6 - ejEOMAwGA1UECgwFWnp6enoxDjAMBgNVBAsMBVp6enp6MQ0wCwYDVQQDDAR0ZXN0 - MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDjW0kJM+4baWKtvO24ZsGXNvNK - KkwTMz7OW5Z6BRqhSOq2WA0c5NCpMk6rD8Z2OTFEolPojEjf8dVyd/Ds/hrjFKQv - 8wQgbdXLN51YTIsgd6h+hBJO+vzhl0PT4aT7M0JKo5ALtS6qk4tsworW2BnwyvsG - SAinwfeWt4t/b1J3kwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAFtj7WArQQBugmh/ - KQjjlfTQ5A052QeXfgTyO9vv1S6MRIi7qgiaEv49cGXnJv/TWbySkMKObPMUApjg - 6z8PqcxuShew5FCTkNvwhABFPiyu0fUj3e2FEPHfsBu76jz4ugtmhUqjqhzwFY9c - tnWRkkl6J0AjM3LnHOSgjNIclDZG"""] - for index, item in enumerate(certs): - item = item.replace(' ', '').replace('\n', '') - certs[index] = item + certs = [ + ( + "MIICITCCAYoCAQEwDQYJKoZIhvcNAQELBQAwWDELMAkGA1UEBhMCenoxCzAJBgNV" + "BAgMAnp6MQ0wCwYDVQQHDAR6enp6MQ4wDAYDVQQKDAVaenp6ejEOMAwGA1UECwwF" + "Wnp6enoxDTALBgNVBAMMBHRlc3QwIBcNMTkwNDEyMTk1MDM0WhgPMzAxODA4MTMx" + "OTUwMzRaMFgxCzAJBgNVBAYTAnp6MQswCQYDVQQIDAJ6ejENMAsGA1UEBwwEenp6" + "ejEOMAwGA1UECgwFWnp6enoxDjAMBgNVBAsMBVp6enp6MQ0wCwYDVQQDDAR0ZXN0" + "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDHcj80WU/XBsd9FlyQmfjPUdfm" + "edhCFDd6TEQmZNNqP/UG+VkGa+BXjRIHMfic/WxPTbGhCjv68ci0UDNomUXagFex" + "LGNpkwa7+CRVtoc/1xgq+ySE6M4nhcCutScoxNvWNn5eSQ66i3U0sTv91MgsXxqE" + "dTaiZg0BIufEc3dueQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAGUV5B+USHvaRa8k" + "gCNJSuNpo6ARlv0ekrk8bbdNRBiEUdCMyoGJFfuM9K0zybX6Vr25wai3nvaog294" + "Vx/jWjX2g5SDbjItH6VGy6C9GCGf1A07VxFRCfJn5tA9HuJjPKiE+g/BmrV5N4Ce" + "alzFxPHWYkNOzoRU8qI7OqUai1kL" + ), + ( + "MIICITCCAYoCAQEwDQYJKoZIhvcNAQELBQAwWDELMAkGA1UEBhMCenoxCzAJBgNV" + "BAgMAnp6MQ0wCwYDVQQHDAR6enp6MQ4wDAYDVQQKDAVaenp6ejEOMAwGA1UECwwF" + "Wnp6enoxDTALBgNVBAMMBHRlc3QwIBcNMTkwNDEyMTk1MDM0WhgPMzAxODA4MTMx" + "OTUwMzRaMFgxCzAJBgNVBAYTAnp6MQswCQYDVQQIDAJ6ejENMAsGA1UEBwwEenp6" + "ejEOMAwGA1UECgwFWnp6enoxDjAMBgNVBAsMBVp6enp6MQ0wCwYDVQQDDAR0ZXN0" + "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDjW0kJM+4baWKtvO24ZsGXNvNK" + "KkwTMz7OW5Z6BRqhSOq2WA0c5NCpMk6rD8Z2OTFEolPojEjf8dVyd/Ds/hrjFKQv" + "8wQgbdXLN51YTIsgd6h+hBJO+vzhl0PT4aT7M0JKo5ALtS6qk4tsworW2BnwyvsG" + "SAinwfeWt4t/b1J3kwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAFtj7WArQQBugmh/" + "KQjjlfTQ5A052QeXfgTyO9vv1S6MRIi7qgiaEv49cGXnJv/TWbySkMKObPMUApjg" + "6z8PqcxuShew5FCTkNvwhABFPiyu0fUj3e2FEPHfsBu76jz4ugtmhUqjqhzwFY9c" + "tnWRkkl6J0AjM3LnHOSgjNIclDZG" + ), + ] return certs def test_invalid_hok_response_fails_verification(self): @@ -60,16 +70,18 @@ class TestHolderOfKeyResponse: resp = self._get_test_response(INVALID_HOLDER_OF_KEY_RESPONSE_FILE) resp.do_not_verify = True - try: + with raises(VerificationError): resp.parse_assertion() - assert False, "parse_assertion() did not fail as expected" - except VerificationError as e: - assert e is not None def _get_test_response(self, path): conf = config_factory("idp", dotname("server_conf")) - resp = authn_response(conf, "https://sp:443/.auth/saml/login", asynchop=False, allow_unsolicited=True) - with open(path, 'r') as fp: + resp = authn_response( + conf, + "https://sp:443/.auth/saml/login", + asynchop=False, + allow_unsolicited=True, + ) + with open(path, "r") as fp: authn_response_xml = fp.read() resp.loads(authn_response_xml, False) return resp -- cgit v1.2.1