Example #1
0
    def _parse_response(self, xmlstr, response_cls, service, binding, **kwargs):
        """ Deal with a Response

        :param xmlstr: The response as a xml string
        :param response_cls: What type of response it is
        :param binding: What type of binding this message came through.
        :param kwargs: Extra key word arguments
        :return: None if the reply doesn't contain a valid SAML Response,
            otherwise the response.
        """

        response = None

        if self.config.accepted_time_diff:
            kwargs["timeslack"] = self.config.accepted_time_diff

        if "asynchop" not in kwargs:
            if binding in [BINDING_SOAP, BINDING_PAOS]:
                kwargs["asynchop"] = False
            else:
                kwargs["asynchop"] = True

        if xmlstr:
            if "return_addr" not in kwargs:
                if binding in [BINDING_HTTP_REDIRECT, BINDING_HTTP_POST]:
                    try:
                        # expected return address
                        kwargs["return_addr"] = self.config.endpoint(
                            service, binding=binding)[0]
                    except Exception:
                        logger.info("Not supposed to handle this!")
                        return None

            try:
                response = response_cls(self.sec, **kwargs)
            except Exception, exc:
                logger.info("%s" % exc)
                raise

            xmlstr = self.unravel(xmlstr, binding, response_cls.msgtype)
            if not xmlstr:  # Not a valid reponse
                return None

            logger.debug("XMLSTR: %s" % xmlstr)

            response = response.loads(xmlstr, False)

            if response:
                response = response.verify()

            if not response:
                return None

            logger.debug(response)
