Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-pysaml2 for openSUSE:Factory checked in at 2021-07-03 20:51:01 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-pysaml2 (Old) and /work/SRC/openSUSE:Factory/.python-pysaml2.new.2625 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pysaml2" Sat Jul 3 20:51:01 2021 rev:25 rq:903859 version:7.0.1 Changes: -------- --- /work/SRC/openSUSE:Factory/python-pysaml2/python-pysaml2.changes 2021-05-03 22:08:04.884513970 +0200 +++ /work/SRC/openSUSE:Factory/.python-pysaml2.new.2625/python-pysaml2.changes 2021-07-03 20:51:31.060460104 +0200 @@ -1,0 +2,29 @@ +Fri Jul 2 20:25:49 UTC 2021 - Michael Str??der <[email protected]> + +- removed obsolete 0001-Always-use-base64.encodebytes-base64.encodestring-ha.patch +- Update to 7.0.1: + * 7.0.1 (2021-05-20) + - Preserve order of response bindings on IdP-initiated logout + - Fix use of expected binding on SP logout + * 7.0.0 (2021-05-18) + - **BREAKING** Replace encryption method rsa-1_5 with rsa-oaep-mgf1p + - Add documentation next to the code + * 6.5.2 (2021-05-18) + - Add shibmd_scopes metadata extractor + - Allow the Issuer element on a Response to be missing + - Respect the preferred_binding configuration for the single_logout_service + - Fix logout signature flags for redirect, post and soap requests + - Respect the logout_requests_signed configuration option + - Fix crash when applying policy on RequestedAttribute without a friendlyName + - Correctly validate IssueInstant + - Correctly handle AudienceRestriction elements with no value + - Raise InvalidAssertion exception when assertion requirements are not met + - Raise SAMLError on failure to parse a metadata file + - Raise StatusInvalidAuthnResponseStatement when the AuthnStatement is not valid + - Handle all forms of ACS endpoint specifications + - tests: Always use base64.encodebytes; base64.encodestring has been dropped + - build: Set minimum version needed for xmlschema + - docs: Update Travis CI badge from travis-ci.org to travis-ci.com + - examples: Fix example code + +------------------------------------------------------------------- Old: ---- 0001-Always-use-base64.encodebytes-base64.encodestring-ha.patch v6.5.1.tar.gz New: ---- v7.0.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-pysaml2.spec ++++++ --- /var/tmp/diff_new_pack.8cAo5s/_old 2021-07-03 20:51:31.568456198 +0200 +++ /var/tmp/diff_new_pack.8cAo5s/_new 2021-07-03 20:51:31.572456167 +0200 @@ -20,14 +20,12 @@ %global modname pysaml2 %global skip_python2 1 Name: python-pysaml2 -Version: 6.5.1 +Version: 7.0.1 Release: 0 Summary: Python implementation of SAML Version 2 to be used in a WSGI environment License: Apache-2.0 URL: https://github.com/IdentityPython/pysaml2 Source: https://github.com/IdentityPython/pysaml2/archive/v%{version}.tar.gz -# PATCH-FIX-UPSTREAM: https://github.com/IdentityPython/pysaml2/pull/797 -Patch1: 0001-Always-use-base64.encodebytes-base64.encodestring-ha.patch BuildRequires: %{python_module Paste} BuildRequires: %{python_module cryptography >= 1.4} BuildRequires: %{python_module dbm} @@ -78,7 +76,6 @@ %prep %setup -q -n %{modname}-%{version} -%autopatch -p1 # delete shebang of files not in executable path find src/ -name '*.py' -print0 | xargs -0 sed -i '1s/#!.*$//' ++++++ v6.5.1.tar.gz -> v7.0.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pysaml2-6.5.1/CHANGELOG.md new/pysaml2-7.0.1/CHANGELOG.md --- old/pysaml2-6.5.1/CHANGELOG.md 2021-01-21 00:08:02.000000000 +0100 +++ new/pysaml2-7.0.1/CHANGELOG.md 2021-05-20 18:56:31.000000000 +0200 @@ -1,6 +1,38 @@ # Changelog +## 7.0.1 (2021-05-20) + +- Preserve order of response bindings on IdP-initiated logout +- Fix use of expected binding on SP logout + + +## 7.0.0 (2021-05-18) + +- **BREAKING** Replace encryption method rsa-1_5 with rsa-oaep-mgf1p +- Add documentation next to the code + + +## 6.5.2 (2021-05-18) + +- Add shibmd_scopes metadata extractor +- Allow the Issuer element on a Response to be missing +- Respect the preferred_binding configuration for the single_logout_service +- Fix logout signature flags for redirect, post and soap requests +- Respect the logout_requests_signed configuration option +- Fix crash when applying policy on RequestedAttribute without a friendlyName +- Correctly validate IssueInstant +- Correctly handle AudienceRestriction elements with no value +- Raise InvalidAssertion exception when assertion requirements are not met +- Raise SAMLError on failure to parse a metadata file +- Raise StatusInvalidAuthnResponseStatement when the AuthnStatement is not valid +- Handle all forms of ACS endpoint specifications +- tests: Always use base64.encodebytes; base64.encodestring has been dropped +- build: Set minimum version needed for xmlschema +- docs: Update Travis CI badge from travis-ci.org to travis-ci.com +- examples: Fix example code + + ## 6.5.1 (2021-01-21) - Fix the parser to take into account both the xs and xsd namespace prefixes diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pysaml2-6.5.1/README.rst new/pysaml2-7.0.1/README.rst --- old/pysaml2-6.5.1/README.rst 2021-01-21 00:08:02.000000000 +0100 +++ new/pysaml2-7.0.1/README.rst 2021-05-20 18:56:31.000000000 +0200 @@ -5,8 +5,8 @@ :Version: see VERSION_ :Documentation: https://pysaml2.readthedocs.io/ -.. image:: https://api.travis-ci.org/IdentityPython/pysaml2.png?branch=master - :target: https://travis-ci.org/IdentityPython/pysaml2 +.. image:: https://api.travis-ci.com/IdentityPython/pysaml2.png?branch=master + :target: https://travis-ci.com/IdentityPython/pysaml2 .. image:: https://img.shields.io/pypi/pyversions/pysaml2.svg :target: https://pypi.org/project/pysaml2/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pysaml2-6.5.1/VERSION new/pysaml2-7.0.1/VERSION --- old/pysaml2-6.5.1/VERSION 2021-01-21 00:08:02.000000000 +0100 +++ new/pysaml2-7.0.1/VERSION 2021-05-20 18:56:31.000000000 +0200 @@ -1 +1 @@ -6.5.1 +7.0.1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pysaml2-6.5.1/docs/howto/config.rst new/pysaml2-7.0.1/docs/howto/config.rst --- old/pysaml2-6.5.1/docs/howto/config.rst 2021-01-21 00:08:02.000000000 +0100 +++ new/pysaml2-7.0.1/docs/howto/config.rst 2021-05-20 18:56:31.000000000 +0200 @@ -400,6 +400,7 @@ * assertion_id_request_service * artifact_resolution_service * attribute_consuming_service +* single_logout_service service diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pysaml2-6.5.1/example/idp2/idp.py new/pysaml2-7.0.1/example/idp2/idp.py --- old/pysaml2-6.5.1/example/idp2/idp.py 2021-01-21 00:08:02.000000000 +0100 +++ new/pysaml2-7.0.1/example/idp2/idp.py 2021-05-20 18:56:31.000000000 +0200 @@ -999,7 +999,7 @@ args.sign, ) start_response("200 OK", [("Content-Type", "text/xml")]) - return metadata + return [metadata] except Exception as ex: logger.error("An error occured while creating metadata: %s", ex.message) return not_found(environ, start_response) @@ -1041,7 +1041,7 @@ path = environ.get("PATH_INFO", "").lstrip("/") - if path == "metadata": + if path == "idp.xml": return metadata(environ, start_response) kaka = environ.get("HTTP_COOKIE", None) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pysaml2-6.5.1/setup.cfg new/pysaml2-7.0.1/setup.cfg --- old/pysaml2-6.5.1/setup.cfg 2021-01-21 00:08:02.000000000 +0100 +++ new/pysaml2-7.0.1/setup.cfg 2021-05-20 18:56:31.000000000 +0200 @@ -54,7 +54,7 @@ requests >= 1.0.0 six importlib_resources - xmlschema + xmlschema >= 1.2.1 [options.packages.find] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pysaml2-6.5.1/src/saml2/__init__.py new/pysaml2-7.0.1/src/saml2/__init__.py --- old/pysaml2-6.5.1/src/saml2/__init__.py 2021-01-21 00:08:02.000000000 +0100 +++ new/pysaml2-7.0.1/src/saml2/__init__.py 2021-05-20 18:56:31.000000000 +0200 @@ -60,12 +60,20 @@ DECISION_TYPE_INDETERMINATE = "Indeterminate" VERSION = "2.0" - +# http://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf +# The specification was later updated with errata, and the new version is here: +# http://www.oasis-open.org/committees/download.php/56779/sstc-saml-bindings-errata-2.0-wd-06.pdf +# parse a SOAP header, make a SOAP request, and receive a SOAP response BINDING_SOAP = 'urn:oasis:names:tc:SAML:2.0:bindings:SOAP' +# parse a PAOS header, make a PAOS request, and receive a PAOS response BINDING_PAOS = 'urn:oasis:names:tc:SAML:2.0:bindings:PAOS' +# URI encoded messages BINDING_HTTP_REDIRECT = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect' +# HTML encoded messages BINDING_HTTP_POST = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST' +# sensitive messages are transported over a backchannel BINDING_HTTP_ARTIFACT = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact' +# as uri response encoded message BINDING_URI = 'urn:oasis:names:tc:SAML:2.0:bindings:URI' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pysaml2-6.5.1/src/saml2/assertion.py new/pysaml2-7.0.1/src/saml2/assertion.py --- old/pysaml2-6.5.1/src/saml2/assertion.py 2021-01-21 00:08:02.000000000 +0100 +++ new/pysaml2-7.0.1/src/saml2/assertion.py 2021-05-20 18:56:31.000000000 +0200 @@ -454,7 +454,16 @@ def post_entity_categories(maps, sp_entity_id=None, mds=None, required=None): restrictions = {} - required = [d['friendly_name'].lower() for d in (required or [])] + required_friendly_names = [ + d.get('friendly_name') or get_local_name( + acs=self.acs, attr=d['name'], name_format=d['name_format'] + ) + for d in (required or []) + ] + required = [ + friendly_name.lower() + for friendly_name in required_friendly_names + ] if mds: ecs = mds.entity_categories(sp_entity_id) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pysaml2-6.5.1/src/saml2/client.py new/pysaml2-7.0.1/src/saml2/client.py --- old/pysaml2-6.5.1/src/saml2/client.py 2021-01-21 00:08:02.000000000 +0100 +++ new/pysaml2-7.0.1/src/saml2/client.py 2021-05-20 18:56:31.000000000 +0200 @@ -251,85 +251,122 @@ not_done = entity_ids[:] responses = {} + bindings_slo_preferred = self.config.preferred_binding["single_logout_service"] + for entity_id in entity_ids: logger.debug("Logout from '%s'", entity_id) - # for all where I can use the SOAP binding, do those first - for binding in [BINDING_SOAP, BINDING_HTTP_POST, BINDING_HTTP_REDIRECT]: - if expected_binding and binding != expected_binding: - continue - - try: - srvs = self.metadata.single_logout_service( - entity_id, binding, "idpsso" - ) - except: - srvs = None - - if not srvs: - logger.debug("No SLO '%s' service", binding) - continue - - destination = next(locations(srvs), None) - logger.info("destination to provider: %s", destination) - - try: - session_info = self.users.get_info_from( - name_id, entity_id, False - ) - session_indexes = [session_info['session_index']] - except KeyError: - session_indexes = None - - sign_post = False if binding == BINDING_HTTP_REDIRECT else sign - sign_redirect = False if binding == BINDING_HTTP_POST and sign else sign - - req_id, request = self.create_logout_request( - destination, - entity_id, - name_id=name_id, - reason=reason, - expire=expire, - session_indexes=session_indexes, - sign=sign_post, - sign_alg=sign_alg, - digest_alg=digest_alg, + + bindings_slo_supported = self.metadata.single_logout_service( + entity_id=entity_id, typ="idpsso" + ) + bindings_slo_preferred_and_supported = ( + binding + for binding in bindings_slo_preferred + if binding in bindings_slo_supported + ) + bindings_slo_choices = filter( + lambda x: x, + ( + expected_binding, + *bindings_slo_preferred_and_supported, + *bindings_slo_supported, ) + ) + binding = next(bindings_slo_choices, None) + if not binding: + logger.info( + { + "message": "Entity does not support SLO", + "entity": entity_id, + } + ) + continue - relay_state = self._relay_state(req_id) - http_info = self.apply_binding( - binding, - str(request), - destination, - relay_state, - sign=sign_redirect, - sigalg=sign_alg, + service_info = bindings_slo_supported[binding] + service_location = next(locations(service_info), None) + if not service_location: + logger.info( + { + "message": "Entity SLO service does not have a location", + "entity": entity_id, + "service_location": service_location, + } ) + continue - if binding == BINDING_SOAP: - response = self.send(**http_info) - if response and response.status_code == 200: - not_done.remove(entity_id) - response = response.text - logger.info("Response: %s", response) - res = self.parse_logout_request_response(response, binding) - responses[entity_id] = res - else: - logger.info("NOT OK response from %s", destination) + session_info = self.users.get_info_from(name_id, entity_id, False) + session_index = session_info.get('session_index') + session_indexes = [session_index] if session_index else None + + sign = sign if sign is not None else self.logout_requests_signed + sign_post = sign and ( + binding == BINDING_HTTP_POST or binding == BINDING_SOAP + ) + sign_redirect = sign and binding == BINDING_HTTP_REDIRECT + + log_report = { + "message": "Invoking SLO on entity", + "entity": entity_id, + "binding": binding, + "location": service_location, + "session_indexes": session_indexes, + "sign": sign, + } + logger.info(log_report) + + req_id, request = self.create_logout_request( + service_location, + entity_id, + name_id=name_id, + reason=reason, + expire=expire, + session_indexes=session_indexes, + sign=sign_post, + sign_alg=sign_alg, + digest_alg=digest_alg, + ) + relay_state = self._relay_state(req_id) + http_info = self.apply_binding( + binding, + str(request), + service_location, + relay_state, + sign=sign_redirect, + sigalg=sign_alg, + ) + + if binding == BINDING_SOAP: + response = self.send(**http_info) + if response and response.status_code == 200: + not_done.remove(entity_id) + response_text = response.text + log_report_response = { + **log_report, + "message": "Response from SLO service", + "response_text": response_text, + } + logger.debug(log_report_response) + res = self.parse_logout_request_response(response_text, binding) + responses[entity_id] = res else: - self.state[req_id] = { - "entity_id": entity_id, - "operation": "SLO", - "entity_ids": entity_ids, - "name_id": code(name_id), - "reason": reason, - "not_on_or_after": expire, - "sign": sign, + log_report_response = { + **log_report, + "message": "Bad status_code response from SLO service", + "status_code": (response and response.status_code), } - responses[entity_id] = (binding, http_info) - not_done.remove(entity_id) - - # only try one binding - break + logger.info(log_report_response) + else: + self.state[req_id] = { + "entity_id": entity_id, + "operation": "SLO", + "entity_ids": entity_ids, + "name_id": code(name_id), + "reason": reason, + "not_on_or_after": expire, + "sign": sign, + } + responses[entity_id] = (binding, http_info) + not_done.remove(entity_id) if not_done: # upstream should try later @@ -629,12 +666,11 @@ status = status_message_factory("Wrong user", STATUS_UNKNOWN_PRINCIPAL) - if binding == BINDING_SOAP: - response_bindings = [BINDING_SOAP] - elif binding in [BINDING_HTTP_POST, BINDING_HTTP_REDIRECT]: - response_bindings = [BINDING_HTTP_POST, BINDING_HTTP_REDIRECT] - else: - response_bindings = self.config.preferred_binding["single_logout_service"] + response_bindings = { + BINDING_SOAP: [BINDING_SOAP], + BINDING_HTTP_POST: [BINDING_HTTP_POST, BINDING_HTTP_REDIRECT], + BINDING_HTTP_REDIRECT: [BINDING_HTTP_REDIRECT, BINDING_HTTP_POST], + }.get(binding) if sign is None: sign = self.logout_responses_signed diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pysaml2-6.5.1/src/saml2/config.py new/pysaml2-7.0.1/src/saml2/config.py --- old/pysaml2-6.5.1/src/saml2/config.py 2021-01-21 00:08:02.000000000 +0100 +++ new/pysaml2-7.0.1/src/saml2/config.py 2021-05-20 18:56:31.000000000 +0200 @@ -404,7 +404,12 @@ if endps and service in endps: for endpspec in endps[service]: try: - endp, bind = endpspec + # endspec sometime is str, sometime is a tuple + if type(endpspec) in (tuple, list): + # slice prevents 3-tuple, eg: sp's assertion_consumer_service + endp, bind = endpspec[0:2] + else: + endp, bind = endpspec if binding is None or bind == binding: spec.append(endp) except ValueError: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pysaml2-6.5.1/src/saml2/data/templates/template_enc.xml new/pysaml2-7.0.1/src/saml2/data/templates/template_enc.xml --- old/pysaml2-6.5.1/src/saml2/data/templates/template_enc.xml 2021-01-21 00:08:02.000000000 +0100 +++ new/pysaml2-7.0.1/src/saml2/data/templates/template_enc.xml 2021-05-20 18:56:31.000000000 +0200 @@ -2,12 +2,10 @@ <EncryptedData xmlns="http://www.w3.org/2001/04/xmlenc#" Type="http://www.w3.org/2001/04/xmlenc#Element"> - <EncryptionMethod Algorithm= - "http://www.w3.org/2001/04/xmlenc#tripledes-cbc"/> + <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc"/> <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#"> <EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#"> - <EncryptionMethod Algorithm= - "http://www.w3.org/2001/04/xmlenc#rsa-1_5"/> + <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"/> <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#"> <KeyName/> </KeyInfo> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pysaml2-6.5.1/src/saml2/entity.py new/pysaml2-7.0.1/src/saml2/entity.py --- old/pysaml2-6.5.1/src/saml2/entity.py 2021-01-21 00:08:02.000000000 +0100 +++ new/pysaml2-7.0.1/src/saml2/entity.py 2021-05-20 18:56:31.000000000 +0200 @@ -65,6 +65,7 @@ from saml2.sigver import SigverError from saml2.sigver import SignatureError from saml2.sigver import make_temp +from saml2.sigver import get_pem_wrapped_unwrapped from saml2.sigver import pre_encryption_part from saml2.sigver import pre_signature_part from saml2.sigver import pre_encrypt_assertion @@ -241,7 +242,11 @@ :return: A dictionary """ - # XXX sig-allowed should be configurable + # XXX SIG_ALLOWED_ALG should be configurable + # XXX should_sign stems from authn_requests_signed and sign_response + # XXX based on the type of the entity + # XXX but should also take into account the type of message (Authn/Logout/etc) + # XXX should_sign should be split and the exact config options should be checked sign = sign if sign is not None else self.should_sign sign_alg = sigalg or self.signing_algorithm if sign_alg not in [long_name for short_name, long_name in SIG_ALLOWED_ALG]: @@ -300,7 +305,7 @@ sfunc = getattr(self.metadata, service) - if bindings is None: + if not bindings: if request and request.protocol_binding: bindings = [request.protocol_binding] else: @@ -650,19 +655,19 @@ _certs = self.metadata.certs(sp_entity_id, "any", "encryption") exception = None for _cert in _certs: + wrapped_cert, unwrapped_cert = get_pem_wrapped_unwrapped(_cert) try: - begin_cert = "-----BEGIN CERTIFICATE-----\n" - end_cert = "\n-----END CERTIFICATE-----\n" - if begin_cert not in _cert: - _cert = "%s%s" % (begin_cert, _cert) - if end_cert not in _cert: - _cert = "%s%s" % (_cert, end_cert) - tmp = make_temp(_cert.encode('ascii'), - decode=False, - delete_tmpfiles=self.config.delete_tmpfiles) - response = self.sec.encrypt_assertion(response, tmp.name, - pre_encryption_part(), - node_xpath=node_xpath) + tmp = make_temp( + wrapped_cert.encode('ascii'), + decode=False, + delete_tmpfiles=self.config.delete_tmpfiles, + ) + response = self.sec.encrypt_assertion( + response, + tmp.name, + pre_encryption_part(encrypt_cert=unwrapped_cert), + node_xpath=node_xpath, + ) return response except Exception as ex: exception = ex diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pysaml2-6.5.1/src/saml2/mdstore.py new/pysaml2-7.0.1/src/saml2/mdstore.py --- old/pysaml2-6.5.1/src/saml2/mdstore.py 2021-01-21 00:08:02.000000000 +0100 +++ new/pysaml2-7.0.1/src/saml2/mdstore.py 2021-05-20 18:56:31.000000000 +0200 @@ -7,12 +7,13 @@ import sys from itertools import chain from warnings import warn as _warn - from hashlib import sha1 from os.path import isfile from os.path import join +from re import compile as regex_compile import requests + import six from saml2 import md @@ -24,7 +25,6 @@ from saml2 import BINDING_HTTP_REDIRECT from saml2 import BINDING_HTTP_POST from saml2 import BINDING_SOAP - from saml2.httpbase import HTTPBase from saml2.extension.idpdisc import BINDING_DISCO from saml2.extension.idpdisc import DiscoveryResponse @@ -59,6 +59,8 @@ from saml2.extension.mdrpi import NAMESPACE as NS_MDRPI from saml2.extension.mdrpi import RegistrationInfo from saml2.extension.mdrpi import RegistrationPolicy +from saml2.extension.shibmd import NAMESPACE as NS_SHIBMD +from saml2.extension.shibmd import Scope logger = logging.getLogger(__name__) @@ -84,6 +86,7 @@ "service_nameid_mapping": "{ns}&{tag}".format(ns=NS_MD, tag=NameIDMappingService.c_tag), "mdrpi_registration_info": "{ns}&{tag}".format(ns=NS_MDRPI, tag=RegistrationInfo.c_tag), "mdrpi_registration_policy": "{ns}&{tag}".format(ns=NS_MDRPI, tag=RegistrationPolicy.c_tag), + "shibmd_scope": "{ns}&{tag}".format(ns=NS_SHIBMD, tag=Scope.c_tag) } ENTITY_CATEGORY = "http://macedir.org/entity-category" @@ -612,7 +615,10 @@ self.entity[entity_descr.entity_id] = _ent def parse(self, xmlstr): - self.entities_descr = md.entities_descriptor_from_string(xmlstr) + try: + self.entities_descr = md.entities_descriptor_from_string(xmlstr) + except Exception as e: + raise SAMLError(f'Failed to parse metadata file: {self.filename}') from e if not self.entities_descr: self.entity_descr = md.entity_descriptor_from_string(xmlstr) @@ -1227,8 +1233,6 @@ # IDP + SP if typ is None: raise AttributeError("Missing type specification") - if binding is None: - binding = BINDING_HTTP_REDIRECT return self.service(entity_id, "%s_descriptor" % typ, "single_logout_service", binding) @@ -1477,6 +1481,41 @@ ) return elements + def sbibmd_scopes(self, entity_id, typ=None): + try: + md = self[entity_id] + except KeyError: + md = {} + + descriptor_scopes = ( + { + "regexp": is_regexp, + "text": regex_compile(text) if is_regexp else text, + } + for elem in md.get("extensions", {}).get("extension_elements", []) + if elem.get("__class__") == classnames["shibmd_scope"] + for is_regexp, text in [ + (elem.get("regexp", "").lower() == "true", elem.get("text", "")), + ] + ) + + services_of_type = md.get(typ) or [] + services_of_type_scopes = ( + { + "regexp": is_regexp, + "text": regex_compile(text) if is_regexp else text, + } + for srv in services_of_type + for elem in srv.get("extensions", {}).get("extension_elements", []) + if elem.get("__class__") == classnames["shibmd_scope"] + for is_regexp, text in [ + (elem.get("regexp", "").lower() == "true", elem.get("text", "")), + ] + ) + + scopes = chain(descriptor_scopes, services_of_type_scopes) + return scopes + def mdui_uiinfo(self, entity_id): try: data = self[entity_id] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pysaml2-6.5.1/src/saml2/response.py new/pysaml2-7.0.1/src/saml2/response.py --- old/pysaml2-6.5.1/src/saml2/response.py 2021-01-21 00:08:02.000000000 +0100 +++ new/pysaml2-7.0.1/src/saml2/response.py 2021-05-20 18:56:31.000000000 +0200 @@ -67,6 +67,10 @@ pass +class InvalidAssertion(SAMLError): + pass + + class DecryptionFailed(SAMLError): pass @@ -94,6 +98,9 @@ class StatusInvalidAttrNameOrValue(StatusError): pass +class StatusInvalidAuthnResponseStatement(StatusError): + pass + class StatusInvalidNameidPolicy(StatusError): pass @@ -209,10 +216,10 @@ if not restriction.audience: continue for audience in restriction.audience: - if audience.text.strip() == myself: + if audience.text and audience.text.strip() == myself: return True else: - logger.debug("AudienceRestriction - One condition not satisfied: %s != %s" % (audience.text.strip(), myself)) + logger.debug("AudienceRestriction - One condition not satisfied: {} != {}".format(audience.text, myself)) logger.debug("AudienceRestrictions not satisfied!") return False @@ -428,7 +435,12 @@ self.response = mold.response def issuer(self): - return self.response.issuer.text.strip() + issuer_value = ( + self.response.issuer.text + if self.response.issuer is not None + else "" + ).strip() + return issuer_value class LogoutResponse(StatusResponse): @@ -920,7 +932,7 @@ n_assertions = len(self.response.assertion) n_assertions_enc = len(self.response.encrypted_assertion) if n_assertions != 1 and n_assertions_enc != 1 and self.assertion is None: - raise Exception( + raise InvalidAssertion( "Invalid number of assertions in Response: {n}".format( n=n_assertions+n_assertions_enc ) @@ -1037,7 +1049,7 @@ logger.error("Verification error on the response: %s", err) raise else: - if res is None: + if not res: return None if not isinstance(self.response, samlp.Response): @@ -1099,12 +1111,16 @@ return {"name_id": self.name_id, "came_from": self.came_from, "issuer": self.issuer(), "not_on_or_after": nooa, "authz_decision_info": self.authz_decision_info()} - else: + elif getattr(self.assertion, 'authn_statement', None): authn_statement = self.assertion.authn_statement[0] return {"ava": self.ava, "name_id": self.name_id, "came_from": self.came_from, "issuer": self.issuer(), "not_on_or_after": nooa, "authn_info": self.authn_info(), "session_index": authn_statement.session_index} + else: + raise StatusInvalidAuthnResponseStatement( + "The Authn Response Statement is not valid" + ) def __str__(self): return self.xmlstr diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pysaml2-6.5.1/src/saml2/saml.py new/pysaml2-7.0.1/src/saml2/saml.py --- old/pysaml2-6.5.1/src/saml2/saml.py 2021-01-21 00:08:02.000000000 +0100 +++ new/pysaml2-7.0.1/src/saml2/saml.py 2021-05-20 18:56:31.000000000 +0200 @@ -3,6 +3,17 @@ # # Generated Mon May 2 14:23:33 2011 by parse_xsd.py version 0.4. # +# A summary of available specifications can be found at: +# https://wiki.oasis-open.org/security/FrontPage +# +# saml core specifications to be found at: +# if any question arise please query the following pdf. +# http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf +# The specification was later updated with errata, and the new version is here: +# https://www.oasis-open.org/committees/download.php/56776/sstc-saml-core-errata-2.0-wd-07.pdf +# + + import base64 from saml2.validate import valid_ipv4, MustValueError @@ -17,32 +28,53 @@ from saml2 import xmldsig as ds from saml2 import xmlenc as xenc +# authentication information fields NAMESPACE = 'urn:oasis:names:tc:SAML:2.0:assertion' -XSI_NAMESPACE = 'http://www.w3.org/2001/XMLSchema-instance' +# xmlschema definition +XSD = "xs" +# xmlschema templates and extensions XS_NAMESPACE = 'http://www.w3.org/2001/XMLSchema' - +# xmlschema-instance, which contains several builtin attributes +XSI_NAMESPACE = 'http://www.w3.org/2001/XMLSchema-instance' +# xml soap namespace +NS_SOAP_ENC = "http://schemas.xmlsoap.org/soap/encoding/" +# type definitions for xmlschemas XSI_TYPE = '{%s}type' % XSI_NAMESPACE +# nil type definition for xmlschemas XSI_NIL = '{%s}nil' % XSI_NAMESPACE +# idp and sp communicate usually about a subject(NameID) +# the format determines the category the subject is in + +# custom subject NAMEID_FORMAT_UNSPECIFIED = ( "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified") +# subject as email address NAMEID_FORMAT_EMAILADDRESS = ( "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress") +# subject as x509 key NAMEID_FORMAT_X509SUBJECTNAME = ( "urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName") +# subject as windows domain name NAMEID_FORMAT_WINDOWSDOMAINQUALIFIEDNAME = ( "urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName") +# subject from a kerberos instance NAMEID_FORMAT_KERBEROS = ( "urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos") +# subject as name NAMEID_FORMAT_ENTITY = ( "urn:oasis:names:tc:SAML:2.0:nameid-format:entity") +# linked subject NAMEID_FORMAT_PERSISTENT = ( "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent") +# annonymous subject NAMEID_FORMAT_TRANSIENT = ( "urn:oasis:names:tc:SAML:2.0:nameid-format:transient") +# subject avaiable in encrypted format NAMEID_FORMAT_ENCRYPTED = ( "urn:oasis:names:tc:SAML:2.0:nameid-format:encrypted") +# dicc for avaiable formats NAMEID_FORMATS_SAML2 = ( ('NAMEID_FORMAT_EMAILADDRESS', NAMEID_FORMAT_EMAILADDRESS), ('NAMEID_FORMAT_ENCRYPTED', NAMEID_FORMAT_ENCRYPTED), @@ -51,41 +83,80 @@ ('NAMEID_FORMAT_TRANSIENT', NAMEID_FORMAT_TRANSIENT), ('NAMEID_FORMAT_UNSPECIFIED', NAMEID_FORMAT_UNSPECIFIED), ) + +# a profile outlines a set of rules describing how to embed SAML assertions. +# https://docs.oasis-open.org/security/saml/v2.0/saml-profiles-2.0-os.pdf +# The specification was later updated with errata, and the new version is here: +# https://www.oasis-open.org/committees/download.php/56782/sstc-saml-profiles-errata-2.0-wd-07.pdf + +# XML based values for SAML attributes PROFILE_ATTRIBUTE_BASIC = ( "urn:oasis:names:tc:SAML:2.0:profiles:attribute:basic") +# an AuthnRequest is made to initiate authentication +# authenticate the request with login credentials AUTHN_PASSWORD = "urn:oasis:names:tc:SAML:2.0:ac:classes:Password" +# authenticate the request with login credentials, over tls/https AUTHN_PASSWORD_PROTECTED = \ "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport" +# attribute statements is key:value metadata shared with your app + +# custom format NAME_FORMAT_UNSPECIFIED = ( "urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified") +# uri format NAME_FORMAT_URI = "urn:oasis:names:tc:SAML:2.0:attrname-format:uri" +# XML-based format NAME_FORMAT_BASIC = "urn:oasis:names:tc:SAML:2.0:attrname-format:basic" +# dicc for avaiable formats NAME_FORMATS_SAML2 = ( ('NAME_FORMAT_BASIC', NAME_FORMAT_BASIC), ('NAME_FORMAT_URI', NAME_FORMAT_URI), ('NAME_FORMAT_UNSPECIFIED', NAME_FORMAT_UNSPECIFIED), ) + +# the SAML authority's decision can be predetermined by arbitrary context + +# the specified action is permitted DECISION_TYPE_PERMIT = "Permit" +# the specified action is denied DECISION_TYPE_DENY = "Deny" +# the SAML authority cannot determine if the action is permitted or denied DECISION_TYPE_INDETERMINATE = "Indeterminate" + +# consent attributes determine wether consent has been given and under +# what conditions + +# no claim to consent is made CONSENT_UNSPECIFIED = "urn:oasis:names:tc:SAML:2.0:consent:unspecified" +# consent has been obtained CONSENT_OBTAINED = "urn:oasis:names:tc:SAML:2.0:consent:obtained" +# consent has been obtained before the message has been initiated CONSENT_PRIOR = "urn:oasis:names:tc:SAML:2.0:consent:prior" +# consent has been obtained implicitly CONSENT_IMPLICIT = "urn:oasis:names:tc:SAML:2.0:consent:current-implicit" +# consent has been obtained explicitly CONSENT_EXPLICIT = "urn:oasis:names:tc:SAML:2.0:consent:current-explicit" +# no consent has been obtained CONSENT_UNAVAILABLE = "urn:oasis:names:tc:SAML:2.0:consent:unavailable" +# no consent is needed. CONSENT_INAPPLICABLE = "urn:oasis:names:tc:SAML:2.0:consent:inapplicable" + +# Subject confirmation methods(scm), can be issued, besides the subject itself +# by third parties. +# http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.0.pdf + +# the 3rd party is identified on behalf of the subject given private/public key SCM_HOLDER_OF_KEY = "urn:oasis:names:tc:SAML:2.0:cm:holder-of-key" +# the 3rd party is identified by subject confirmation and must include a security header +# signing its content. SCM_SENDER_VOUCHES = "urn:oasis:names:tc:SAML:2.0:cm:sender-vouches" +# a bearer token is issued instead. SCM_BEARER = "urn:oasis:names:tc:SAML:2.0:cm:bearer" -XSD = "xs" -NS_SOAP_ENC = "http://schemas.xmlsoap.org/soap/encoding/" - class AttributeValueBase(SamlBase): def __init__(self, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pysaml2-6.5.1/src/saml2/sigver.py new/pysaml2-7.0.1/src/saml2/sigver.py --- old/pysaml2-6.5.1/src/saml2/sigver.py 2021-01-21 00:08:02.000000000 +0100 +++ new/pysaml2-7.0.1/src/saml2/sigver.py 2021-05-20 18:56:31.000000000 +0200 @@ -7,6 +7,7 @@ import itertools import logging import os +import re import six from uuid import uuid4 as gen_random_key from time import mktime @@ -59,9 +60,10 @@ SIG = '{{{ns}#}}{attribute}'.format(ns=ds.NAMESPACE, attribute='Signature') +# RSA_1_5 is considered deprecated RSA_1_5 = 'http://www.w3.org/2001/04/xmlenc#rsa-1_5' TRIPLE_DES_CBC = 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc' - +RSA_OAEP_MGF1P = "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p" class SigverError(SAMLError): pass @@ -100,6 +102,14 @@ pass +def get_pem_wrapped_unwrapped(cert): + begin_cert = "-----BEGIN CERTIFICATE-----\n" + end_cert = "\n-----END CERTIFICATE-----\n" + unwrapped_cert = re.sub(f'{begin_cert}|{end_cert}', '', cert) + wrapped_cert = f'{begin_cert}{unwrapped_cert}{end_cert}' + return wrapped_cert, unwrapped_cert + + def read_file(*args, **kwargs): with open(*args, **kwargs) as handler: return handler.read() @@ -1085,10 +1095,8 @@ pass if _encrypt_cert is not None: - if _encrypt_cert.find('-----BEGIN CERTIFICATE-----\n') == -1: - _encrypt_cert = '-----BEGIN CERTIFICATE-----\n' + _encrypt_cert - if _encrypt_cert.find('\n-----END CERTIFICATE-----') == -1: - _encrypt_cert = _encrypt_cert + '\n-----END CERTIFICATE-----' + wrapped_cert, unwrapped_cert = get_pem_wrapped_unwrapped(_encrypt_cert) + _encrypt_cert = wrapped_cert return _encrypt_cert @@ -1835,6 +1843,7 @@ if identifier: signature.id = 'Signature{n}'.format(n=identifier) + # XXX remove - do not embed the cert if public_key: x509_data = ds.X509Data( x509_certificate=[ds.X509Certificate(text=public_key)]) @@ -1872,23 +1881,34 @@ # </EncryptedData> -def pre_encryption_part(msg_enc=TRIPLE_DES_CBC, key_enc=RSA_1_5, key_name='my-rsa-key', - encrypted_key_id=None, encrypted_data_id=None): - """ - - :param msg_enc: - :param key_enc: - :param key_name: - :return: - """ +def pre_encryption_part( + *, + msg_enc=TRIPLE_DES_CBC, + key_enc=RSA_OAEP_MGF1P, + key_name='my-rsa-key', + encrypted_key_id=None, + encrypted_data_id=None, + encrypt_cert=None, +): ek_id = encrypted_key_id or "EK_{id}".format(id=gen_random_key()) ed_id = encrypted_data_id or "ED_{id}".format(id=gen_random_key()) msg_encryption_method = EncryptionMethod(algorithm=msg_enc) key_encryption_method = EncryptionMethod(algorithm=key_enc) + + x509_data = ( + ds.X509Data(x509_certificate=ds.X509Certificate(text=encrypt_cert)) + if encrypt_cert + else None + ) + key_info = ds.KeyInfo( + key_name=ds.KeyName(text=key_name), + x509_data=x509_data, + ) + encrypted_key = EncryptedKey( id=ek_id, encryption_method=key_encryption_method, - key_info=ds.KeyInfo(key_name=ds.KeyName(text=key_name)), + key_info=key_info, cipher_data=CipherData(cipher_value=CipherValue(text='')), ) key_info = ds.KeyInfo(encrypted_key=encrypted_key) @@ -1897,7 +1917,8 @@ type='http://www.w3.org/2001/04/xmlenc#Element', encryption_method=msg_encryption_method, key_info=key_info, - cipher_data=CipherData(cipher_value=CipherValue(text=''))) + cipher_data=CipherData(cipher_value=CipherValue(text='')), + ) return encrypted_data diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pysaml2-6.5.1/tests/entity_no_friendly_name_sp.xml new/pysaml2-7.0.1/tests/entity_no_friendly_name_sp.xml --- old/pysaml2-6.5.1/tests/entity_no_friendly_name_sp.xml 1970-01-01 01:00:00.000000000 +0100 +++ new/pysaml2-7.0.1/tests/entity_no_friendly_name_sp.xml 2021-05-20 18:56:31.000000000 +0200 @@ -0,0 +1,93 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ns0:EntityDescriptor xmlns:ns0="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ns1="http://www.w3.org/2000/09/xmldsig#" xmlns:mdrpi="urn:oasis:names:tc:SAML:metadata:rpi" entityID="https://no-friendly-name.example.edu/saml2/metadata/"> + <ns0:Extensions> + <mdrpi:RegistrationInfo registrationAuthority="http://geant.example.eu/" registrationInstant="2018-05-10T09:45:00Z" /> + <mdattr:EntityAttributes xmlns:mdattr="urn:oasis:names:tc:SAML:metadata:attribute"> + <saml:Attribute xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Name="http://macedir.org/entity-category" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"> + <saml:AttributeValue>http://www.geant.net/uri/dataprotection-code-of-conduct/v1</saml:AttributeValue></saml:Attribute> + </mdattr:EntityAttributes></ns0:Extensions> + <ns0:SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> + <ns0:KeyDescriptor use="encryption"> + <ns1:KeyInfo> + <ns1:X509Data> + <ns1:X509Certificate>MIIDvDCCAqQCCQDXVjecpE8ibTANBgkqhkiG9w0BAQUFADCBnzELMAkGA1UEBhMC +U0UxEjAQBgNVBAgMCVN0b2NraG9sbTESMBAGA1UEBwwJU3RvY2tob2xtMQ4wDAYD +VQQKDAVFRFVJRDEaMBgGA1UECwwRZWR1aWQuZXhhbXBsZS5jb20xGjAYBgNVBAMM +EWVkdWlkLmV4YW1wbGUuY29tMSAwHgYJKoZIhvcNAQkBFhFlZHVpZEBleGFtcGxl +LmNvbTAeFw0xMzA2MTIxMTU5NTdaFw0yMzA2MTAxMTU5NTdaMIGfMQswCQYDVQQG +EwJTRTESMBAGA1UECAwJU3RvY2tob2xtMRIwEAYDVQQHDAlTdG9ja2hvbG0xDjAM +BgNVBAoMBUVEVUlEMRowGAYDVQQLDBFlZHVpZC5leGFtcGxlLmNvbTEaMBgGA1UE +AwwRZWR1aWQuZXhhbXBsZS5jb20xIDAeBgkqhkiG9w0BCQEWEWVkdWlkQGV4YW1w +bGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwHzXvBlv+DN1 +0tV9z6M79RFKJEE1HoBpo/vuQzcIP8SZZNhzwQpYxTVTQ9ocagX1onfJn2ZjoWsi +p45tSMnwLM9a9+UETYAV8O/AUq3gNDp+Mu6sS3smNhdykVR4STscIiP/hWMkZbJ4 +4dmJ2ccT3H6VosXR/OIVTjyalanmvMpDb6ZkKqmuQCDvRMii/R0HhbYUCytToDiy +Bxw1tQG946g8pe5RhZxxzmxVwAGwOyDn1dwi+j4wH2eCDyLu8hLanPHNFNiy5hiN +5B40N24V5YixlksgdT0pF46DfkJRrOCsNWHWnMSN+Xvo1oXLRFXEnfsCB1cw0EAp +SMMGX4dhSwIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQA8+faeCQVTadTrXpB8jzfE +MJq6+V4oajnWb0LJ5ZZcKSlQZ5sfYJ1385CaXGh60Tg4uhtwTOgpRi1R1cZMLTz9 +ST6WPF+2vDJv7dGPuglzyQLvA2fd6BLnyGV6kLUc2XNOyCmD/tWuMvKvW62j4Y3B +XZvRFZZdHNgay4Wgvs8D6wyozWpkWpawXkQ3LqbXO6GChYC4VLru+uJuMKvvKCd/ +I125dzkP2nf9zkGV0cil3oIVSBPBtSRTF/M+oZhkHTwoM6hhonRvdOLuvobKfZ2Q +wHyaxzYldWmVC5omkgZeAdCGpJ316GQF8Zwg/yDOUzm4cvGeIESf1Q6ZxBwI6zGE +</ns1:X509Certificate> + </ns1:X509Data> + </ns1:KeyInfo> + </ns0:KeyDescriptor> + <ns0:KeyDescriptor use="signing"> + <ns1:KeyInfo> + <ns1:X509Data> + <ns1:X509Certificate>MIIDvDCCAqQCCQDXVjecpE8ibTANBgkqhkiG9w0BAQUFADCBnzELMAkGA1UEBhMC +U0UxEjAQBgNVBAgMCVN0b2NraG9sbTESMBAGA1UEBwwJU3RvY2tob2xtMQ4wDAYD +VQQKDAVFRFVJRDEaMBgGA1UECwwRZWR1aWQuZXhhbXBsZS5jb20xGjAYBgNVBAMM +EWVkdWlkLmV4YW1wbGUuY29tMSAwHgYJKoZIhvcNAQkBFhFlZHVpZEBleGFtcGxl +LmNvbTAeFw0xMzA2MTIxMTU5NTdaFw0yMzA2MTAxMTU5NTdaMIGfMQswCQYDVQQG +EwJTRTESMBAGA1UECAwJU3RvY2tob2xtMRIwEAYDVQQHDAlTdG9ja2hvbG0xDjAM +BgNVBAoMBUVEVUlEMRowGAYDVQQLDBFlZHVpZC5leGFtcGxlLmNvbTEaMBgGA1UE +AwwRZWR1aWQuZXhhbXBsZS5jb20xIDAeBgkqhkiG9w0BCQEWEWVkdWlkQGV4YW1w +bGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwHzXvBlv+DN1 +0tV9z6M79RFKJEE1HoBpo/vuQzcIP8SZZNhzwQpYxTVTQ9ocagX1onfJn2ZjoWsi +p45tSMnwLM9a9+UETYAV8O/AUq3gNDp+Mu6sS3smNhdykVR4STscIiP/hWMkZbJ4 +4dmJ2ccT3H6VosXR/OIVTjyalanmvMpDb6ZkKqmuQCDvRMii/R0HhbYUCytToDiy +Bxw1tQG946g8pe5RhZxxzmxVwAGwOyDn1dwi+j4wH2eCDyLu8hLanPHNFNiy5hiN +5B40N24V5YixlksgdT0pF46DfkJRrOCsNWHWnMSN+Xvo1oXLRFXEnfsCB1cw0EAp +SMMGX4dhSwIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQA8+faeCQVTadTrXpB8jzfE +MJq6+V4oajnWb0LJ5ZZcKSlQZ5sfYJ1385CaXGh60Tg4uhtwTOgpRi1R1cZMLTz9 +ST6WPF+2vDJv7dGPuglzyQLvA2fd6BLnyGV6kLUc2XNOyCmD/tWuMvKvW62j4Y3B +XZvRFZZdHNgay4Wgvs8D6wyozWpkWpawXkQ3LqbXO6GChYC4VLru+uJuMKvvKCd/ +I125dzkP2nf9zkGV0cil3oIVSBPBtSRTF/M+oZhkHTwoM6hhonRvdOLuvobKfZ2Q +wHyaxzYldWmVC5omkgZeAdCGpJ316GQF8Zwg/yDOUzm4cvGeIESf1Q6ZxBwI6zGE +</ns1:X509Certificate> + </ns1:X509Data> + </ns1:KeyInfo> + </ns0:KeyDescriptor> + <ns0:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://no-friendly-name.example.edu/saml2/ls/"/> + <ns0:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://no-friendly-name.example.edu/saml2/acs/" index="1"/> + <!-- Require eduPersonTargetedID --> + <ns0:AttributeConsumingService index="0"> + <ns0:ServiceName xml:lang="en">no-friendlyName-SP</ns0:ServiceName> + <ns0:ServiceDescription xml:lang="en">No friendlyName SP</ns0:ServiceDescription> + <ns0:RequestedAttribute Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.10" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" isRequired="true" /> + </ns0:AttributeConsumingService> + </ns0:SPSSODescriptor> + <ns0:Organization> + <ns0:OrganizationName xml:lang="es">Example CO</ns0:OrganizationName> + <ns0:OrganizationName xml:lang="en">Example CO</ns0:OrganizationName> + <ns0:OrganizationDisplayName xml:lang="es">Example</ns0:OrganizationDisplayName> + <ns0:OrganizationDisplayName xml:lang="en">Example</ns0:OrganizationDisplayName> + <ns0:OrganizationURL xml:lang="es">http://www.example.edu</ns0:OrganizationURL> + <ns0:OrganizationURL xml:lang="en">http://www.example.com</ns0:OrganizationURL> + </ns0:Organization> + <ns0:ContactPerson contactType="technical"> + <ns0:Company>Example CO</ns0:Company> + <ns0:GivenName>Sysadmin</ns0:GivenName> + <ns0:SurName/> + <ns0:EmailAddress>[email protected]</ns0:EmailAddress> + </ns0:ContactPerson> + <ns0:ContactPerson contactType="administrative"> + <ns0:Company>Example CO</ns0:Company> + <ns0:GivenName>Admin</ns0:GivenName> + <ns0:SurName>CEO</ns0:SurName> + <ns0:EmailAddress>[email protected]</ns0:EmailAddress> + </ns0:ContactPerson> +</ns0:EntityDescriptor> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pysaml2-6.5.1/tests/idp_uiinfo.xml new/pysaml2-7.0.1/tests/idp_uiinfo.xml --- old/pysaml2-6.5.1/tests/idp_uiinfo.xml 2021-01-21 00:08:02.000000000 +0100 +++ new/pysaml2-7.0.1/tests/idp_uiinfo.xml 2021-05-20 18:56:31.000000000 +0200 @@ -1,17 +1,19 @@ <?xml version='1.0' encoding='UTF-8'?> -<ns0:EntitiesDescriptor xmlns:ns0="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ns1="urn:mace:shibboleth:metadata:1.0" xmlns:ns2="urn:oasis:names:tc:SAML:metadata:ui" xmlns:ns3="http://www.w3.org/2000/09/xmldsig#"><ns0:EntityDescriptor entityID="http://example.com/saml2/idp.xml"><ns0:IDPSSODescriptor WantAuthnRequestsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"><ns0:Extensions><ns1:Scope regexp="false">example.org</ns1:Scope><ns2:UIInfo><ns2:Keywords xml:lang="en">foo bar</ns2:Keywords><ns2:Logo height="40" width="30">http://example.com/logo.jpg</ns2:Logo><ns2:InformationURL>http://example.com/saml2/info.html</ns2:InformationURL><ns2:DisplayName>Example Co.</ns2:DisplayName><ns2:Description xml:lang="se">Exempel bolag</ns2:Description><ns2:PrivacyStatementURL>http://example.com/saml2/privacyStatement.html</ns2:PrivacyStatementURL></ns2:UIInfo></ns0:Extensions><ns0:KeyDescriptor><ns3:KeyInfo><ns3:X509Data><ns3:X509Certificate>MIICsDCCAhmgAwIBAgIJAJr zqSSwmDY9MA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV -BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX -aWRnaXRzIFB0eSBMdGQwHhcNMDkxMDA2MTk0OTQxWhcNMDkxMTA1MTk0OTQxWjBF -MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50 -ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB -gQDJg2cms7MqjniT8Fi/XkNHZNPbNVQyMUMXE9tXOdqwYCA1cc8vQdzkihscQMXy -3iPw2cMggBu6gjMTOSOxECkuvX5ZCclKr8pXAJM5cY6gVOaVO2PdTZcvDBKGbiaN -efiEw5hnoZomqZGp8wHNLAUkwtH9vjqqvxyS/vclc6k2ewIDAQABo4GnMIGkMB0G -A1UdDgQWBBRePsKHKYJsiojE78ZWXccK9K4aJTB1BgNVHSMEbjBsgBRePsKHKYJs -iojE78ZWXccK9K4aJaFJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUt -U3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJAJrzqSSw -mDY9MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAJSrKOEzHO7TL5cy6 -h3qh+3+JAk8HbGBW+cbX6KBCAw/mzU8flK25vnWwXS3dv2FF3Aod0/S7AWNfKib5 -U/SA9nJaz/mWeF9S0farz9AQFc8/NSzAzaVq7YbM4F6f6N2FRl7GikdXRCed45j6 -mrPzGzk3ECbupFnqyREH3+ZPSdk= -</ns3:X509Certificate></ns3:X509Data></ns3:KeyInfo></ns0:KeyDescriptor><ns0:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://example.com/saml2/" /></ns0:IDPSSODescriptor></ns0:EntityDescriptor></ns0:EntitiesDescriptor> +<ns0:EntitiesDescriptor xmlns:ns0="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ns1="urn:mace:shibboleth:metadata:1.0" xmlns:ns2="urn:oasis:names:tc:SAML:metadata:ui" xmlns:ns3="http://www.w3.org/2000/09/xmldsig#"> + <ns0:EntityDescriptor entityID="http://example.com/saml2/idp.xml"> + <ns0:Extensions> + <ns1:Scope regexp="false">descriptor-example.org</ns1:Scope> + <ns1:Scope regexp="true">descriptor-example[^0-9]*\.org</ns1:Scope> + </ns0:Extensions> + <ns0:IDPSSODescriptor WantAuthnRequestsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> + <ns0:Extensions> + <ns1:Scope regexp="false">idpssodescriptor-example.org</ns1:Scope> + <ns2:UIInfo><ns2:Keywords xml:lang="en">foo bar</ns2:Keywords><ns2:Logo height="40" width="30">http://example.com/logo.jpg</ns2:Logo><ns2:InformationURL>http://example.com/saml2/info.html</ns2:InformationURL><ns2:DisplayName>Example Co.</ns2:DisplayName><ns2:Description xml:lang="se">Exempel bolag</ns2:Description><ns2:PrivacyStatementURL>http://example.com/saml2/privacyStatement.html</ns2:PrivacyStatementURL></ns2:UIInfo> + </ns0:Extensions> + <ns0:KeyDescriptor> + <ns3:KeyInfo><ns3:X509Data><ns3:X509Certificate>MIICsDCCAhmgAwIBAgIJAJrzqSSwmDY9MA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMDkxMDA2MTk0OTQxWhcNMDkxMTA1MTk0OTQxWjBFMQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJg2cms7MqjniT8Fi/XkNHZNPbNVQyMUMXE9tXOdqwYCA1cc8vQdzkihscQMXy3iPw2cMggBu6gjMTOSOxECkuvX5ZCclKr8pXAJM5cY6gVOaVO2PdTZcvDBKGbiaNefiEw5hnoZomqZGp8wHNLAUkwtH9vjqqvxyS/vclc6k2ewIDAQABo4GnMIGkMB0GA1UdDgQWBBRePsKHKYJsiojE78ZWXccK9K4aJTB1BgNVHSMEbjBsgBRePsKHKYJsiojE78ZWXccK9K4aJaFJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJAJrzqSSwmDY9MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAJSrKOEzHO7TL5cy6h3qh+3+JAk8HbGBW+cbX6KBCAw/mzU8flK25vnWwXS3dv2FF3Aod0/S7AWNfKib5U/SA9nJaz/mWeF9S0farz9AQFc8/NSzAzaVq7YbM4F6f6N2FRl7GikdXRCed45j6mrPzGzk3ECbupFnqyREH3+ZPSdk=</ns3:X50 9Certificate></ns3:X509Data></ns3:KeyInfo> + </ns0:KeyDescriptor> + <ns0:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://example.com/saml2/" /> + </ns0:IDPSSODescriptor> + </ns0:EntityDescriptor> +</ns0:EntitiesDescriptor> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pysaml2-6.5.1/tests/invalid_metadata_file.xml new/pysaml2-7.0.1/tests/invalid_metadata_file.xml --- old/pysaml2-6.5.1/tests/invalid_metadata_file.xml 1970-01-01 01:00:00.000000000 +0100 +++ new/pysaml2-7.0.1/tests/invalid_metadata_file.xml 2021-05-20 18:56:31.000000000 +0200 @@ -0,0 +1 @@ +this content is invalid diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pysaml2-6.5.1/tests/test_30_mdstore.py new/pysaml2-7.0.1/tests/test_30_mdstore.py --- old/pysaml2-6.5.1/tests/test_30_mdstore.py 2021-01-21 00:08:02.000000000 +0100 +++ new/pysaml2-7.0.1/tests/test_30_mdstore.py 2021-05-20 18:56:31.000000000 +0200 @@ -3,10 +3,13 @@ import datetime import os import re +from re import compile as regex_compile from collections import OrderedDict from unittest.mock import Mock from unittest.mock import patch +from pytest import raises + import responses from six.moves.urllib import parse @@ -19,6 +22,7 @@ from saml2.mdstore import name from saml2 import sigver from saml2.httpbase import HTTPBase +from saml2 import SAMLError from saml2 import BINDING_SOAP from saml2 import BINDING_HTTP_REDIRECT from saml2 import BINDING_HTTP_POST @@ -156,6 +160,14 @@ "class": "saml2.mdstore.MetaDataFile", "metadata": [(full_path("swamid-2.0.xml"),)], }], + "14": [{ + "class": "saml2.mdstore.MetaDataFile", + "metadata": [(full_path("invalid_metadata_file.xml"),)], + }], + "15": [{ + "class": "saml2.mdstore.MetaDataFile", + "metadata": [(full_path("idp_uiinfo.xml"),)], + }], } @@ -170,6 +182,12 @@ xmlstring) +def test_invalid_metadata(): + mds = MetadataStore(ATTRCONV, sec_config, disable_ssl_certificate_validation=True) + with raises(SAMLError): + mds.imp(METADATACONF["14"]) + + def test_swami_1(): UMU_IDP = 'https://idp.umu.se/saml2/idp/metadata.php' mds = MetadataStore(ATTRCONV, sec_config, @@ -595,5 +613,50 @@ assert mds.extension("entity2", "idpsso_descriptor", "test") +def test_shibmd_scope_no_regex_no_descriptor_type(): + mds = MetadataStore(ATTRCONV, sec_config, disable_ssl_certificate_validation=True) + mds.imp(METADATACONF["15"]) + + scopes = mds.sbibmd_scopes(entity_id='http://example.com/saml2/idp.xml') + all_scopes = list(scopes) + + expected = [ + { + "regexp": False, + "text": "descriptor-example.org", + }, + { + "regexp": True, + "text": regex_compile("descriptor-example[^0-9]*\.org"), + }, + ] + assert len(all_scopes) == 2 + assert all_scopes == expected + + +def test_shibmd_scope_no_regex_all_descriptors(): + mds = MetadataStore(ATTRCONV, sec_config, disable_ssl_certificate_validation=True) + mds.imp(METADATACONF["15"]) + + scopes = mds.sbibmd_scopes(entity_id='http://example.com/saml2/idp.xml', typ="idpsso_descriptor") + all_scopes = list(scopes) + expected = [ + { + "regexp": False, + "text": "descriptor-example.org", + }, + { + "regexp": True, + "text": regex_compile("descriptor-example[^0-9]*\.org"), + }, + { + "regexp": False, + "text": "idpssodescriptor-example.org", + }, + ] + assert len(all_scopes) == 3 + assert all_scopes == expected + + if __name__ == "__main__": test_metadata_extension_algsupport() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pysaml2-6.5.1/tests/test_37_entity_categories.py new/pysaml2-7.0.1/tests/test_37_entity_categories.py --- old/pysaml2-6.5.1/tests/test_37_entity_categories.py 2021-01-21 00:08:02.000000000 +0100 +++ new/pysaml2-7.0.1/tests/test_37_entity_categories.py 2021-05-20 18:56:31.000000000 +0200 @@ -1,11 +1,16 @@ from contextlib import closing -from saml2 import sigver + +from pathutils import full_path from saml2 import config +from saml2 import sigver from saml2.assertion import Policy from saml2.attribute_converter import ac_factory -from pathutils import full_path +from saml2.extension import mdattr +from saml2.mdie import to_dict from saml2.mdstore import MetadataStore +from saml2.saml import Attribute, NAME_FORMAT_URI from saml2.server import Server +from saml2.md import RequestedAttribute ATTRCONV = ac_factory(full_path("attributemaps")) @@ -228,3 +233,48 @@ "sn" ] ) + + +def test_filter_ava_required_attributes_with_no_friendly_name(): + entity_id = "https://no-friendly-name.example.edu/saml2/metadata/" + mds = MetadataStore(ATTRCONV, sec_config, disable_ssl_certificate_validation=True) + mds.imp( + [ + { + "class": "saml2.mdstore.MetaDataFile", + "metadata": [(full_path("entity_no_friendly_name_sp.xml"),)] + } + ] + ) + + policy_conf = { + "default": { + "lifetime": {"minutes": 15}, + "entity_categories": ["swamid"] + } + } + policy = Policy(policy_conf, mds) + + ava = { + "givenName": ["Derek"], + "sn": ["Jeter"], + "mail": ["[email protected]"], + "c": ["USA"], + "eduPersonTargetedID": "foo!bar!xyz", + "norEduPersonNIN": "19800101134", + } + + attribute_requirements = mds.attribute_requirement(entity_id) + required = attribute_requirements.get("required", []) + optional = attribute_requirements.get("optional", []) + + # ensure the requirements define the eduPersonTargetedID + # without the friendlyName attribute + oid_eptid = 'urn:oid:1.3.6.1.4.1.5923.1.1.1.10' + requested_attribute_eptid = RequestedAttribute( + name=oid_eptid, name_format=NAME_FORMAT_URI, is_required='true' + ) + assert required == [to_dict(requested_attribute_eptid, onts=[mdattr])] + + ava = policy.filter(ava, entity_id, required=required, optional=optional) + assert _eq(list(ava.keys()), ["eduPersonTargetedID"]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pysaml2-6.5.1/tests/test_41_response.py new/pysaml2-7.0.1/tests/test_41_response.py --- old/pysaml2-6.5.1/tests/test_41_response.py 2021-01-21 00:08:02.000000000 +0100 +++ new/pysaml2-7.0.1/tests/test_41_response.py 2021-05-20 18:56:31.000000000 +0200 @@ -48,28 +48,38 @@ self._resp_ = server.create_authn_response( IDENTITY, - "id12", # in_response_to - "http://lingon.catalogix.se:8087/", - # consumer_url - "urn:mace:example.com:saml:roland:sp", - # sp_entity_id - name_id=name_id) + in_response_to="id12", + destination="http://lingon.catalogix.se:8087/", + sp_entity_id="urn:mace:example.com:saml:roland:sp", + name_id=name_id, + ) self._sign_resp_ = server.create_authn_response( IDENTITY, - "id12", # in_response_to - "http://lingon.catalogix.se:8087/", # consumer_url - "urn:mace:example.com:saml:roland:sp", # sp_entity_id + in_response_to="id12", + destination="http://lingon.catalogix.se:8087/", + sp_entity_id="urn:mace:example.com:saml:roland:sp", name_id=name_id, - sign_assertion=True) + sign_assertion=True, + ) self._resp_authn = server.create_authn_response( IDENTITY, - "id12", # in_response_to - "http://lingon.catalogix.se:8087/", # consumer_url - "urn:mace:example.com:saml:roland:sp", # sp_entity_id + in_response_to="id12", + destination="http://lingon.catalogix.se:8087/", + sp_entity_id="urn:mace:example.com:saml:roland:sp", + name_id=name_id, + authn=AUTHN, + ) + + self._resp_issuer_none = server.create_authn_response( + IDENTITY, + in_response_to="id12", + destination="http://lingon.catalogix.se:8087/", + sp_entity_id="urn:mace:example.com:saml:roland:sp", name_id=name_id, - authn=AUTHN) + ) + self._resp_issuer_none.issuer = None conf = config.SPConfig() conf.load_file("server_conf") @@ -77,27 +87,45 @@ def test_1(self): xml_response = ("%s" % (self._resp_,)) - resp = response_factory(xml_response, self.conf, - return_addrs=[ - "http://lingon.catalogix.se:8087/"], - outstanding_queries={ - "id12": "http://localhost:8088/sso"}, - timeslack=TIMESLACK, decode=False) + resp = response_factory( + xml_response, self.conf, + return_addrs=["http://lingon.catalogix.se:8087/"], + outstanding_queries={"id12": "http://localhost:8088/sso"}, + timeslack=TIMESLACK, + decode=False, + ) assert isinstance(resp, StatusResponse) assert isinstance(resp, AuthnResponse) def test_2(self): xml_response = self._sign_resp_ - resp = response_factory(xml_response, self.conf, - return_addrs=[ - "http://lingon.catalogix.se:8087/"], - outstanding_queries={ - "id12": "http://localhost:8088/sso"}, - timeslack=TIMESLACK, decode=False) + resp = response_factory( + xml_response, + self.conf, + return_addrs=["http://lingon.catalogix.se:8087/"], + outstanding_queries={"id12": "http://localhost:8088/sso"}, + timeslack=TIMESLACK, + decode=False, + ) + + assert isinstance(resp, StatusResponse) + assert isinstance(resp, AuthnResponse) + + def test_issuer_none(self): + xml_response = ("%s" % (self._resp_issuer_none,)) + resp = response_factory( + xml_response, + self.conf, + return_addrs=["http://lingon.catalogix.se:8087/"], + outstanding_queries={"id12": "http://localhost:8088/sso"}, + timeslack=TIMESLACK, + decode=False, + ) assert isinstance(resp, StatusResponse) assert isinstance(resp, AuthnResponse) + assert resp.issuer() == "" @mock.patch('saml2.time_util.datetime') def test_false_sign(self, mock_datetime): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pysaml2-6.5.1/tests/test_42_enc.py new/pysaml2-7.0.1/tests/test_42_enc.py --- old/pysaml2-6.5.1/tests/test_42_enc.py 2021-01-21 00:08:02.000000000 +0100 +++ new/pysaml2-7.0.1/tests/test_42_enc.py 2021-05-20 18:56:31.000000000 +0200 @@ -12,8 +12,8 @@ __author__ = 'roland' -TMPL_NO_HEADER = """<ns0:EncryptedData xmlns:ns0="http://www.w3.org/2001/04/xmlenc#" xmlns:ns1="http://www.w3.org/2000/09/xmldsig#" Id="{ed_id}" Type="http://www.w3.org/2001/04/xmlenc#Element"><ns0:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" /><ns1:KeyInfo><ns0:EncryptedKey Id="{ek_id}"><ns0:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" /><ns1:KeyInfo><ns1:KeyName>my-rsa-key</ns1:KeyName></ns1:KeyInfo><ns0:CipherData><ns0:CipherValue /></ns0:CipherData></ns0:EncryptedKey></ns1:KeyInfo><ns0:CipherData><ns0:CipherValue /></ns0:CipherData></ns0:EncryptedData>""" -TMPL = "<?xml version='1.0' encoding='UTF-8'?>\n%s" % TMPL_NO_HEADER +TMPL_NO_HEADER = """<ns0:EncryptedData xmlns:ns0="http://www.w3.org/2001/04/xmlenc#" xmlns:ns1="http://www.w3.org/2000/09/xmldsig#" Id="{ed_id}" Type="http://www.w3.org/2001/04/xmlenc#Element"><ns0:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" /><ns1:KeyInfo><ns0:EncryptedKey Id="{ek_id}"><ns0:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p" /><ns1:KeyInfo><ns1:KeyName>my-rsa-key</ns1:KeyName></ns1:KeyInfo><ns0:CipherData><ns0:CipherValue /></ns0:CipherData></ns0:EncryptedKey></ns1:KeyInfo><ns0:CipherData><ns0:CipherValue /></ns0:CipherData></ns0:EncryptedData>""" +TMPL = f"<?xml version='1.0' encoding='UTF-8'?>\n{TMPL_NO_HEADER}" IDENTITY = {"eduPersonAffiliation": ["staff", "member"], "surName": ["Jeter"], "givenName": ["Derek"], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pysaml2-6.5.1/tests/test_50_server.py new/pysaml2-7.0.1/tests/test_50_server.py --- old/pysaml2-6.5.1/tests/test_50_server.py 2021-01-21 00:08:02.000000000 +0100 +++ new/pysaml2-7.0.1/tests/test_50_server.py 2021-05-20 18:56:31.000000000 +0200 @@ -6,6 +6,7 @@ from contextlib import closing from six.moves.urllib.parse import parse_qs import uuid +import re from saml2.cert import OpenSSLWrapper from saml2.sigver import make_temp, DecryptError, EncryptError, CertificateError @@ -129,8 +130,10 @@ self.verify_assertion(assertion) assert assertion[0].signature is None - assert 'EncryptedAssertion><encas1:Assertion xmlns:encas0="http://www.w3.org/2001/XMLSchema-instance" ' \ - 'xmlns:encas1="urn:oasis:names:tc:SAML:2.0:assertion"' in decr_text + assert re.search( + r':EncryptedAssertion><encas[0-9]:Assertion ([^ >]* )*xmlns:encas[0-9]="urn:oasis:names:tc:SAML:2.0:assertion"', + decr_text, + ) def verify_advice_assertion(self, resp, decr_text): assert resp.assertion[0].signature is None @@ -1188,9 +1191,10 @@ def verify_encrypted_assertion(self, assertion, decr_text): self.verify_assertion(assertion) assert assertion[0].signature is None - - assert 'EncryptedAssertion><encas1:Assertion xmlns:encas0="http://www.w3.org/2001/XMLSchema-instance" ' \ - 'xmlns:encas1="urn:oasis:names:tc:SAML:2.0:assertion"' in decr_text + assert re.search( + r':EncryptedAssertion><encas[0-9]:Assertion ([^ >]* )*xmlns:encas[0-9]="urn:oasis:names:tc:SAML:2.0:assertion"', + decr_text, + ) def verify_advice_assertion(self, resp, decr_text): assert resp.assertion[0].signature is None diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pysaml2-6.5.1/tests/test_60_sp.py new/pysaml2-7.0.1/tests/test_60_sp.py --- old/pysaml2-6.5.1/tests/test_60_sp.py 2021-01-21 00:08:02.000000000 +0100 +++ new/pysaml2-7.0.1/tests/test_60_sp.py 2021-05-20 18:56:31.000000000 +0200 @@ -1,7 +1,8 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -import base64 +from base64 import encodebytes as b64encode + import pytest from saml2.authn_context import INTERNETPROTOCOLPASSWORD from saml2.saml import NAMEID_FORMAT_TRANSIENT @@ -73,7 +74,7 @@ "urn:mace:example.com:saml:roland:sp", trans_name_policy, "[email protected]", authn=AUTHN) - resp_str = base64.encodestring(resp_str.encode('utf-8')) + resp_str = b64encode(resp_str.encode('utf-8')) self.sp.outstanding_queries = {"id1": "http://www.example.com/service"} session_info = self.sp._eval_authn_response( {}, {"SAMLResponse": [resp_str]})
