summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Kanakarakis <ivan.kanak@gmail.com>2020-11-24 15:25:28 +0200
committerIvan Kanakarakis <ivan.kanak@gmail.com>2020-11-24 17:46:41 +0200
commita159cc537835b4588544d9ee129fec10bf757124 (patch)
tree81ee860ff92a6752f3b1f7a67d5021817985926a
parentc0410837a5ee8c5c1fe656c501aa640c57000b59 (diff)
downloadpysaml2-a159cc537835b4588544d9ee129fec10bf757124.tar.gz
WIP, TODO cleanup, see description
* client_base::Base is the base for an SP and manages SP_ARGS * server::Server is the base for an IdP and maanges AA_IDP_ARGS * entity::Entity is the base of SP/IdPs and manages the COMMON_ARGS The signing_algorithm and digest_algorithm are COMMON_ARGS and should be set and managed by entity::Entity. On init they are set as properties of the Entity object. If no configuration has been given, the internal-default is set (through DefaultSignature()). The set sign_alg and digest_alg must be checked against an allow/block-list --- - Signing is done both by SPs (on requests) and IdPs (on responses). - Signing is done both for the Redirect-binding (apply_binding()) and the POST-binding (_message() > sign()). --- * All client_base::Base(SP) (create_*) methods end in Entity::_message() * Almost all server::Server(IdP) (create_*) methods end in Entity::_response() thus: - Entity::_message() must decide the value of "sign" and call Entity::sign() - Entity::_response() must decide the value of "sign" and call Entity::sign() - Entity::_status_response() must decide the value of "sign" and call Entity::sign() - Entity::sign() must decide the value of sign_alg and digest_alg and call sigver::pre_signature_part() --- All calls to Entity::_message() and Entity::_response() (or to their callers) must pass on sign, sign_alg and digest_alg All calls to sigver::pre_signature_part() should happen through the same call-chain and should pass on specific sign_alg and digest_alg params All relevant params should be set to None unless they have been set by the caller. --- client::do_logout should be refactored to use the same call-chain --- These type of checks (and self.lock blocks) should be removed (there are more for sign_assertion) ``` if (sign and self.sec.cert_handler.generate_cert()) or client_crt is not None: ``` ``` if self.sec.cert_handler.generate_cert() ``` --- Signed-off-by: Ivan Kanakarakis <ivan.kanak@gmail.com>
-rw-r--r--src/saml2/client_base.py384
-rw-r--r--src/saml2/entity.py410
-rw-r--r--src/saml2/server.py292
-rw-r--r--src/saml2/sigver.py2
4 files changed, 723 insertions, 365 deletions
diff --git a/src/saml2/client_base.py b/src/saml2/client_base.py
index dd3ddfdf..90a4fbd1 100644
--- a/src/saml2/client_base.py
+++ b/src/saml2/client_base.py
@@ -283,7 +283,7 @@ class Base(Entity):
else:
return None
- # XXX sp create
+ # XXX DONE sp create > _message
def create_authn_request(
self,
destination,
@@ -296,7 +296,7 @@ class Base(Entity):
consent=None,
extensions=None,
sign=None,
- sign_prepare=False,
+ sign_prepare=None,
sign_alg=None,
digest_alg=None,
allow_create=None,
@@ -448,64 +448,39 @@ class Base(Entity):
client_crt = kwargs.get("client_crt")
nsprefix = kwargs.get("nsprefix")
- sign = sign if sign is not None else self.should_sign
- sign_alg = sign_alg or self.signing_algorithm
- digest_alg = digest_alg or self.digest_algorithm
-
- if sign_alg not in [long_name for short_name, long_name in SIG_ALLOWED_ALG]:
- raise Exception(
- "Signature algo not in allowed list: {algo}".format(algo=sign_alg)
- )
- if digest_alg not in [long_name for short_name, long_name in DIGEST_ALLOWED_ALG]:
- raise Exception(
- "Digest algo not in allowed list: {algo}".format(algo=digest_alg)
- )
-
- if (sign and self.sec.cert_handler.generate_cert()) or client_crt is not None:
- with self.lock:
- self.sec.cert_handler.update_cert(True, client_crt)
- if client_crt is not None:
- sign_prepare = True
- msg = self._message(
- AuthnRequest,
- destination,
- message_id,
- consent,
- extensions,
- sign,
- sign_prepare,
- sign_alg=sign_alg,
- digest_alg=digest_alg,
- protocol_binding=binding,
- scoping=scoping,
- nsprefix=nsprefix,
- **args,
- )
- else:
- msg = self._message(
- AuthnRequest,
- destination,
- message_id,
- consent,
- extensions,
- sign,
- sign_prepare,
- sign_alg=sign_alg,
- digest_alg=digest_alg,
- protocol_binding=binding,
- scoping=scoping,
- nsprefix=nsprefix,
- **args,
- )
+ msg = self._message(
+ AuthnRequest,
+ destination,
+ message_id,
+ consent,
+ extensions,
+ sign,
+ sign_prepare,
+ sign_alg=sign_alg,
+ digest_alg=digest_alg,
+ protocol_binding=binding,
+ scoping=scoping,
+ nsprefix=nsprefix,
+ **args,
+ )
return msg
- # XXX sp create
- def create_attribute_query(self, destination, name_id=None,
- attribute=None, message_id=0, consent=None,
- extensions=None, sign=False, sign_prepare=False, sign_alg=None,
- digest_alg=None,
- **kwargs):
+ # XXX DONE sp create > _message
+ def create_attribute_query(
+ self,
+ destination,
+ name_id=None,
+ attribute=None,
+ message_id=0,
+ consent=None,
+ extensions=None,
+ sign=None,
+ sign_prepare=None,
+ sign_alg=None,
+ digest_alg=None,
+ **kwargs,
+ ):
""" Constructs an AttributeQuery
:param destination: To whom the query should be sent
@@ -559,19 +534,40 @@ class Base(Entity):
except KeyError:
nsprefix = None
- return self._message(AttributeQuery, destination, message_id, consent,
- extensions, sign, sign_prepare, subject=subject,
- attribute=attribute, nsprefix=nsprefix,
- sign_alg=sign_alg, digest_alg=digest_alg)
+ return self._message(
+ AttributeQuery,
+ destination,
+ message_id,
+ consent,
+ extensions,
+ sign,
+ sign_prepare,
+ subject=subject,
+ attribute=attribute,
+ nsprefix=nsprefix,
+ sign_alg=sign_alg,
+ digest_alg=digest_alg,
+ )
# MUST use SOAP for
# AssertionIDRequest, SubjectQuery,
# AuthnQuery, AttributeQuery, or AuthzDecisionQuery
- # XXX sp create
- def create_authz_decision_query(self, destination, action,
- evidence=None, resource=None, subject=None,
- message_id=0, consent=None, extensions=None,
- sign=None, sign_alg=None, digest_alg=None, **kwargs):
+ # XXX DONE sp create > _message
+ def create_authz_decision_query(
+ self,
+ destination,
+ action,
+ evidence=None,
+ resource=None,
+ subject=None,
+ message_id=0,
+ consent=None,
+ extensions=None,
+ sign=None,
+ sign_alg=None,
+ digest_alg=None,
+ **kwargs,
+ ):
""" Creates an authz decision query.
:param destination: The IdP endpoint
@@ -586,20 +582,38 @@ class Base(Entity):
:return: AuthzDecisionQuery instance
"""
- return self._message(AuthzDecisionQuery, destination, message_id,
- consent, extensions, sign, action=action,
- evidence=evidence, resource=resource,
- subject=subject, sign_alg=sign_alg,
- digest_alg=digest_alg, **kwargs)
-
- # XXX sp create
- def create_authz_decision_query_using_assertion(self, destination,
- assertion, action=None,
- resource=None,
- subject=None, message_id=0,
- consent=None,
- extensions=None,
- sign=False, nsprefix=None):
+ return self._message(
+ AuthzDecisionQuery,
+ destination,
+ message_id,
+ consent,
+ extensions,
+ sign,
+ action=action,
+ evidence=evidence,
+ resource=resource,
+ subject=subject,
+ sign_alg=sign_alg,
+ digest_alg=digest_alg,
+ **kwargs,
+ )
+
+ # XXX DONE sp create > self.create_authz_decision_query (FIXME pass sign/sign_alg/etc) > _message
+ def create_authz_decision_query_using_assertion(
+ self,
+ destination,
+ assertion,
+ action=None,
+ resource=None,
+ subject=None,
+ message_id=0,
+ consent=None,
+ extensions=None,
+ sign=None,
+ sign_ag=None,
+ digest_alg=None,
+ nsprefix=None,
+ ):
""" Makes an authz decision query based on a previously received
Assertion.
@@ -624,12 +638,21 @@ class Base(Entity):
_action = None
return self.create_authz_decision_query(
- destination, _action, saml.Evidence(assertion=assertion),
- resource, subject, message_id=message_id, consent=consent,
- extensions=extensions, sign=sign, nsprefix=nsprefix)
+ destination,
+ _action,
+ saml.Evidence(assertion=assertion),
+ resource,
+ subject,
+ message_id=message_id,
+ consent=consent,
+ extensions=extensions,
+ sign=sign,
+ sign_alg=sign_alg,
+ digest_alg=digest_alg,
+ nsprefix=nsprefix,
+ )
@staticmethod
- # XXX sp create
def create_assertion_id_request(assertion_id_refs, **kwargs):
"""
@@ -642,11 +665,21 @@ class Base(Entity):
else:
return 0, assertion_id_refs[0]
- # XXX sp create
- def create_authn_query(self, subject, destination=None, authn_context=None,
- session_index="", message_id=0, consent=None,
- extensions=None, sign=False, nsprefix=None, sign_alg=None,
- digest_alg=None):
+ # XXX DONE sp create > _message
+ def create_authn_query(
+ self,
+ subject,
+ destination=None,
+ authn_context=None,
+ session_index="",
+ message_id=0,
+ consent=None,
+ extensions=None,
+ sign=None,
+ nsprefix=None,
+ sign_alg=None,
+ digest_alg=None,
+ ):
"""
:param subject: The subject its all about as a <Subject> instance
@@ -659,20 +692,37 @@ class Base(Entity):
:param sign: Whether the request should be signed or not.
:return:
"""
- return self._message(AuthnQuery, destination, message_id, consent,
- extensions, sign, subject=subject,
- session_index=session_index,
- requested_authn_context=authn_context,
- nsprefix=nsprefix, sign_alg=sign_alg,
- digest_alg=digest_alg)
-
- # XXX sp create
- def create_name_id_mapping_request(self, name_id_policy,
- name_id=None, base_id=None,
- encrypted_id=None, destination=None,
- message_id=0, consent=None,
- extensions=None, sign=False,
- nsprefix=None, sign_alg=None, digest_alg=None):
+ return self._message(
+ AuthnQuery,
+ destination,
+ message_id,
+ consent,
+ extensions,
+ sign,
+ subject=subject,
+ session_index=session_index,
+ requested_authn_context=authn_context,
+ nsprefix=nsprefix,
+ sign_alg=sign_alg,
+ digest_alg=digest_alg,
+ )
+
+ # XXX DONE sp create > _message
+ def create_name_id_mapping_request(
+ self,
+ name_id_policy,
+ name_id=None,
+ base_id=None,
+ encrypted_id=None,
+ destination=None,
+ message_id=0,
+ consent=None,
+ extensions=None,
+ sign=None,
+ nsprefix=None,
+ sign_alg=None,
+ digest_alg=None,
+ ):
"""
:param name_id_policy:
@@ -692,29 +742,39 @@ class Base(Entity):
"At least one of name_id, base_id or encrypted_id must be present."
)
- if name_id:
- return self._message(NameIDMappingRequest, destination, message_id,
- consent, extensions, sign,
- name_id_policy=name_id_policy, name_id=name_id,
- nsprefix=nsprefix, sign_alg=sign_alg,
- digest_alg=digest_alg)
- elif base_id:
- return self._message(NameIDMappingRequest, destination, message_id,
- consent, extensions, sign,
- name_id_policy=name_id_policy, base_id=base_id,
- nsprefix=nsprefix, sign_alg=sign_alg,
- digest_alg=digest_alg)
- else:
- return self._message(NameIDMappingRequest, destination, message_id,
- consent, extensions, sign,
- name_id_policy=name_id_policy,
- encrypted_id=encrypted_id, nsprefix=nsprefix,
- sign_alg=sign_alg, digest_alg=digest_alg)
+ id_attr = {
+ "name_id": name_id,
+ "base_id": (
+ base_id
+ if not name_id
+ else None
+ ),
+ "encrypted_id": (
+ encrypted_id
+ if not name_id and not base_id
+ else None
+ ),
+ }
+
+ return self._message(
+ NameIDMappingRequest,
+ destination,
+ message_id,
+ consent,
+ extensions,
+ sign,
+ name_id_policy=name_id_policy,
+ **id_attr,
+ nsprefix=nsprefix,
+ sign_alg=sign_alg,
+ digest_alg=digest_alg,
+ )
# ======== response handling ===========
- def parse_authn_request_response(self, xmlstr, binding, outstanding=None,
- outstanding_certs=None, conv_info=None):
+ def parse_authn_request_response(
+ self, xmlstr, binding, outstanding=None, outstanding_certs=None, conv_info=None
+ ):
""" Deal with an AuthnResponse
:param xmlstr: The reply as a xml string
@@ -743,15 +803,14 @@ class Base(Entity):
"return_addrs": self.service_urls(binding=binding),
"entity_id": self.config.entityid,
"attribute_converters": self.config.attribute_converters,
- "allow_unknown_attributes":
- self.config.allow_unknown_attributes,
- 'conv_info': conv_info
+ "allow_unknown_attributes": self.config.allow_unknown_attributes,
+ 'conv_info': conv_info,
}
try:
- resp = self._parse_response(xmlstr, AuthnResponse,
- "assertion_consumer_service",
- binding, **kwargs)
+ resp = self._parse_response(
+ xmlstr, AuthnResponse, "assertion_consumer_service", binding, **kwargs
+ )
except StatusError as err:
logger.error("SAML status error: %s", err)
raise
@@ -762,12 +821,14 @@ class Base(Entity):
raise
if not isinstance(resp, AuthnResponse):
- logger.error("Response type not supported: %s",
- saml2.class_name(resp))
+ logger.error("Response type not supported: %s", saml2.class_name(resp))
return None
- if (resp.assertion and len(resp.response.encrypted_assertion) == 0 and
- resp.assertion.subject.name_id):
+ if (
+ resp.assertion
+ and len(resp.response.encrypted_assertion) == 0
+ and resp.assertion.subject.name_id
+ ):
self.users.add_information_about_person(resp.session_info())
logger.info("--- ADDED person info ----")
@@ -777,15 +838,15 @@ class Base(Entity):
# SubjectQuery, AuthnQuery, RequestedAuthnContext, AttributeQuery,
# AuthzDecisionQuery all get Response as response
- def parse_authz_decision_query_response(self, response,
- binding=BINDING_SOAP):
+ def parse_authz_decision_query_response(self, response, binding=BINDING_SOAP):
""" Verify that the response is OK
"""
- kwargs = {"entity_id": self.config.entityid,
- "attribute_converters": self.config.attribute_converters}
+ kwargs = {
+ "entity_id": self.config.entityid,
+ "attribute_converters": self.config.attribute_converters,
+ }
- return self._parse_response(response, AuthzResponse, "", binding,
- **kwargs)
+ return self._parse_response(response, AuthzResponse, "", binding, **kwargs)
def parse_authn_query_response(self, response, binding=BINDING_SOAP):
""" Verify that the response is OK
@@ -828,9 +889,16 @@ class Base(Entity):
# ------------------- ECP ------------------------------------------------
- # XXX sp create
- def create_ecp_authn_request(self, entityid=None, relay_state="",
- sign=False, **kwargs):
+ # XXX DONE sp create > create_authn_request (FIXME DONE sign/sign_alg/etc) > _message
+ def create_ecp_authn_request(
+ self,
+ entityid=None,
+ relay_state="",
+ sign=None,
+ sign_alg=None,
+ digest_alg=None,
+ **kwargs,
+ ):
""" Makes an authentication request.
:param entityid: The entity ID of the IdP to send the request to
@@ -847,16 +915,22 @@ class Base(Entity):
# must_understand and act according to the standard
#
- paos_request = paos.Request(must_understand="1", actor=ACTOR,
- response_consumer_url=my_url,
- service=ECP_SERVICE)
+ paos_request = paos.Request(
+ must_understand="1",
+ actor=ACTOR,
+ response_consumer_url=my_url,
+ service=ECP_SERVICE,
+ )
# ----------------------------------------
# <ecp:RelayState>
# ----------------------------------------
- relay_state = ecp.RelayState(actor=ACTOR, must_understand="1",
- text=relay_state)
+ relay_state = ecp.RelayState(
+ actor=ACTOR,
+ must_understand="1",
+ text=relay_state,
+ )
# ----------------------------------------
# <samlp:AuthnRequest>
@@ -883,18 +957,23 @@ class Base(Entity):
"single_sign_on_service", [_binding], entity_id=entityid
)
req_id, authn_req = self.create_authn_request(
- location, service_url_binding=BINDING_PAOS, **kwargs
+ location,
+ service_url_binding=BINDING_PAOS,
+ sign=sign,
+ sign_alg=sign_alg,
+ digest_alg=digest_alg,
+ **kwargs,
)
# ----------------------------------------
# The SOAP envelope
# ----------------------------------------
- soap_envelope = make_soap_enveloped_saml_thingy(authn_req,
- [paos_request,
- relay_state])
+ soap_envelope = make_soap_enveloped_saml_thingy(
+ authn_req, [paos_request, relay_state]
+ )
- return req_id, "%s" % soap_envelope
+ return req_id, str(soap_envelope)
def parse_ecp_authn_response(self, txt, outstanding=None):
rdict = soap.class_instances_from_soap_enveloped_saml_thingies(txt,
@@ -933,7 +1012,6 @@ class Base(Entity):
# ----------------------------------------------------------------------
@staticmethod
- # XXX sp create
def create_discovery_service_request(url, entity_id, **kwargs):
"""
Created the HTTP redirect URL needed to send the user to the
diff --git a/src/saml2/entity.py b/src/saml2/entity.py
index 39bc3dec..a619882b 100644
--- a/src/saml2/entity.py
+++ b/src/saml2/entity.py
@@ -75,6 +75,8 @@ from saml2.pack import http_redirect_message
from saml2.pack import http_form_post_message
from saml2.xmldsig import DefaultSignature
+from saml2.xmldsig import SIG_ALLOWED_ALG
+from saml2.xmldsig import DIGEST_ALLOWED_ALG
logger = logging.getLogger(__name__)
@@ -213,6 +215,10 @@ class Entity(HTTPBase):
return Issuer(text=self.config.entityid,
format=NAMEID_FORMAT_ENTITY)
+ # XXX DONE will actually use sign_alg and digest_alg for the Redirect-Binding
+ # XXX DONE deepest level - needs to decide the sign_alg (no digest_alg here)
+ # XXX verify digest_alg is not needed
+ # XXX deprecate sigalg for sign_alg
def apply_binding(
self,
binding,
@@ -237,8 +243,13 @@ class Entity(HTTPBase):
:return: A dictionary
"""
+ # XXX sig-allowed should be configurable
sign = sign if sign is not None else self.should_sign
- sigalg = sigalg or self.signing_algorithm
+ sign_alg = sigalg or self.signing_algorithm
+ if sign_alg not in [long_name for short_name, long_name in SIG_ALLOWED_ALG]:
+ raise Exception(
+ "Signature algo not in allowed list: {algo}".format(algo=sign_alg)
+ )
# unless if BINDING_HTTP_ARTIFACT
if response:
@@ -260,14 +271,14 @@ class Entity(HTTPBase):
relay_state=relay_state,
typ=typ,
sign=sign,
- sigalg=sigalg,
+ sigalg=sign_alg,
backend=self.sec.sec_backend,
)
info["url"] = str(destination)
info["method"] = "GET"
elif binding == BINDING_SOAP or binding == BINDING_PAOS:
info = self.use_soap(
- msg_str, destination, sign=sign, sigalg=sigalg, **kwargs
+ msg_str, destination, sign=sign, sigalg=sign_alg, **kwargs
)
elif binding == BINDING_URI:
info = self.use_http_uri(msg_str, typ, destination)
@@ -335,8 +346,13 @@ class Entity(HTTPBase):
if not message_id:
message_id = sid()
- return {"id": message_id, "version": VERSION,
- "issue_instant": instant(), "issuer": self._issuer()}
+ margs = {
+ "id": message_id,
+ "version": VERSION,
+ "issue_instant": instant(),
+ "issuer": self._issuer(),
+ }
+ return margs
def response_args(self, message, bindings=None, descr_type=""):
"""
@@ -450,15 +466,30 @@ class Entity(HTTPBase):
# --------------------------------------------------------------------------
+ # XXX DONE will actually use sign_alg and digest_alg for the POST-Binding
+ # XXX DONE deepest level - needs to decide the sign_alg and digest_alg value
+ # XXX calls pre_signature_part
def sign(
self,
msg,
mid=None,
to_sign=None,
- sign_prepare=False,
+ sign_prepare=None,
sign_alg=None,
digest_alg=None,
):
+ # XXX sig/digest-allowed should be configurable
+ sign_alg = sign_alg or self.signing_algorithm
+ digest_alg = digest_alg or self.digest_algorithm
+ if sign_alg not in [long_name for short_name, long_name in SIG_ALLOWED_ALG]:
+ raise Exception(
+ "Signature algo not in allowed list: {algo}".format(algo=sign_alg)
+ )
+ if digest_alg not in [long_name for short_name, long_name in DIGEST_ALLOWED_ALG]:
+ raise Exception(
+ "Digest algo not in allowed list: {algo}".format(algo=digest_alg)
+ )
+
if msg.signature is None:
msg.signature = pre_signature_part(
msg.id, self.sec.my_cert, 1, sign_alg=sign_alg, digest_alg=digest_alg
@@ -478,7 +509,11 @@ class Entity(HTTPBase):
logger.info("REQUEST: %s", msg)
return signed_instance_factory(msg, self.sec, to_sign)
- # XXX calls self.sign
+ # XXX DONE will actually use sign the POST-Binding
+ # XXX DONE deepest level - needs to decide the sign value
+ # XXX DONE calls self.sign must figure out sign
+ # XXX ensure both SPs and IdPs go through this
+ # XXX ensure this works for the POST-Binding
def _message(
self,
request_cls,
@@ -486,8 +521,8 @@ class Entity(HTTPBase):
message_id=0,
consent=None,
extensions=None,
- sign=False,
- sign_prepare=False,
+ sign=None,
+ sign_prepare=None,
nsprefix=None,
sign_alg=None,
digest_alg=None,
@@ -533,6 +568,8 @@ class Entity(HTTPBase):
req = self.msg_cb(req)
reqid = req.id
+
+ sign = sign if sign is not None else self.should_sign
if sign:
signed_req = self.sign(
req,
@@ -593,8 +630,7 @@ class Entity(HTTPBase):
return True
return False
- def _encrypt_assertion(self, encrypt_cert, sp_entity_id, response,
- node_xpath=None):
+ def _encrypt_assertion(self, encrypt_cert, sp_entity_id, response, node_xpath=None):
""" Encryption of assertions.
:param encrypt_cert: Certificate to be used for encryption.
@@ -632,15 +668,27 @@ class Entity(HTTPBase):
raise exception
return response
- # XXX calls self.sign
- def _response(self, in_response_to, consumer_url=None, status=None,
- issuer=None, sign=False, to_sign=None, sp_entity_id=None,
- encrypt_assertion=False,
- encrypt_assertion_self_contained=False,
- encrypted_advice_attributes=False,
- encrypt_cert_advice=None, encrypt_cert_assertion=None,
- sign_assertion=None, pefim=False, sign_alg=None,
- digest_alg=None, **kwargs):
+ # XXX DONE calls self.sign must figure out sign
+ def _response(
+ self,
+ in_response_to,
+ consumer_url=None,
+ status=None,
+ issuer=None,
+ sign=None,
+ to_sign=None,
+ sp_entity_id=None,
+ encrypt_assertion=False,
+ encrypt_assertion_self_contained=False,
+ encrypted_advice_attributes=False,
+ encrypt_cert_advice=None,
+ encrypt_cert_assertion=None,
+ sign_assertion=None,
+ pefim=False,
+ sign_alg=None,
+ digest_alg=None,
+ **kwargs,
+ ):
""" Create a Response.
Encryption:
encrypt_assertion must be true for encryption to be
@@ -692,7 +740,12 @@ class Entity(HTTPBase):
self._add_info(response, **kwargs)
- if not sign and to_sign and not encrypt_assertion:
+ sign = sign if sign is not None else self.should_sign
+ if (
+ not sign
+ and to_sign
+ and not encrypt_assertion
+ ):
return signed_instance_factory(response, self.sec, to_sign)
has_encrypt_cert = self.has_encrypt_cert_in_metadata(sp_entity_id)
@@ -701,23 +754,31 @@ class Entity(HTTPBase):
if not has_encrypt_cert and encrypt_cert_assertion is None:
encrypt_assertion = False
- if encrypt_assertion or (
- encrypted_advice_attributes and
- response.assertion.advice is
- not None and
- len(response.assertion.advice.assertion) == 1):
+ if (
+ encrypt_assertion
+ or (
+ encrypted_advice_attributes
+ and response.assertion.advice is not None
+ and len(response.assertion.advice.assertion) == 1
+ )
+ ):
if sign:
- response.signature = pre_signature_part(response.id,
- self.sec.my_cert, 1,
- sign_alg=sign_alg,
- digest_alg=digest_alg)
+ response.signature = pre_signature_part(
+ response.id,
+ self.sec.my_cert,
+ 1,
+ sign_alg=sign_alg,
+ digest_alg=digest_alg,
+ )
sign_class = [(class_name(response), response.id)]
else:
sign_class = []
- if encrypted_advice_attributes and response.assertion.advice is \
- not None \
- and len(response.assertion.advice.assertion) > 0:
+ if (
+ encrypted_advice_attributes
+ and response.assertion.advice is not None
+ and len(response.assertion.advice.assertion) > 0
+ ):
_assertions = response.assertion
if not isinstance(_assertions, list):
_assertions = [_assertions]
@@ -810,10 +871,17 @@ class Entity(HTTPBase):
else:
return response
- # XXX calls self.sign
- def _status_response(self, response_class, issuer, status, sign=False,
- sign_alg=None, digest_alg=None,
- **kwargs):
+ # XXX DONE calls self.sign must figure out sign
+ def _status_response(
+ self,
+ response_class,
+ issuer,
+ status,
+ sign=None,
+ sign_alg=None,
+ digest_alg=None,
+ **kwargs,
+ ):
""" Create a StatusResponse.
:param response_class: Which subclass of StatusResponse that should be
@@ -828,18 +896,21 @@ class Entity(HTTPBase):
mid = sid()
for key in ["binding"]:
- try:
- del kwargs[key]
- except KeyError:
- pass
+ kwargs.pop(key, None)
if not status:
status = success_status_factory()
- response = response_class(issuer=issuer, id=mid, version=VERSION,
- issue_instant=instant(),
- status=status, **kwargs)
+ response = response_class(
+ issuer=issuer,
+ id=mid,
+ version=VERSION,
+ issue_instant=instant(),
+ status=status,
+ **kwargs,
+ )
+ sign = sign if sign is not None else self.should_sign
if sign:
return self.sign(response, mid, sign_alg=sign_alg, digest_alg=digest_alg)
else:
@@ -919,10 +990,18 @@ class Entity(HTTPBase):
# ------------------------------------------------------------------------
- # XXX ent create
- def create_error_response(self, in_response_to, destination, info,
- sign=False, issuer=None, sign_alg=None,
- digest_alg=None, **kwargs):
+ # XXX DONE ent create > _response
+ def create_error_response(
+ self,
+ in_response_to,
+ destination,
+ info,
+ sign=None,
+ issuer=None,
+ sign_alg=None,
+ digest_alg=None,
+ **kwargs,
+ ):
""" Create a error response.
:param in_response_to: The identifier of the message this is a response
@@ -937,18 +1016,35 @@ class Entity(HTTPBase):
"""
status = error_status_factory(info)
- return self._response(in_response_to, destination, status, issuer,
- sign, sign_alg=sign_alg, digest_alg=digest_alg)
+ return self._response(
+ in_response_to,
+ destination,
+ status,
+ issuer,
+ sign,
+ sign_alg=sign_alg,
+ digest_alg=digest_alg,
+ )
# ------------------------------------------------------------------------
- # XXX ent create
- def create_logout_request(self, destination, issuer_entity_id,
- subject_id=None, name_id=None,
- reason=None, expire=None, message_id=0,
- consent=None, extensions=None, sign=False,
- session_indexes=None, sign_alg=None,
- digest_alg=None):
+ # XXX DONE ent create > _message
+ def create_logout_request(
+ self,
+ destination,
+ issuer_entity_id,
+ subject_id=None,
+ name_id=None,
+ reason=None,
+ expire=None,
+ message_id=0,
+ consent=None,
+ extensions=None,
+ sign=None,
+ session_indexes=None,
+ sign_alg=None,
+ digest_alg=None,
+ ):
""" Constructs a LogoutRequest
:param destination: Destination of the request
@@ -970,9 +1066,9 @@ class Entity(HTTPBase):
if subject_id:
if self.entity_type == "idp":
- name_id = NameID(text=self.users.get_entityid(subject_id,
- issuer_entity_id,
- False))
+ name_id = NameID(
+ text=self.users.get_entityid(subject_id, issuer_entity_id, False)
+ )
else:
name_id = NameID(text=subject_id)
@@ -989,16 +1085,33 @@ class Entity(HTTPBase):
sis.append(SessionIndex(text=si))
args["session_index"] = sis
- return self._message(LogoutRequest, destination, message_id,
- consent, extensions, sign, name_id=name_id,
- reason=reason, not_on_or_after=expire,
- issuer=self._issuer(), sign_alg=sign_alg,
- digest_alg=digest_alg, **args)
+ return self._message(
+ LogoutRequest,
+ destination,
+ message_id,
+ consent,
+ extensions,
+ sign,
+ name_id=name_id,
+ reason=reason,
+ not_on_or_after=expire,
+ issuer=self._issuer(),
+ sign_alg=sign_alg,
+ digest_alg=digest_alg,
+ **args,
+ )
- # XXX ent create
- def create_logout_response(self, request, bindings=None, status=None,
- sign=False, issuer=None, sign_alg=None,
- digest_alg=None):
+ # XXX DONE ent create > _status_response
+ def create_logout_response(
+ self,
+ request,
+ bindings=None,
+ status=None,
+ sign=None,
+ issuer=None,
+ sign_alg=None,
+ digest_alg=None,
+ ):
""" Create a LogoutResponse.
:param request: The request this is a response to
@@ -1015,18 +1128,32 @@ class Entity(HTTPBase):
if not issuer:
issuer = self._issuer()
- response = self._status_response(samlp.LogoutResponse, issuer, status,
- sign, sign_alg=sign_alg,
- digest_alg=digest_alg, **rinfo)
+ response = self._status_response(
+ samlp.LogoutResponse,
+ issuer,
+ status,
+ sign,
+ sign_alg=sign_alg,
+ digest_alg=digest_alg,
+ **rinfo,
+ )
logger.info("Response: %s", response)
return response
- # XXX ent create
- def create_artifact_resolve(self, artifact, destination, sessid,
- consent=None, extensions=None, sign=False,
- sign_alg=None, digest_alg=None):
+ # XXX DONE ent create > _message
+ def create_artifact_resolve(
+ self,
+ artifact,
+ destination,
+ sessid,
+ consent=None,
+ extensions=None,
+ sign=None,
+ sign_alg=None,
+ digest_alg=None,
+ ):
"""
Create a ArtifactResolve request
@@ -1041,23 +1168,45 @@ class Entity(HTTPBase):
artifact = Artifact(text=artifact)
- return self._message(ArtifactResolve, destination, sessid,
- consent, extensions, sign, artifact=artifact,
- sign_alg=sign_alg, digest_alg=digest_alg)
+ return self._message(
+ ArtifactResolve,
+ destination,
+ sessid,
+ consent,
+ extensions,
+ sign,
+ artifact=artifact,
+ sign_alg=sign_alg,
+ digest_alg=digest_alg,
+ )
- # XXX ent create
- def create_artifact_response(self, request, artifact, bindings=None,
- status=None, sign=False, issuer=None,
- sign_alg=None, digest_alg=None):
+ # XXX DONE ent create > _status_response
+ def create_artifact_response(
+ self,
+ request,
+ artifact,
+ bindings=None,
+ status=None,
+ sign=None,
+ issuer=None,
+ sign_alg=None,
+ digest_alg=None,
+ ):
"""
Create an ArtifactResponse
:return:
"""
rinfo = self.response_args(request, bindings)
- response = self._status_response(ArtifactResponse, issuer, status,
- sign=sign, sign_alg=sign_alg,
- digest_alg=digest_alg, **rinfo)
+ response = self._status_response(
+ ArtifactResponse,
+ issuer,
+ status,
+ sign=sign,
+ sign_alg=sign_alg,
+ digest_alg=digest_alg,
+ **rinfo,
+ )
msg = element_to_extension_element(self.artifact[artifact])
response.extension_elements = [msg]
@@ -1066,13 +1215,22 @@ class Entity(HTTPBase):
return response
- # XXX ent create
- def create_manage_name_id_request(self, destination, message_id=0,
- consent=None, extensions=None, sign=False,
- name_id=None, new_id=None,
- encrypted_id=None, new_encrypted_id=None,
- terminate=None, sign_alg=None,
- digest_alg=None):
+ # XXX DONE ent create > _message
+ def create_manage_name_id_request(
+ self,
+ destination,
+ message_id=0,
+ consent=None,
+ extensions=None,
+ sign=None,
+ name_id=None,
+ new_id=None,
+ encrypted_id=None,
+ new_encrypted_id=None,
+ terminate=None,
+ sign_alg=None,
+ digest_alg=None,
+ ):
"""
:param destination:
@@ -1108,9 +1266,16 @@ class Entity(HTTPBase):
"One of NewID, NewEncryptedNameID or Terminate has to be "
"provided")
- return self._message(ManageNameIDRequest, destination, consent=consent,
- extensions=extensions, sign=sign,
- sign_alg=sign_alg, digest_alg=digest_alg, **kwargs)
+ return self._message(
+ ManageNameIDRequest,
+ destination,
+ consent=consent,
+ extensions=extensions,
+ sign=sign,
+ sign_alg=sign_alg,
+ digest_alg=digest_alg,
+ **kwargs,
+ )
def parse_manage_name_id_request(self, xmlstr, binding=BINDING_SOAP):
""" Deal with a LogoutRequest
@@ -1125,11 +1290,18 @@ class Entity(HTTPBase):
return self._parse_request(xmlstr, saml_request.ManageNameIDRequest,
"manage_name_id_service", binding)
- # XXX ent create
- def create_manage_name_id_response(self, request, bindings=None,
- status=None, sign=False, issuer=None,
- sign_alg=None, digest_alg=None,
- **kwargs):
+ # XXX DONE ent create > _status_response
+ def create_manage_name_id_response(
+ self,
+ request,
+ bindings=None,
+ status=None,
+ sign=None,
+ issuer=None,
+ sign_alg=None,
+ digest_alg=None,
+ **kwargs,
+ ):
rinfo = self.response_args(request, bindings)
@@ -1147,16 +1319,26 @@ class Entity(HTTPBase):
return response
- def parse_manage_name_id_request_response(self, string,
- binding=BINDING_SOAP):
- return self._parse_response(string, saml_response.ManageNameIDResponse,
- "manage_name_id_service", binding,
- asynchop=False)
+ def parse_manage_name_id_request_response(self, string, binding=BINDING_SOAP):
+ return self._parse_response(
+ string,
+ saml_response.ManageNameIDResponse,
+ "manage_name_id_service",
+ binding,
+ asynchop=False,
+ )
# ------------------------------------------------------------------------
- def _parse_response(self, xmlstr, response_cls, service, binding,
- outstanding_certs=None, **kwargs):
+ def _parse_response(
+ self,
+ xmlstr,
+ response_cls,
+ service,
+ binding,
+ outstanding_certs=None,
+ **kwargs,
+ ):
""" Deal with a Response
:param xmlstr: The response as a xml string
@@ -1357,7 +1539,15 @@ class Entity(HTTPBase):
return destination
- def artifact2message(self, artifact, descriptor, sign=False):
+ # XXX DONE uses sign but not a create_*
+ def artifact2message(
+ self,
+ artifact,
+ descriptor,
+ sign=None,
+ sign_alg=None,
+ digest_alg=None,
+ ):
"""
:param artifact: The Base64 encoded SAML artifact as sent over the net
@@ -1377,6 +1567,8 @@ class Entity(HTTPBase):
destination,
_sid,
sign=sign,
+ sign_alg=sign_alg,
+ digest_alg=digest_alg,
)
return self.send_using_soap(msg, destination)
diff --git a/src/saml2/server.py b/src/saml2/server.py
index 1d4b7543..04604b09 100644
--- a/src/saml2/server.py
+++ b/src/saml2/server.py
@@ -392,6 +392,7 @@ class Server(Entity):
**kwargs)
return assertion
+ # XXX > _response
def _authn_response(
self,
in_response_to,
@@ -403,8 +404,8 @@ class Server(Entity):
authn=None,
issuer=None,
policy=None,
- sign_assertion=False,
- sign_response=False,
+ sign_assertion=None,
+ sign_response=None,
best_effort=False,
encrypt_assertion=False,
encrypt_cert_advice=None,
@@ -493,10 +494,9 @@ class Server(Entity):
to_sign = []
if not encrypt_assertion:
if sign_assertion:
- assertion.signature = pre_signature_part(assertion.id,
- self.sec.my_cert, 2,
- sign_alg=sign_alg,
- digest_alg=digest_alg)
+ assertion.signature = pre_signature_part(
+ assertion.id, self.sec.my_cert, 2, sign_alg=sign_alg, digest_alg=digest_alg
+ )
to_sign.append((class_name(assertion), assertion.id))
args["assertion"] = assertion
@@ -505,25 +505,47 @@ class Server(Entity):
self.session_db.store_assertion(assertion, to_sign)
return self._response(
- in_response_to, consumer_url, status, issuer, sign_response,
- to_sign, sp_entity_id=sp_entity_id,
+ in_response_to,
+ consumer_url,
+ status,
+ issuer,
+ sign_response,
+ to_sign,
+ sp_entity_id=sp_entity_id,
encrypt_assertion=encrypt_assertion,
encrypt_cert_advice=encrypt_cert_advice,
encrypt_cert_assertion=encrypt_cert_assertion,
encrypt_assertion_self_contained=encrypt_assertion_self_contained,
encrypted_advice_attributes=encrypted_advice_attributes,
sign_assertion=sign_assertion,
- pefim=pefim, sign_alg=sign_alg, digest_alg=digest_alg, **args)
+ pefim=pefim,
+ sign_alg=sign_alg,
+ digest_alg=digest_alg,
+ **args,
+ )
# ------------------------------------------------------------------------
- # XXX idp create
- def create_attribute_response(self, identity, in_response_to, destination,
- sp_entity_id, userid="", name_id=None,
- status=None, issuer=None,
- sign_assertion=False, sign_response=False,
- attributes=None, sign_alg=None,
- digest_alg=None, farg=None, **kwargs):
+ # XXX calls pre_signature_part without ensuring sign_alg/digest_alg
+ # XXX DONE idp create > _response
+ def create_attribute_response(
+ self,
+ identity,
+ in_response_to,
+ destination,
+ sp_entity_id,
+ userid="",
+ name_id=None,
+ status=None,
+ issuer=None,
+ sign_assertion=None,
+ sign_response=None,
+ attributes=None,
+ sign_alg=None,
+ digest_alg=None,
+ farg=None,
+ **kwargs,
+ ):
""" Create an attribute assertion response.
:param identity: A dictionary with attributes and values that are
@@ -573,10 +595,10 @@ class Server(Entity):
farg=farg['assertion'])
if sign_assertion:
- assertion.signature = pre_signature_part(assertion.id,
- self.sec.my_cert, 1,
- sign_alg=sign_alg,
- digest_alg=digest_alg)
+ # XXX calls pre_signature_part without ensuring sign_alg/digest_alg
+ assertion.signature = pre_signature_part(
+ assertion.id, self.sec.my_cert, 1, sign_alg=sign_alg, digest_alg=digest_alg
+ )
# Just the assertion or the response and the assertion ?
to_sign = [(class_name(assertion), assertion.id)]
kwargs['sign_assertion'] = True
@@ -690,7 +712,7 @@ class Server(Entity):
return args
- # XXX idp create
+ # XXX DONE idp create > _authn_response > _response
def create_authn_response(
self,
identity,
@@ -769,52 +791,67 @@ class Server(Entity):
try:
_authn = authn
- if (sign_assertion or sign_response) and \
- self.sec.cert_handler.generate_cert():
- with self.lock:
- self.sec.cert_handler.update_cert(True)
- return self._authn_response(
- in_response_to, destination, sp_entity_id, identity,
- authn=_authn, issuer=issuer, pefim=pefim,
- sign_alg=sign_alg, digest_alg=digest_alg,
- session_not_on_or_after=session_not_on_or_after, **args)
return self._authn_response(
- in_response_to, destination, sp_entity_id, identity,
- authn=_authn, issuer=issuer, pefim=pefim, sign_alg=sign_alg,
+ in_response_to,
+ destination,
+ sp_entity_id,
+ identity,
+ authn=_authn,
+ issuer=issuer,
+ pefim=pefim,
+ sign_alg=sign_alg,
digest_alg=digest_alg,
- session_not_on_or_after=session_not_on_or_after, **args)
-
+ session_not_on_or_after=session_not_on_or_after,
+ **args,
+ )
except MissingValue as exc:
- return self.create_error_response(in_response_to, destination,
- sp_entity_id, exc, name_id)
-
- # XXX idp create
- def create_authn_request_response(self, identity, in_response_to,
- destination, sp_entity_id,
- name_id_policy=None, userid=None,
- name_id=None, authn=None, authn_decl=None,
- issuer=None, sign_response=False,
- sign_assertion=False,
- session_not_on_or_after=None, **kwargs):
-
- return self.create_authn_response(identity, in_response_to, destination,
- sp_entity_id, name_id_policy, userid,
- name_id, authn, issuer,
- sign_response, sign_assertion,
- authn_decl=authn_decl,
- session_not_on_or_after=session_not_on_or_after)
-
- # XXX idp create
- def create_assertion_id_request_response(self, assertion_id, sign=False,
- sign_alg=None,
- digest_alg=None, **kwargs):
- """
+ return self.create_error_response(
+ in_response_to, destination, sp_entity_id, exc, name_id
+ )
- :param assertion_id:
- :param sign:
- :return:
- """
+ # XXX DONE idp create > create_authn_response > _authn_response > _response
+ def create_authn_request_response(
+ self,
+ identity,
+ in_response_to,
+ destination,
+ sp_entity_id,
+ name_id_policy=None,
+ userid=None,
+ name_id=None,
+ authn=None,
+ authn_decl=None,
+ issuer=None,
+ sign_response=None,
+ sign_assertion=None,
+ session_not_on_or_after=None,
+ sign_alg=None,
+ digest_alg=None,
+ **kwargs,
+ ):
+ return self.create_authn_response(
+ identity,
+ in_response_to,
+ destination,
+ sp_entity_id,
+ name_id_policy,
+ userid,
+ name_id,
+ authn,
+ issuer,
+ sign_response,
+ sign_assertion,
+ authn_decl=authn_decl,
+ session_not_on_or_after=session_not_on_or_after,
+ sign_alg=sign_alg,
+ digest_alg=digest_alg,
+ )
+ # XXX calls pre_signature_part without ensuring sign_alg/digest_alg
+ # XXX DONE idp create > [...]
+ def create_assertion_id_request_response(
+ self, assertion_id, sign=None, sign_alg=None, digest_alg=None, **kwargs
+ ):
try:
(assertion, to_sign) = self.session_db.get_assertion(assertion_id)
except KeyError:
@@ -822,22 +859,33 @@ class Server(Entity):
if to_sign:
if assertion.signature is None:
- assertion.signature = pre_signature_part(assertion.id,
- self.sec.my_cert, 1,
- sign_alg=sign_alg,
- digest_alg=digest_alg)
-
+ # XXX calls pre_signature_part without ensuring sign_alg/digest_alg
+ assertion.signature = pre_signature_part(
+ assertion.id,
+ self.sec.my_cert,
+ 1,
+ sign_alg=sign_alg,
+ digest_alg=digest_alg,
+ )
return signed_instance_factory(assertion, self.sec, to_sign)
else:
return assertion
+ # XXX calls self.sign without ensuring sign
# XXX calls self.sign => should it call _message (which calls self.sign)?
- # XXX idp create
- def create_name_id_mapping_response(self, name_id=None, encrypted_id=None,
- in_response_to=None,
- issuer=None, sign_response=False,
- status=None, sign_alg=None,
- digest_alg=None, **kwargs):
+ # XXX idp create > NameIDMappingResponse & sign?
+ def create_name_id_mapping_response(
+ self,
+ name_id=None,
+ encrypted_id=None,
+ in_response_to=None,
+ issuer=None,
+ sign_response=None,
+ status=None,
+ sign_alg=None,
+ digest_alg=None,
+ **kwargs,
+ ):
"""
protocol for mapping a principal's name identifier into a
different name identifier for the same principal.
@@ -855,8 +903,9 @@ class Server(Entity):
ms_args = self.message_args()
- _resp = NameIDMappingResponse(name_id, encrypted_id,
- in_response_to=in_response_to, **ms_args)
+ _resp = NameIDMappingResponse(
+ name_id, encrypted_id, in_response_to=in_response_to, **ms_args
+ )
if sign_response:
return self.sign(_resp, sign_alg=sign_alg, digest_alg=digest_alg)
@@ -864,12 +913,20 @@ class Server(Entity):
logger.info("Message: %s", _resp)
return _resp
- # XXX idp create
- def create_authn_query_response(self, subject, session_index=None,
- requested_context=None, in_response_to=None,
- issuer=None, sign_response=False,
- status=None, sign_alg=None, digest_alg=None,
- **kwargs):
+ # XXX DONE idp create > _response
+ def create_authn_query_response(
+ self,
+ subject,
+ session_index=None,
+ requested_context=None,
+ in_response_to=None,
+ issuer=None,
+ sign_response=None,
+ status=None,
+ sign_alg=None,
+ digest_alg=None,
+ **kwargs,
+ ):
"""
A successful <Response> will contain one or more assertions containing
authentication statements.
@@ -878,33 +935,54 @@ class Server(Entity):
"""
margs = self.message_args()
- asserts = []
- for statement in self.session_db.get_authn_statements(
- subject.name_id, session_index, requested_context):
- asserts.append(saml.Assertion(authn_statement=statement,
- subject=subject, **margs))
+ asserts = [
+ saml.Assertion(authn_statement=statement, subject=subject, **margs)
+ for statement in self.session_db.get_authn_statements(
+ subject.name_id, session_index, requested_context
+ )
+ ]
if asserts:
args = {"assertion": asserts}
else:
args = {}
- return self._response(in_response_to, "", status, issuer,
- sign_response, to_sign=[], sign_alg=sign_alg,
- digest_alg=digest_alg, **args)
+ return self._response(
+ in_response_to,
+ "",
+ status,
+ issuer,
+ sign_response,
+ to_sign=[],
+ sign_alg=sign_alg,
+ digest_alg=digest_alg,
+ **args,
+ )
# ---------
def parse_ecp_authn_request(self):
pass
- # XXX idp create
- def create_ecp_authn_request_response(self, acs_url, identity,
- in_response_to, destination,
- sp_entity_id, name_id_policy=None,
- userid=None, name_id=None, authn=None,
- issuer=None, sign_response=False,
- sign_assertion=False, **kwargs):
+ # XXX DONE idp create > create_authn_response > _authn_response > _response
+ def create_ecp_authn_request_response(
+ self,
+ acs_url,
+ identity,
+ in_response_to,
+ destination,
+ sp_entity_id,
+ name_id_policy=None,
+ userid=None,
+ name_id=None,
+ authn=None,
+ issuer=None,
+ sign_response=None,
+ sign_assertion=None,
+ sign_alg=None,
+ digest_alg=None,
+ **kwargs,
+ ):
# ----------------------------------------
# <ecp:Response
@@ -918,17 +996,27 @@ class Server(Entity):
# <samlp:Response
# ----------------------------------------
- response = self.create_authn_response(identity, in_response_to,
- destination, sp_entity_id,
- name_id_policy, userid, name_id,
- authn, issuer,
- sign_response, sign_assertion)
+ response = self.create_authn_response(
+ identity,
+ in_response_to,
+ destination,
+ sp_entity_id,
+ name_id_policy,
+ userid,
+ name_id,
+ authn,
+ issuer,
+ sign_response,
+ sign_assertion,
+ sign_alg=sign_alg,
+ digest_alg=digest_alg
+ )
body = soapenv.Body()
body.extension_elements = [element_to_extension_element(response)]
soap_envelope = soapenv.Envelope(header=header, body=body)
- return "%s" % soap_envelope
+ return str(soap_envelope)
def close(self):
self.ident.close()
diff --git a/src/saml2/sigver.py b/src/saml2/sigver.py
index 87441807..b8e924c2 100644
--- a/src/saml2/sigver.py
+++ b/src/saml2/sigver.py
@@ -1750,7 +1750,7 @@ class SecurityContext(object):
return statement
-# XXX calls DefaultSignature
+# XXX FIXME calls DefaultSignature - remove to unveil chain of calls without proper args
def pre_signature_part(ident, public_key=None, identifier=None, digest_alg=None, sign_alg=None):
"""
If an assertion is to be signed the signature part has to be preset