From c9c01cc57f2669419e291bf847a9bf04fba10c75 Mon Sep 17 00:00:00 2001 From: Roland Hedberg Date: Fri, 21 Mar 2014 12:49:19 +0100 Subject: [PATCH] PEP-8 clean up . --- src/saml2/response.py | 151 +++++++++++++++++++++++++----------------- 1 file changed, 89 insertions(+), 62 deletions(-) diff --git a/src/saml2/response.py b/src/saml2/response.py index 3c6bfa3..8212584 100644 --- a/src/saml2/response.py +++ b/src/saml2/response.py @@ -204,7 +204,7 @@ def _dummy(_): def for_me(conditions, myself): """ Am I among the intended audiences """ - if not conditions.audience_restriction: # No audience restriction + if not conditions.audience_restriction: # No audience restriction return True for restriction in conditions.audience_restriction: @@ -216,19 +216,20 @@ def for_me(conditions, myself): else: #print "Not for me: %s != %s" % (audience.text.strip(), myself) pass - + return False def authn_response(conf, return_addrs, outstanding_queries=None, timeslack=0, - asynchop=True, allow_unsolicited=False, want_assertions_signed=False): + asynchop=True, allow_unsolicited=False, + want_assertions_signed=False): sec = security_context(conf) if not timeslack: try: timeslack = int(conf.accepted_time_diff) except TypeError: timeslack = 0 - + return AuthnResponse(sec, conf.attribute_converters, conf.entityid, return_addrs, outstanding_queries, timeslack, asynchop=asynchop, allow_unsolicited=allow_unsolicited, @@ -271,13 +272,13 @@ class StatusResponse(object): self.require_response_signature = False self.not_signed = False self.asynchop = asynchop - + def _clear(self): self.xmlstr = "" self.name_id = None self.response = None self.not_on_or_after = 0 - + def _postamble(self): if not self.response: logger.error("Response was not correctly signed") @@ -293,10 +294,10 @@ class StatusResponse(object): logger.error("Not valid response: %s" % exc.args[0]) self._clear() return self - + self.in_response_to = self.response.in_response_to return self - + def load_instance(self, instance): if signed(instance): # This will check signature on Assertion which is the default @@ -309,9 +310,9 @@ class StatusResponse(object): else: self.not_signed = True self.response = instance - + return self._postamble() - + def _loads(self, xmldata, decode=True, origxml=None): # own copy @@ -319,7 +320,8 @@ class StatusResponse(object): logger.debug("xmlstr: %s" % (self.xmlstr,)) try: - self.response = self.signature_check(xmldata, origdoc=origxml, must=self.require_signature, + self.response = self.signature_check(xmldata, origdoc=origxml, + must=self.require_signature, require_response_signature=self.require_response_signature) except TypeError: @@ -329,11 +331,11 @@ class StatusResponse(object): except Exception, excp: #logger.exception("EXCEPTION: %s", excp) raise - + #print "<", self.response - + return self._postamble() - + def status_ok(self): if self.response.status: status = self.response.status @@ -369,7 +371,7 @@ class StatusResponse(object): def _verify(self): if self.request_id and self.in_response_to and \ - self.in_response_to != self.request_id: + self.in_response_to != self.request_id: logger.error("Not the id I expected: %s != %s" % ( self.in_response_to, self.request_id)) return None @@ -385,11 +387,11 @@ class StatusResponse(object): if self.asynchop: if self.response.destination and \ - self.response.destination not in self.return_addrs: + self.response.destination not in self.return_addrs: logger.error("%s not in %s" % (self.response.destination, - self.return_addrs)) + self.return_addrs)) return None - + assert self.issue_instant_ok() assert self.status_ok() return self @@ -408,10 +410,10 @@ class StatusResponse(object): self.xmlstr = mold.xmlstr self.in_response_to = mold.in_response_to self.response = mold.response - + def issuer(self): return self.response.issuer.text.strip() - + class LogoutResponse(StatusResponse): msgtype = "logout_response" @@ -430,7 +432,8 @@ class NameIDMappingResponse(StatusResponse): request_id=0, asynchop=True): StatusResponse.__init__(self, sec_context, return_addrs, timeslack, request_id, asynchop) - self.signature_check = self.sec.correctly_signed_name_id_mapping_response + self.signature_check = self.sec\ + .correctly_signed_name_id_mapping_response class ManageNameIDResponse(StatusResponse): @@ -455,7 +458,8 @@ class AuthnResponse(StatusResponse): return_addrs=None, outstanding_queries=None, timeslack=0, asynchop=True, allow_unsolicited=False, test=False, allow_unknown_attributes=False, - want_assertions_signed=False, want_response_signed=False, **kwargs): + want_assertions_signed=False, want_response_signed=False, + **kwargs): StatusResponse.__init__(self, sec_context, return_addrs, timeslack, asynchop=asynchop) @@ -465,7 +469,7 @@ class AuthnResponse(StatusResponse): self.outstanding_queries = outstanding_queries else: self.outstanding_queries = {} - self.context = "AuthnReq" + self.context = "AuthnReq" self.came_from = "" self.ava = None self.assertion = None @@ -481,19 +485,40 @@ class AuthnResponse(StatusResponse): except KeyError: self.extension_schema = {} + def check_subject_confirmation_in_response_to(self, irp): + for assertion in self.response.assertion: + for _sc in assertion.subject.subject_confirmation: + try: + assert _sc.subject_confirmation_data.in_response_to == irp + except AssertionError: + return False + + return True + def loads(self, xmldata, decode=True, origxml=None): self._loads(xmldata, decode, origxml) - + if self.asynchop: if self.in_response_to in self.outstanding_queries: self.came_from = self.outstanding_queries[self.in_response_to] del self.outstanding_queries[self.in_response_to] + try: + if not self.check_subject_confirmation_in_response_to( + self.in_response_to): + logger.exception( + "Unsolicited response %s" % self.in_response_to) + raise UnsolicitedResponse( + "Unsolicited response: %s" % self.in_response_to) + except AttributeError: + pass elif self.allow_unsolicited: pass else: - logger.exception("Unsolicited response %s" % self.in_response_to) - raise UnsolicitedResponse("Unsolicited response: %s" % self.in_response_to) - + logger.exception( + "Unsolicited response %s" % self.in_response_to) + raise UnsolicitedResponse( + "Unsolicited response: %s" % self.in_response_to) + return self def clear(self): @@ -501,7 +526,7 @@ class AuthnResponse(StatusResponse): self.came_from = "" self.ava = None self.assertion = None - + def authn_statement_ok(self, optional=False): try: # the assertion MUST contain one AuthNStatement @@ -511,7 +536,7 @@ class AuthnResponse(StatusResponse): return True else: raise - + authn_statement = self.assertion.authn_statement[0] if authn_statement.session_not_on_or_after: if validate_on_or_after(authn_statement.session_not_on_or_after, @@ -523,7 +548,7 @@ class AuthnResponse(StatusResponse): return False return True # check authn_statement.session_index - + def condition_ok(self, lax=False): if self.test: lax = True @@ -541,7 +566,8 @@ class AuthnResponse(StatusResponse): # if both are present NotBefore must be earlier than NotOnOrAfter if conditions.not_before and conditions.not_on_or_after: - if not later_than(conditions.not_on_or_after, conditions.not_before): + if not later_than(conditions.not_on_or_after, + conditions.not_before): return False try: @@ -562,10 +588,11 @@ class AuthnResponse(StatusResponse): if not lax: raise Exception("Not for me!!!") - if conditions.condition: # extra conditions + if conditions.condition: # extra conditions for cond in conditions.condition: try: - if cond.extension_attributes[XSI_TYPE] in self.extension_schema: + if cond.extension_attributes[ + XSI_TYPE] in self.extension_schema: pass else: raise Exception("Unknown condition") @@ -582,9 +609,9 @@ class AuthnResponse(StatusResponse): :param attribute_statement: A SAML.AttributeStatement which might contain both encrypted attributes and attributes. """ -# _node_name = [ -# "urn:oasis:names:tc:SAML:2.0:assertion:EncryptedData", -# "urn:oasis:names:tc:SAML:2.0:assertion:EncryptedAttribute"] + # _node_name = [ + # "urn:oasis:names:tc:SAML:2.0:assertion:EncryptedData", + # "urn:oasis:names:tc:SAML:2.0:assertion:EncryptedAttribute"] for encattr in attribute_statement.encrypted_attribute: if not encattr.encrypted_key: @@ -624,7 +651,7 @@ class AuthnResponse(StatusResponse): if data.address: if not valid_address(data.address): return False - # verify that I got it from the correct sender + # verify that I got it from the correct sender # These two will raise exception if untrue validate_on_or_after(data.not_on_or_after, self.timeslack) @@ -650,7 +677,8 @@ class AuthnResponse(StatusResponse): logger.info("outstanding queries: %s" % ( self.outstanding_queries.keys(),)) raise Exception( - "Combination of session id and requestURI I don't recall") + "Combination of session id and requestURI I don't " + "recall") return True def _holder_of_key_confirmed(self, data): @@ -687,12 +715,12 @@ class AuthnResponse(StatusResponse): subject_confirmation.method,)) subjconf.append(subject_confirmation) - + if not subjconf: raise VerificationError("No valid subject confirmation") - + subject.subject_confirmation = subjconf - + # The subject must contain a name_id try: assert subject.name_id @@ -709,19 +737,19 @@ class AuthnResponse(StatusResponse): logger.info("Subject NameID: %s" % self.name_id) return self.name_id - + def _assertion(self, assertion): self.assertion = assertion logger.debug("assertion context: %s" % (self.context,)) logger.debug("assertion keys: %s" % (assertion.keyswv())) logger.debug("outstanding_queries: %s" % (self.outstanding_queries,)) - + #if self.context == "AuthnReq" or self.context == "AttrQuery": if self.context == "AuthnReq": self.authn_statement_ok() -# elif self.context == "AttrQuery": -# self.authn_statement_ok(True) + # elif self.context == "AttrQuery": + # self.authn_statement_ok(True) if not self.condition_ok(): raise VerificationError("Condition not OK") @@ -732,7 +760,7 @@ class AuthnResponse(StatusResponse): self.ava = self.get_identity() logger.debug("--- AVA: %s" % (self.ava,)) - + try: self.get_subject() if self.asynchop: @@ -744,7 +772,7 @@ class AuthnResponse(StatusResponse): except Exception: logger.exception("get subject") raise - + def _encrypted_assertion(self, xmlstr): if xmlstr.encrypted_data: assertion_str = self.sec.decrypt(xmlstr.encrypted_data.to_string()) @@ -765,7 +793,7 @@ class AuthnResponse(StatusResponse): logger.debug("Decrypted Assertion: %s" % assertion) return self._assertion(assertion) - + def parse_assertion(self): if self.context == "AuthnQuery": # can contain one or more assertions @@ -773,10 +801,10 @@ class AuthnResponse(StatusResponse): else: # This is a saml2int limitation try: assert len(self.response.assertion) == 1 or \ - len(self.response.encrypted_assertion) == 1 + len(self.response.encrypted_assertion) == 1 except AssertionError: raise Exception("No assertion part") - + if self.response.assertion: logger.debug("***Unencrypted response***") for assertion in self.response.assertion: @@ -793,7 +821,7 @@ class AuthnResponse(StatusResponse): def verify(self): """ Verify that the assertion is syntactically correct and the signature is correct if present.""" - + try: self._verify() except AssertionError: @@ -807,15 +835,15 @@ class AuthnResponse(StatusResponse): else: logger.error("Could not parse the assertion") return None - + def session_id(self): - """ Returns the SessionID of the response """ + """ Returns the SessionID of the response """ return self.response.in_response_to - + def id(self): """ Return the ID of the response """ return self.response.id - + def authn_info(self): res = [] for astat in self.assertion.authn_statement: @@ -858,7 +886,7 @@ class AuthnResponse(StatusResponse): 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()} - + def __str__(self): return "%s" % self.xmlstr @@ -892,7 +920,6 @@ class AuthnQueryResponse(AuthnResponse): def __init__(self, sec_context, attribute_converters, entity_id, return_addrs=None, timeslack=0, asynchop=False, test=False): - AuthnResponse.__init__(self, sec_context, attribute_converters, entity_id, return_addrs, timeslack=timeslack, asynchop=asynchop, test=test) @@ -910,7 +937,6 @@ class AttributeResponse(AuthnResponse): def __init__(self, sec_context, attribute_converters, entity_id, return_addrs=None, timeslack=0, asynchop=False, test=False): - AuthnResponse.__init__(self, sec_context, attribute_converters, entity_id, return_addrs, timeslack=timeslack, asynchop=asynchop, test=test) @@ -941,7 +967,6 @@ class ArtifactResponse(AuthnResponse): def __init__(self, sec_context, attribute_converters, entity_id, return_addrs=None, timeslack=0, asynchop=False, test=False): - AuthnResponse.__init__(self, sec_context, attribute_converters, entity_id, return_addrs, timeslack=timeslack, asynchop=asynchop, test=test) @@ -953,14 +978,15 @@ class ArtifactResponse(AuthnResponse): def response_factory(xmlstr, conf, return_addrs=None, outstanding_queries=None, timeslack=0, decode=True, request_id=0, origxml=None, - asynchop=True, allow_unsolicited=False, want_assertions_signed=False): + asynchop=True, allow_unsolicited=False, + want_assertions_signed=False): sec_context = security_context(conf) if not timeslack: try: timeslack = int(conf.accepted_time_diff) except TypeError: timeslack = 0 - + attribute_converters = conf.attribute_converters entity_id = conf.entityid extension_schema = conf.extension_schema @@ -985,9 +1011,10 @@ def response_factory(xmlstr, conf, return_addrs=None, outstanding_queries=None, asynchop=asynchop) logoutresp.update(response) return logoutresp - + return response + # =========================================================================== # A class of it's own