Example #2
0
class Entity(HTTPBase):
    def __init__(self, entity_type, config=None, config_file="",
                 virtual_organization=""):
        self.entity_type = entity_type
        self.users = None

        if config:
            self.config = config
        elif config_file:
            self.config = config_factory(entity_type, config_file)
        else:
            raise SAMLError("Missing configuration")

        HTTPBase.__init__(self, self.config.verify_ssl_cert,
                          self.config.ca_certs, self.config.key_file,
                          self.config.cert_file)

        if self.config.vorg:
            for vo in self.config.vorg.values():
                vo.sp = self

        self.metadata = self.config.metadata
        self.config.setup_logger()
        self.debug = self.config.debug
        self.seed = rndstr(32)

        self.sec = security_context(self.config)

        if virtual_organization:
            if isinstance(virtual_organization, basestring):
                self.vorg = self.config.vorg[virtual_organization]
            elif isinstance(virtual_organization, VirtualOrg):
                self.vorg = virtual_organization
        else:
            self.vorg = None

        self.artifact = {}
        if self.metadata:
            self.sourceid = self.metadata.construct_source_id()
        else:
            self.sourceid = {}

    def _issuer(self, entityid=None):
        """ Return an Issuer instance """
        if entityid:
            if isinstance(entityid, Issuer):
                return entityid
            else:
                return Issuer(text=entityid, format=NAMEID_FORMAT_ENTITY)
        else:
            return Issuer(text=self.config.entityid,
                          format=NAMEID_FORMAT_ENTITY)

    def apply_binding(self, binding, msg_str, destination="", relay_state="",
                      response=False, sign=False):
        """
        Construct the necessary HTTP arguments dependent on Binding

        :param binding: Which binding to use
        :param msg_str: The return message as a string (XML) if the message is
            to be signed it MUST contain the signature element.
        :param destination: Where to send the message
        :param relay_state: Relay_state if provided
        :param response: Which type of message this is
        :return: A dictionary
        """
        # unless if BINDING_HTTP_ARTIFACT
        if response:
            typ = "SAMLResponse"
        else:
            typ = "SAMLRequest"

        if binding == BINDING_HTTP_POST:
            logger.info("HTTP POST")
            info = self.use_http_form_post(msg_str, destination,
                                           relay_state, typ)
            info["url"] = destination
            info["method"] = "GET"
        elif binding == BINDING_HTTP_REDIRECT:
            logger.info("HTTP REDIRECT")
            info = self.use_http_get(msg_str, destination, relay_state, typ)
            info["url"] = destination
            info["method"] = "GET"
        elif binding == BINDING_SOAP or binding == BINDING_PAOS:
            info = self.use_soap(msg_str, destination, sign=sign)
        elif binding == BINDING_URI:
            info = self.use_http_uri(msg_str, typ, destination)
        elif binding == BINDING_HTTP_ARTIFACT:
            if response:
                info = self.use_http_artifact(msg_str, destination, relay_state)
                info["method"] = "GET"
                info["status"] = 302
            else:
                info = self.use_http_artifact(msg_str, destination, relay_state)
        else:
            raise SAMLError("Unknown binding type: %s" % binding)

        return info

    def pick_binding(self, service, bindings=None, descr_type="", request=None,
                     entity_id=""):
        if request and not entity_id:
            entity_id = request.issuer.text.strip()

        sfunc = getattr(self.metadata, service)

        if bindings is None:
            bindings = self.config.preferred_binding[service]

        if not descr_type:
            if self.entity_type == "sp":
                descr_type = "idpsso"
            else:
                descr_type = "spsso"

        for binding in bindings:
            try:
                srvs = sfunc(entity_id, binding, descr_type)
                if srvs:
                    return binding, destinations(srvs)[0]
            except UnsupportedBinding:
                pass

        logger.error("Failed to find consumer URL: %s, %s, %s" % (entity_id,
                                                                  bindings,
                                                                  descr_type))
        #logger.error("Bindings: %s" % bindings)
        #logger.error("Entities: %s" % self.metadata)

        raise SAMLError("Unkown entity or unsupported bindings")

    def message_args(self, message_id=0):
        if not message_id:
            message_id = sid(self.seed)

        return {"id": message_id, "version": VERSION,
                "issue_instant": instant(), "issuer": self._issuer()}

    def response_args(self, message, bindings=None, descr_type=""):
        """

        :param message: The message to which a reply is constructed
        :param bindings: Which bindings can be used.
        :param descr_type: Type of descriptor (spssp, idpsso, )
        :return: Dictionary
        """
        info = {"in_response_to": message.id}

        if isinstance(message, AuthnRequest):
            rsrv = "assertion_consumer_service"
            descr_type = "spsso"
            info["sp_entity_id"] = message.issuer.text
            info["name_id_policy"] = message.name_id_policy
        elif isinstance(message, LogoutRequest):
            rsrv = "single_logout_service"
        elif isinstance(message, AttributeQuery):
            info["sp_entity_id"] = message.issuer.text
            rsrv = "attribute_consuming_service"
            descr_type = "spsso"
        elif isinstance(message, ManageNameIDRequest):
            rsrv = "manage_name_id_service"
        # The once below are solely SOAP so no return destination needed
        elif isinstance(message, AssertionIDRequest):
            rsrv = ""
        elif isinstance(message, ArtifactResolve):
            rsrv = ""
        elif isinstance(message, AssertionIDRequest):
            rsrv = ""
        elif isinstance(message, NameIDMappingRequest):
            rsrv = ""
        else:
            raise SAMLError("No support for this type of query")

        if bindings == [BINDING_SOAP]:
            info["binding"] = BINDING_SOAP
            info["destination"] = ""
            return info

        if rsrv:
            if not descr_type:
                if self.entity_type == "sp":
                    descr_type = "idpsso"
                else:
                    descr_type = "spsso"

            binding, destination = self.pick_binding(rsrv, bindings,
                                                     descr_type=descr_type,
                                                     request=message)
            info["binding"] = binding
            info["destination"] = destination

        return info

    def unravel(self, txt, binding, msgtype="response"):
        #logger.debug("unravel '%s'" % txt)
        if binding not in [BINDING_HTTP_REDIRECT, BINDING_HTTP_POST,
                           BINDING_SOAP, BINDING_URI, None]:
            raise ValueError("Don't know how to handle '%s'" % binding)
        else:
            try:
                if binding == BINDING_HTTP_REDIRECT:
                    xmlstr = decode_base64_and_inflate(txt)
                elif binding == BINDING_HTTP_POST:
                    xmlstr = base64.b64decode(txt)
                elif binding == BINDING_SOAP:
                    func = getattr(soap, "parse_soap_enveloped_saml_%s" % msgtype)
                    xmlstr = func(txt)
                else:
                    xmlstr = txt
            except Exception:
                raise UnravelError()

        return xmlstr

    def parse_soap_message(self, text):
        """

        :param text: The SOAP message
        :return: A dictionary with two keys "body" and "header"
        """
        return class_instances_from_soap_enveloped_saml_thingies(text, [paos,
                                                                        ecp,
                                                                        samlp])

    def unpack_soap_message(self, text):
        """
        Picks out the parts of the SOAP message, body and headers apart
        :param text: The SOAP message
        :return: A dictionary with two keys "body"/"header"
        """
        return open_soap_envelope(text)

# --------------------------------------------------------------------------

    def sign(self, msg, mid=None, to_sign=None, sign_prepare=False):
        if msg.signature is None:
            msg.signature = pre_signature_part(msg.id, self.sec.my_cert, 1)

        if sign_prepare:
            return msg

        if mid is None:
            mid = msg.id

        try:
            to_sign.append([(class_name(msg), mid)])
        except AttributeError:
            to_sign = [(class_name(msg), mid)]

        logger.info("REQUEST: %s" % msg)
        return signed_instance_factory(msg, self.sec, to_sign)

    def _message(self, request_cls, destination=None, message_id=0,
                 consent=None, extensions=None, sign=False, sign_prepare=False,
                 **kwargs):
        """
        Some parameters appear in all requests so simplify by doing
        it in one place

        :param request_cls: The specific request type
        :param destination: The recipient
        :param message_id: A message identifier
        :param consent: Whether the principal have given her consent
        :param extensions: Possible extensions
        :param sign: Whether the request should be signed or not.
        :param sign_prepare: Whether the signature should be prepared or not.
        :param kwargs: Key word arguments specific to one request type
        :return: An instance of the request_cls
        """
        if not message_id:
            message_id = sid(self.seed)

        for key, val in self.message_args(message_id).items():
            if key not in kwargs:
                kwargs[key] = val

        req = request_cls(**kwargs)

        if destination:
            req.destination = destination

        if consent:
            req.consent = consent

        if extensions:
            req.extensions = extensions

        if sign:
            return self.sign(req, sign_prepare=sign_prepare)
        else:
            logger.info("REQUEST: %s" % req)
            return req

    def _filter_args(self, instance, extensions=None, **kwargs):
        args = {}
        if extensions is None:
            extensions = []

        allowed_attributes = instance.keys()
        for key, val in kwargs.items():
            if key in allowed_attributes:
                args[key] = val
            elif isinstance(val, SamlBase):
                # extension elements allowed ?
                extensions.append(element_to_extension_element(val))

        return args, extensions

    def _add_info(self, msg, **kwargs):
        """
        Add information to a SAML message. If the attribute is not part of
        what's defined in the SAML standard add it as an extension.

        :param msg:
        :param kwargs:
        :return:
        """

        args, extensions = self._filter_args(msg, **kwargs)
        for key, val in args.items():
            setattr(msg, key, val)

        if extensions:
            if msg.extension_elements:
                msg.extension_elements.extend(extensions)
            else:
                msg.extension_elements = extensions

    def _response(self, in_response_to, consumer_url=None, status=None,
                  issuer=None, sign=False, to_sign=None, **kwargs):
        """ Create a Response.

        :param in_response_to: The session identifier of the request
        :param consumer_url: The URL which should receive the response
        :param status: The status of the response
        :param issuer: The issuer of the response
        :param sign: Whether the response should be signed or not
        :param to_sign: If there are other parts to sign
        :param kwargs: Extra key word arguments
        :return: A Response instance
        """

        if not status:
            status = success_status_factory()

        _issuer = self._issuer(issuer)

        response = response_factory(issuer=_issuer,
                                    in_response_to=in_response_to,
                                    status=status)

        if consumer_url:
            response.destination = consumer_url

        self._add_info(response, **kwargs)

        if sign:
            self.sign(response, to_sign=to_sign)
        elif to_sign:
            return signed_instance_factory(response, self.sec, to_sign)
        else:
            return response

    def _status_response(self, response_class, issuer, status, sign=False,
                         **kwargs):
        """ Create a StatusResponse.

        :param response_class: Which subclass of StatusResponse that should be
            used
        :param issuer: The issuer of the response message
        :param status: The return status of the response operation
        :param sign: Whether the response should be signed or not
        :param kwargs: Extra arguments to the response class
        :return: Class instance or string representation of the instance
        """

        mid = sid()

        for key in ["destination", "binding"]:
            try:
                del kwargs[key]
            except KeyError:
                pass

        if not status:
            status = success_status_factory()

        response = response_class(issuer=issuer, id=mid, version=VERSION,
                                  issue_instant=instant(),
                                  status=status, **kwargs)

        if sign:
            return self.sign(response, mid)
        else:
            return response

    # ------------------------------------------------------------------------

    def srv2typ(self, service):
        for typ in ["aa", "pdp", "aq"]:
            if service in ENDPOINTS[typ]:
                if typ == "aa":
                    return "attribute_authority"
                elif typ == "aq":
                    return  "authn_authority"
                else:
                    return typ

    def _parse_request(self, xmlstr, request_cls, service, binding):
        """Parse a Request

        :param xmlstr: The request in its transport format
        :param request_cls: The type of requests I expect
        :param service:
        :param binding: Which binding that was used to transport the message
            to this entity.
        :return: A request instance
        """

        _log_info = logger.info
        _log_debug = logger.debug

        # The addresses I should receive messages like this on
        receiver_addresses = self.config.endpoint(service, binding,
                                                  self.entity_type)
        if not receiver_addresses and self.entity_type == "idp":
            for typ in ["aa", "aq", "pdp"]:
                receiver_addresses = self.config.endpoint(service, binding, typ)
                if receiver_addresses:
                    break

        _log_info("receiver addresses: %s" % receiver_addresses)
        _log_info("Binding: %s" % binding)

        try:
            timeslack = self.config.accepted_time_diff
            if not timeslack:
                timeslack = 0
        except AttributeError:
            timeslack = 0

        _request = request_cls(self.sec, receiver_addresses,
                               self.config.attribute_converters,
                               timeslack=timeslack)

        origdoc = xmlstr
        xmlstr = self.unravel(xmlstr, binding, request_cls.msgtype)
        _request = _request.loads(xmlstr, binding, origdoc=origdoc)

        _log_debug("Loaded request")

        if _request:
            _request = _request.verify()
            _log_debug("Verified request")

        if not _request:
            return None
        else:
            return _request

    # ------------------------------------------------------------------------

    def create_error_response(self, in_response_to, destination, info,
                              sign=False, issuer=None, **kwargs):
        """ Create a error response.

        :param in_response_to: The identifier of the message this is a response
            to.
        :param destination: The intended recipient of this message
        :param info: Either an Exception instance or a 2-tuple consisting of
            error code and descriptive text
        :param sign: Whether the response should be signed or not
        :param issuer: The issuer of the response
        :param kwargs: To capture key,value pairs I don't care about
        :return: A response instance
        """
        status = error_status_factory(info)

        return self._response(in_response_to, destination, status, issuer,
                              sign)

    # ------------------------------------------------------------------------

    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):
        """ Constructs a LogoutRequest

        :param destination: Destination of the request
        :param issuer_entity_id: The entity ID of the IdP the request is
            target at.
        :param subject_id: The identifier of the subject
        :param name_id: A NameID instance identifying the subject
        :param reason: An indication of the reason for the logout, in the
            form of a URI reference.
        :param expire: The time at which the request expires,
            after which the recipient may discard the message.
        :param message_id: Request identifier
        :param consent: Whether the principal have given her consent
        :param extensions: Possible extensions
        :param sign: Whether the query should be signed or not.
        :return: A LogoutRequest instance
        """

        if subject_id:
            if self.entity_type == "idp":
                name_id = NameID(text=self.users.get_entityid(subject_id,
                                                              issuer_entity_id,
                                                              False))
            else:
                name_id = NameID(text=subject_id)

        if not name_id:
            raise SAMLError("Missing subject identification")

        return self._message(LogoutRequest, destination, message_id,
                             consent, extensions, sign, name_id=name_id,
                             reason=reason, not_on_or_after=expire)

    def create_logout_response(self, request, bindings=None, status=None,
                               sign=False, issuer=None):
        """ Create a LogoutResponse.

        :param request: The request this is a response to
        :param bindings: Which bindings that can be used for the response
            If None the preferred bindings are gathered from the configuration
        :param status: The return status of the response operation
            If None the operation is regarded as a Success.
        :param issuer: The issuer of the message
        :return: HTTP args
        """

        rinfo = self.response_args(request, bindings)

        response = self._status_response(samlp.LogoutResponse, issuer, status,
                                         sign, **rinfo)

        logger.info("Response: %s" % (response,))

        return response

    def create_artifact_resolve(self, artifact, destination, sid, consent=None,
                                extensions=None, sign=False):
        """
        Create a ArtifactResolve request

        :param artifact:
        :param destination:
        :param sid: session id
        :param consent:
        :param extensions:
        :param sign:
        :return: The request message
        """

        artifact = Artifact(text=artifact)

        return self._message(ArtifactResolve, destination, sid,
                             consent, extensions, sign, artifact=artifact)

    def create_artifact_response(self, request, artifact, bindings=None,
                                 status=None, sign=False, issuer=None):
        """
        Create an ArtifactResponse
        :return:
        """

        rinfo = self.response_args(request, bindings)
        response = self._status_response(ArtifactResponse, issuer, status,
                                         sign=sign, **rinfo)

        msg = element_to_extension_element(self.artifact[artifact])
        response.extension_elements = [msg]

        logger.info("Response: %s" % (response,))

        return response

    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):
        """

        :param destination:
        :param message_id:
        :param consent:
        :param extensions:
        :param sign:
        :param name_id:
        :param new_id:
        :param encrypted_id:
        :param new_encrypted_id:
        :param terminate:
        :return:
        """
        kwargs = self.message_args(message_id)

        if name_id:
            kwargs["name_id"] = name_id
        elif encrypted_id:
            kwargs["encrypted_id"] = encrypted_id
        else:
            raise AttributeError(
                "One of NameID or EncryptedNameID has to be provided")

        if new_id:
            kwargs["new_id"] = new_id
        elif new_encrypted_id:
            kwargs["new_encrypted_id"] = new_encrypted_id
        elif terminate:
            kwargs["terminate"] = terminate
        else:
            raise AttributeError(
                "One of NewID, NewEncryptedNameID or Terminate has to be provided")

        return self._message(ManageNameIDRequest, destination, consent=consent,
                             extensions=extensions, sign=sign, **kwargs)

    def parse_manage_name_id_request(self, xmlstr, binding=BINDING_SOAP):
        """ Deal with a LogoutRequest

        :param xmlstr: The response as a xml string
        :param binding: What type of binding this message came through.
        :return: None if the reply doesn't contain a valid SAML LogoutResponse,
            otherwise the reponse if the logout was successful and None if it
            was not.
        """

        return self._parse_request(xmlstr, request.ManageNameIDRequest,
                                   "manage_name_id_service", binding)

    def create_manage_name_id_response(self, request, bindings=None,
                                       status=None, sign=False, issuer=None,
                                       **kwargs):

        rinfo = self.response_args(request, bindings)

        response = self._status_response(samlp.ManageNameIDResponse, issuer,
                                         status, sign, **rinfo)

        logger.info("Response: %s" % (response,))

        return response

    def parse_manage_name_id_request_response(self, string, 
                                              binding=BINDING_SOAP):
        return self._parse_response(string, response.ManageNameIDResponse,
                                    "manage_name_id_service", binding)

    # ------------------------------------------------------------------------

    def _parse_response(self, xmlstr, response_cls, service, binding, **kwargs):
        """ Deal with a Response

        :param xmlstr: The response as a xml string
        :param response_cls: What type of response it is
        :param binding: What type of binding this message came through.
        :param kwargs: Extra key word arguments
        :return: None if the reply doesn't contain a valid SAML Response,
            otherwise the response.
        """

        response = None

        if self.config.accepted_time_diff:
            kwargs["timeslack"] = self.config.accepted_time_diff

        if "asynchop" not in kwargs:
            if binding in [BINDING_SOAP, BINDING_PAOS]:
                kwargs["asynchop"] = False
            else:
                kwargs["asynchop"] = True

        if xmlstr:
            if "return_addr" not in kwargs:
                if binding in [BINDING_HTTP_REDIRECT, BINDING_HTTP_POST]:
                    try:
                        # expected return address
                        kwargs["return_addr"] = self.config.endpoint(
                            service, binding=binding)[0]
                    except Exception:
                        logger.info("Not supposed to handle this!")
                        return None

            try:
                response = response_cls(self.sec, **kwargs)
            except Exception, exc:
                logger.info("%s" % exc)
                raise

            xmlstr = self.unravel(xmlstr, binding, response_cls.msgtype)
            if not xmlstr:  # Not a valid reponse
                return None

            logger.debug("XMLSTR: %s" % xmlstr)

            try:
                response = response.loads(xmlstr, False)
            except Exception, err:
                if "not well-formed" in "%s" % err:
                    logger.error("Not well-formed XML")
                    return None

            if response:
                response = response.verify()

            if not response:
                return None
Example #3
0
    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
        :param response_cls: What type of response it is
        :param binding: What type of binding this message came through.
        :param kwargs: Extra key word arguments
        :return: None if the reply doesn't contain a valid SAML Response,
            otherwise the response.
        """

        response = None

        if self.config.accepted_time_diff:
            kwargs["timeslack"] = self.config.accepted_time_diff

        if "asynchop" not in kwargs:
            if binding in [BINDING_SOAP, BINDING_PAOS]:
                kwargs["asynchop"] = False
            else:
                kwargs["asynchop"] = True

        if xmlstr:
            if "return_addrs" not in kwargs:
                if binding in [BINDING_HTTP_REDIRECT, BINDING_HTTP_POST]:
                    try:
                        # expected return address
                        kwargs["return_addrs"] = self.config.endpoint(
                            service, binding=binding)
                    except Exception:
                        logger.info("Not supposed to handle this!")
                        return None

            try:
                response = response_cls(self.sec, **kwargs)
            except Exception as exc:
                logger.info("%s" % exc)
                raise

            xmlstr = self.unravel(xmlstr, binding, response_cls.msgtype)
            origxml = xmlstr
            if outstanding_certs is not None:
                _response = samlp.any_response_from_string(xmlstr)
                if len(_response.encrypted_assertion) > 0:
                    _, cert_file = make_temp(
                        "%s" %
                        outstanding_certs[_response.in_response_to]["key"],
                        decode=False)
                    cbxs = CryptoBackendXmlSec1(self.config.xmlsec_binary)
                    xmlstr = cbxs.decrypt(xmlstr, cert_file)

            if not xmlstr:  # Not a valid reponse
                return None

            try:
                response = response.loads(xmlstr, False, origxml=origxml)
            except SigverError as err:
                logger.error("Signature Error: %s" % err)
                raise
            except UnsolicitedResponse:
                logger.error("Unsolicited response")
                raise
            except Exception as err:
                if "not well-formed" in "%s" % err:
                    logger.error("Not well-formed XML")
                    raise

            logger.debug("XMLSTR: %s" % xmlstr)

            if hasattr(response.response, 'encrypted_assertion'):
                for encrypted_assertion in response.response\
                        .encrypted_assertion:
                    if encrypted_assertion.extension_elements is not None:
                        assertion_list = extension_elements_to_elements(
                            encrypted_assertion.extension_elements, [saml])
                        for assertion in assertion_list:
                            _assertion = saml.assertion_from_string(
                                str(assertion))
                            response.response.assertion.append(_assertion)

            if response:
                response = response.verify()

            if not response:
                return None

                #logger.debug(response)

        return response
Example #4
0
                if "not well-formed" in "%s" % err:
                    logger.error("Not well-formed XML")
                    raise

            logger.debug("XMLSTR: %s" % xmlstr)

            if hasattr(response.response, 'encrypted_assertion'):
                for encrypted_assertion in response.response.encrypted_assertion:
                    if encrypted_assertion.extension_elements is not None:
                        assertion_list = extension_elements_to_elements(encrypted_assertion.extension_elements, [saml])
                        for assertion in assertion_list:
                            _assertion = saml.assertion_from_string(str(assertion))
                            response.response.assertion.append(_assertion)

            if response:
                response = response.verify()

            if not response:
                return None

            #logger.debug(response)

        return response

    # ------------------------------------------------------------------------

    def parse_logout_request_response(self, xmlstr, binding=BINDING_SOAP):
        return self._parse_response(xmlstr, LogoutResponse,
                                    "single_logout_service", binding)

    # ------------------------------------------------------------------------
Example #5
0
    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
        :param response_cls: What type of response it is
        :param binding: What type of binding this message came through.
        :param kwargs: Extra key word arguments
        :return: None if the reply doesn't contain a valid SAML Response,
            otherwise the response.
        """

        response = None

        if self.config.accepted_time_diff:
            kwargs["timeslack"] = self.config.accepted_time_diff

        if "asynchop" not in kwargs:
            if binding in [BINDING_SOAP, BINDING_PAOS]:
                kwargs["asynchop"] = False
            else:
                kwargs["asynchop"] = True

        if xmlstr:
            if "return_addrs" not in kwargs:
                if binding in [BINDING_HTTP_REDIRECT, BINDING_HTTP_POST]:
                    try:
                        # expected return address
                        kwargs["return_addrs"] = self.config.endpoint(
                            service, binding=binding)
                    except Exception:
                        logger.info("Not supposed to handle this!")
                        return None

            try:
                response = response_cls(self.sec, **kwargs)
            except Exception as exc:
                logger.info("%s" % exc)
                raise

            xmlstr = self.unravel(xmlstr, binding, response_cls.msgtype)
            origxml = xmlstr
            if outstanding_certs is not None:
                _response = samlp.any_response_from_string(xmlstr)
                if len(_response.encrypted_assertion) > 0:
                    _, cert_file = make_temp(
                        "%s" % outstanding_certs[_response.in_response_to][
                            "key"],
                        decode=False)
                    cbxs = CryptoBackendXmlSec1(self.config.xmlsec_binary)
                    xmlstr = cbxs.decrypt(xmlstr, cert_file)

            if not xmlstr:  # Not a valid reponse
                return None

            try:
                response = response.loads(xmlstr, False, origxml=origxml)
            except SigverError as err:
                logger.error("Signature Error: %s" % err)
                raise
            except UnsolicitedResponse:
                logger.error("Unsolicited response")
                raise
            except Exception as err:
                if "not well-formed" in "%s" % err:
                    logger.error("Not well-formed XML")
                    raise

            logger.debug("XMLSTR: %s" % xmlstr)

            if hasattr(response.response, 'encrypted_assertion'):
                for encrypted_assertion in response.response\
                        .encrypted_assertion:
                    if encrypted_assertion.extension_elements is not None:
                        assertion_list = extension_elements_to_elements(
                            encrypted_assertion.extension_elements, [saml])
                        for assertion in assertion_list:
                            _assertion = saml.assertion_from_string(
                                str(assertion))
                            response.response.assertion.append(_assertion)

            if response:
                response = response.verify()

            if not response:
                return None

                #logger.debug(response)

        return response