Beispiel #1
0
    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):

        # ----------------------------------------
        # <ecp:Response
        # ----------------------------------------

        ecp_response = ecp.Response(assertion_consumer_service_url=acs_url)
        header = soapenv.Header()
        header.extension_elements = [element_to_extension_element(ecp_response)]

        # ----------------------------------------
        # <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)
        body = soapenv.Body()
        body.extension_elements = [element_to_extension_element(response)]

        soap_envelope = soapenv.Envelope(header=header, body=body)

        return "%s" % soap_envelope
Beispiel #2
0
def ecp_response(target_url, response):

    # ----------------------------------------
    # <ecp:Response
    # ----------------------------------------

    ecp_response = ecp.Response(assertion_consumer_service_url=target_url)
    header = soapenv.Header()
    header.extension_elements = [element_to_extension_element(ecp_response)]

    # ----------------------------------------
    # <samlp:Response
    # ----------------------------------------

    body = soapenv.Body()
    body.extension_elements = [element_to_extension_element(response)]

    soap_envelope = soapenv.Envelope(header=header, body=body)

    return "%s" % soap_envelope
Beispiel #3
0
def ecp_response(target_url, response):

    # ----------------------------------------
    # <ecp:Response
    # ----------------------------------------

    ecp_response = ecp.Response(assertion_consumer_service_url=target_url)
    header = soapenv.Header()
    header.extension_elements = [element_to_extension_element(ecp_response)]

    # ----------------------------------------
    # <samlp:Response
    # ----------------------------------------

    body = soapenv.Body()
    body.extension_elements = [element_to_extension_element(response)]

    soap_envelope = soapenv.Envelope(header=header, body=body)

    return "%s" % soap_envelope
Beispiel #4
0
    def redirect_to_auth(self, _cli, entity_id, came_from, sigalg=""):
        try:
            # Picks a binding to use for sending the Request to the IDP
            _binding, destination = _cli.pick_binding("single_sign_on_service",
                                                      self.bindings,
                                                      "idpsso",
                                                      entity_id=entity_id)
            logger.debug("binding: %s, destination: %s", _binding, destination)
            # Binding here is the response binding that is which binding the
            # IDP should use to return the response.
            acs = _cli.config.getattr("endpoints",
                                      "sp")["assertion_consumer_service"]
            # just pick one
            endp, return_binding = acs[0]

            extensions = None
            cert = None
            if _cli.config.generate_cert_func is not None:
                cert_str, req_key_str = _cli.config.generate_cert_func()
                cert = {"cert": cert_str, "key": req_key_str}
                spcertenc = SPCertEnc(x509_data=ds.X509Data(
                    x509_certificate=ds.X509Certificate(text=cert_str)))
                extensions = Extensions(extension_elements=[
                    element_to_extension_element(spcertenc)
                ])

            req_id, req = _cli.create_authn_request(
                destination,
                binding=return_binding,
                extensions=extensions,
                nameid_format=NAMEID_FORMAT_PERSISTENT,
            )
            _rstate = rndstr()
            self.cache.relay_state[_rstate] = came_from
            ht_args = _cli.apply_binding(_binding,
                                         "%s" % req,
                                         destination,
                                         relay_state=_rstate,
                                         sigalg=sigalg)
            _sid = req_id

            if cert is not None:
                self.cache.outstanding_certs[_sid] = cert

        except Exception as exc:
            logger.exception(exc)
            resp = ServiceError("Failed to construct the AuthnRequest: %s" %
                                exc)
            return resp

        # remember the request
        self.cache.outstanding_queries[_sid] = came_from
        return self.response(_binding, ht_args, do_not_start_response=True)
Beispiel #5
0
    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):

        # ----------------------------------------
        # <ecp:Response
        # ----------------------------------------

        ecp_response = ecp.Response(assertion_consumer_service_url=acs_url)
        header = soapenv.Header()
        header.extension_elements = [
            element_to_extension_element(ecp_response)
        ]

        # ----------------------------------------
        # <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)
        body = soapenv.Body()
        body.extension_elements = [element_to_extension_element(response)]

        soap_envelope = soapenv.Envelope(header=header, body=body)

        return "%s" % soap_envelope
Beispiel #6
0
    def redirect_to_auth(self, _cli, entity_id, came_from, sigalg=""):
        try:
            # Picks a binding to use for sending the Request to the IDP
            _binding, destination = _cli.pick_binding(
                "single_sign_on_service", self.bindings, "idpsso", entity_id=entity_id
            )
            logger.debug("binding: %s, destination: %s", _binding, destination)
            # Binding here is the response binding that is which binding the
            # IDP should use to return the response.
            acs = _cli.config.getattr("endpoints", "sp")["assertion_consumer_service"]
            # just pick one
            endp, return_binding = acs[0]

            extensions = None
            cert = None
            if _cli.config.generate_cert_func is not None:
                cert_str, req_key_str = _cli.config.generate_cert_func()
                cert = {"cert": cert_str, "key": req_key_str}
                spcertenc = SPCertEnc(
                    x509_data=ds.X509Data(
                        x509_certificate=ds.X509Certificate(text=cert_str)
                    )
                )
                extensions = Extensions(
                    extension_elements=[element_to_extension_element(spcertenc)]
                )

            req_id, req = _cli.create_authn_request(
                destination,
                binding=return_binding,
                extensions=extensions,
                nameid_format=NAMEID_FORMAT_PERSISTENT,
            )
            _rstate = rndstr()
            self.cache.relay_state[_rstate] = came_from
            ht_args = _cli.apply_binding(
                _binding, "%s" % req, destination, relay_state=_rstate, sigalg=sigalg
            )
            _sid = req_id

            if cert is not None:
                self.cache.outstanding_certs[_sid] = cert

        except Exception as exc:
            logger.exception(exc)
            resp = ServiceError("Failed to construct the AuthnRequest: %s" % exc)
            return resp

        # remember the request
        self.cache.outstanding_queries[_sid] = came_from
        return self.response(_binding, ht_args, do_not_start_response=True)
Beispiel #7
0
def test_attribute_element_to_extension_element():
    attr = create_class_from_xml_string(Attribute, saml2_data.TEST_ATTRIBUTE)
    ee = saml2_tophat.element_to_extension_element(attr)
    print(ee.__dict__)
    assert ee.tag == "Attribute"
    assert ee.namespace == 'urn:oasis:names:tc:SAML:2.0:assertion'
    assert _eq(ee.attributes.keys(), ['FriendlyName', 'Name', 'NameFormat'])
    assert ee.attributes["FriendlyName"] == 'test attribute'
    assert ee.attributes["Name"] == "testAttribute"
    assert ee.attributes["NameFormat"] == \
           'urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified'
    assert len(ee.children) == 2
    for child in ee.children:
        # children are also extension element instances
        assert child.namespace == 'urn:oasis:names:tc:SAML:2.0:assertion'
        assert child.tag == "AttributeValue"
Beispiel #8
0
def test_attribute_element_to_extension_element():
    attr = create_class_from_xml_string(Attribute, saml2_data.TEST_ATTRIBUTE)
    ee = saml2_tophat.element_to_extension_element(attr)
    print(ee.__dict__)
    assert ee.tag == "Attribute"
    assert ee.namespace == 'urn:oasis:names:tc:SAML:2.0:assertion'
    assert _eq(ee.attributes.keys(), ['FriendlyName', 'Name', 'NameFormat'])
    assert ee.attributes["FriendlyName"] == 'test attribute'
    assert ee.attributes["Name"] == "testAttribute"
    assert ee.attributes["NameFormat"] == \
           'urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified'
    assert len(ee.children) == 2
    for child in ee.children:
        # children are also extension element instances
        assert child.namespace == 'urn:oasis:names:tc:SAML:2.0:assertion'
        assert child.tag == "AttributeValue"
Beispiel #9
0
def from_dict(val, onts, mdb_safe=False):
    """
    Converts a dictionary into a pysaml2 object
    :param val: A dictionary
    :param onts: Dictionary of schemas to use in the conversion
    :return: The pysaml2 object instance
    """
    if isinstance(val, dict):
        if "__class__" in val:
            ns, typ = val["__class__"].split("&")
            cls = getattr(onts[ns], typ)
            if cls is md.Extensions:
                lv = []
                for key, ditems in val.items():
                    if key in EXP_SKIP:
                        continue
                    for item in ditems:
                        ns, typ = item["__class__"].split("&")
                        cls = getattr(onts[ns], typ)
                        kwargs = _kwa(item, onts, mdb_safe)
                        inst = cls(**kwargs)
                        lv.append(element_to_extension_element(inst))
                return lv
            else:
                kwargs = _kwa(val, onts, mdb_safe)
                inst = cls(**kwargs)
            return inst
        else:
            res = {}
            for key, v in val.items():
                if mdb_safe:
                    key = key.replace("__", ".")
                res[key] = from_dict(v, onts)
            return res
    elif isinstance(val, six.string_types):
        return val
    elif isinstance(val, list):
        return [from_dict(v, onts) for v in val]
    else:
        return val
Beispiel #10
0
def from_dict(val, onts, mdb_safe=False):
    """
    Converts a dictionary into a pysaml2 object
    :param val: A dictionary
    :param onts: Dictionary of schemas to use in the conversion
    :return: The pysaml2 object instance
    """
    if isinstance(val, dict):
        if "__class__" in val:
            ns, typ = val["__class__"].split("&")
            cls = getattr(onts[ns], typ)
            if cls is md.Extensions:
                lv = []
                for key, ditems in val.items():
                    if key in EXP_SKIP:
                        continue
                    for item in ditems:
                        ns, typ = item["__class__"].split("&")
                        cls = getattr(onts[ns], typ)
                        kwargs = _kwa(item, onts, mdb_safe)
                        inst = cls(**kwargs)
                        lv.append(element_to_extension_element(inst))
                return lv
            else:
                kwargs = _kwa(val, onts, mdb_safe)
                inst = cls(**kwargs)
            return inst
        else:
            res = {}
            for key, v in val.items():
                if mdb_safe:
                    key = key.replace("__", ".")
                res[key] = from_dict(v, onts)
            return res
    elif isinstance(val, six.string_types):
        return val
    elif isinstance(val, list):
        return [from_dict(v, onts) for v in val]
    else:
        return val
Beispiel #11
0
    def challenge(self, environ, _status, _app_headers, _forget_headers):

        _cli = self.saml_client

        if "REMOTE_USER" in environ:
            name_id = decode(environ["REMOTE_USER"])

            _cli = self.saml_client
            path_info = environ["PATH_INFO"]

            if "samlsp.logout" in environ:
                responses = _cli.global_logout(name_id)
                return self._handle_logout(responses)

        if "samlsp.pending" in environ:
            response = environ["samlsp.pending"]
            if isinstance(response, HTTPRedirection):
                response.headers += _forget_headers
            return response

        # logger = environ.get('repoze.who.logger','')

        # Which page was accessed to get here
        came_from = construct_came_from(environ)
        environ["myapp.came_from"] = came_from
        logger.debug("[sp.challenge] RelayState >> '%s'", came_from)

        # Am I part of a virtual organization or more than one ?
        try:
            vorg_name = environ["myapp.vo"]
        except KeyError:
            try:
                vorg_name = _cli.vorg._name
            except AttributeError:
                vorg_name = ""

        logger.info("[sp.challenge] VO: %s", vorg_name)

        # If more than one idp and if none is selected, I have to do wayf
        (done, response) = self._pick_idp(environ, came_from)
        # Three cases: -1 something went wrong or Discovery service used
        #               0 I've got an IdP to send a request to
        #               >0 ECP in progress
        logger.debug("_idp_pick returned: %s", done)
        if done == -1:
            return response
        elif done > 0:
            self.outstanding_queries[done] = came_from
            return ECP_response(response)
        else:
            entity_id = response
            logger.info("[sp.challenge] entity_id: %s", entity_id)
            # Do the AuthnRequest
            _binding = BINDING_HTTP_REDIRECT
            try:
                srvs = _cli.metadata.single_sign_on_service(entity_id, _binding)
                logger.debug("srvs: %s", srvs)
                dest = srvs[0]["location"]
                logger.debug("destination: %s", dest)

                extensions = None
                cert = None

                if _cli.config.generate_cert_func is not None:
                    cert_str, req_key_str = _cli.config.generate_cert_func()
                    cert = {"cert": cert_str, "key": req_key_str}
                    spcertenc = SPCertEnc(
                        x509_data=ds.X509Data(
                            x509_certificate=ds.X509Certificate(text=cert_str)
                        )
                    )
                    extensions = Extensions(
                        extension_elements=[element_to_extension_element(spcertenc)]
                    )

                if _cli.authn_requests_signed:
                    _sid = saml2_tophat.s_utils.sid()
                    req_id, msg_str = _cli.create_authn_request(
                        dest,
                        vorg=vorg_name,
                        sign=_cli.authn_requests_signed,
                        message_id=_sid,
                        extensions=extensions,
                    )
                    _sid = req_id
                else:
                    req_id, req = _cli.create_authn_request(
                        dest, vorg=vorg_name, sign=False, extensions=extensions
                    )
                    msg_str = "%s" % req
                    _sid = req_id

                if cert is not None:
                    self.outstanding_certs[_sid] = cert

                ht_args = _cli.apply_binding(
                    _binding,
                    msg_str,
                    destination=dest,
                    relay_state=came_from,
                    sign=_cli.authn_requests_signed,
                )

                logger.debug("ht_args: %s", ht_args)
            except Exception as exc:
                logger.exception(exc)
                raise Exception("Failed to construct the AuthnRequest: %s" % exc)

            try:
                ret = _cli.config.getattr("endpoints", "sp")["discovery_response"][0][0]
                if (environ["PATH_INFO"]) in ret and ret.split(environ["PATH_INFO"])[
                    1
                ] == "":
                    query = parse_qs(environ["QUERY_STRING"])
                    sid = query["sid"][0]
                    came_from = self.outstanding_queries[sid]
            except:
                pass
            # remember the request
            self.outstanding_queries[_sid] = came_from

            if not ht_args["data"] and ht_args["headers"][0][0] == "Location":
                logger.debug("redirect to: %s", ht_args["headers"][0][1])
                return HTTPSeeOther(headers=ht_args["headers"])
            else:
                return ht_args["data"]
Beispiel #12
0
def ecp_auth_request(cls, entityid=None, relay_state="", sign=False):
    """ Makes an authentication request.

    :param entityid: The entity ID of the IdP to send the request to
    :param relay_state: To where the user should be returned after
        successfull log in.
    :param sign: Whether the request should be signed or not.
    :return: AuthnRequest response
    """

    eelist = []

    # ----------------------------------------
    # <paos:Request>
    # ----------------------------------------
    my_url = cls.service_urls(BINDING_PAOS)[0]

    # must_understand and actor according to the standard
    #
    paos_request = paos.Request(must_understand="1", actor=ACTOR,
                                response_consumer_url=my_url,
                                service=SERVICE)

    eelist.append(element_to_extension_element(paos_request))

    # ----------------------------------------
    # <samlp:AuthnRequest>
    # ----------------------------------------

    logger.info("entityid: %s, binding: %s" % (entityid, BINDING_SOAP))

    location = cls._sso_location(entityid, binding=BINDING_SOAP)
    req_id, authn_req = cls.create_authn_request(
        location, binding=BINDING_PAOS, service_url_binding=BINDING_PAOS)

    body = soapenv.Body()
    body.extension_elements = [element_to_extension_element(authn_req)]

    # ----------------------------------------
    # <ecp:Request>
    # ----------------------------------------

#        idp = samlp.IDPEntry(
#            provider_id = "https://idp.example.org/entity",
#            name = "Example identity provider",
#            loc = "https://idp.example.org/saml2/sso",
#            )
#
#        idp_list = samlp.IDPList(idp_entry= [idp])

    idp_list = None
    ecp_request = ecp.Request(
        actor=ACTOR,
        must_understand="1",
        provider_name=None,
        issuer=saml.Issuer(text=authn_req.issuer.text),
        idp_list=idp_list)

    eelist.append(element_to_extension_element(ecp_request))

    # ----------------------------------------
    # <ecp:RelayState>
    # ----------------------------------------

    relay_state = ecp.RelayState(actor=ACTOR, must_understand="1",
                                 text=relay_state)

    eelist.append(element_to_extension_element(relay_state))

    header = soapenv.Header()
    header.extension_elements = eelist

    # ----------------------------------------
    # The SOAP envelope
    # ----------------------------------------

    soap_envelope = soapenv.Envelope(header=header, body=body)

    return req_id, "%s" % soap_envelope
Beispiel #13
0
from pathutils import full_path

__author__ = 'roland'

conf = config.SPConfig()
conf.load_file("server_conf")
client = Saml2Client(conf)

# place a certificate in an authn request
cert = read_cert_from_file(full_path("test.pem"), "pem")

spcertenc = SPCertEnc(x509_data=ds.X509Data(
    x509_certificate=ds.X509Certificate(text=cert)))

extensions = Extensions(
    extension_elements=[element_to_extension_element(spcertenc)])

req_id, req = client.create_authn_request(
    "http://www.example.com/sso",
    "urn:mace:example.com:it:tek",
    nameid_format=saml.NAMEID_FORMAT_PERSISTENT,
    message_id="666",
    extensions=extensions)

print(req)

# Get a certificate from an authn request

xml = "%s" % req

parsed = authn_request_from_string(xml)
Beispiel #14
0
    def challenge(self, environ, _status, _app_headers, _forget_headers):

        _cli = self.saml_client

        if "REMOTE_USER" in environ:
            name_id = decode(environ["REMOTE_USER"])

            _cli = self.saml_client
            path_info = environ["PATH_INFO"]

            if "samlsp.logout" in environ:
                responses = _cli.global_logout(name_id)
                return self._handle_logout(responses)

        if "samlsp.pending" in environ:
            response = environ["samlsp.pending"]
            if isinstance(response, HTTPRedirection):
                response.headers += _forget_headers
            return response

        # logger = environ.get('repoze.who.logger','')

        # Which page was accessed to get here
        came_from = construct_came_from(environ)
        environ["myapp.came_from"] = came_from
        logger.debug("[sp.challenge] RelayState >> '%s'", came_from)

        # Am I part of a virtual organization or more than one ?
        try:
            vorg_name = environ["myapp.vo"]
        except KeyError:
            try:
                vorg_name = _cli.vorg._name
            except AttributeError:
                vorg_name = ""

        logger.info("[sp.challenge] VO: %s", vorg_name)

        # If more than one idp and if none is selected, I have to do wayf
        (done, response) = self._pick_idp(environ, came_from)
        # Three cases: -1 something went wrong or Discovery service used
        #               0 I've got an IdP to send a request to
        #               >0 ECP in progress
        logger.debug("_idp_pick returned: %s", done)
        if done == -1:
            return response
        elif done > 0:
            self.outstanding_queries[done] = came_from
            return ECP_response(response)
        else:
            entity_id = response
            logger.info("[sp.challenge] entity_id: %s", entity_id)
            # Do the AuthnRequest
            _binding = BINDING_HTTP_REDIRECT
            try:
                srvs = _cli.metadata.single_sign_on_service(
                    entity_id, _binding)
                logger.debug("srvs: %s", srvs)
                dest = srvs[0]["location"]
                logger.debug("destination: %s", dest)

                extensions = None
                cert = None

                if _cli.config.generate_cert_func is not None:
                    cert_str, req_key_str = _cli.config.generate_cert_func()
                    cert = {"cert": cert_str, "key": req_key_str}
                    spcertenc = SPCertEnc(x509_data=ds.X509Data(
                        x509_certificate=ds.X509Certificate(text=cert_str)))
                    extensions = Extensions(extension_elements=[
                        element_to_extension_element(spcertenc)
                    ])

                if _cli.authn_requests_signed:
                    _sid = saml2_tophat.s_utils.sid()
                    req_id, msg_str = _cli.create_authn_request(
                        dest,
                        vorg=vorg_name,
                        sign=_cli.authn_requests_signed,
                        message_id=_sid,
                        extensions=extensions,
                    )
                    _sid = req_id
                else:
                    req_id, req = _cli.create_authn_request(
                        dest,
                        vorg=vorg_name,
                        sign=False,
                        extensions=extensions)
                    msg_str = "%s" % req
                    _sid = req_id

                if cert is not None:
                    self.outstanding_certs[_sid] = cert

                ht_args = _cli.apply_binding(
                    _binding,
                    msg_str,
                    destination=dest,
                    relay_state=came_from,
                    sign=_cli.authn_requests_signed,
                )

                logger.debug("ht_args: %s", ht_args)
            except Exception as exc:
                logger.exception(exc)
                raise Exception("Failed to construct the AuthnRequest: %s" %
                                exc)

            try:
                ret = _cli.config.getattr("endpoints",
                                          "sp")["discovery_response"][0][0]
                if (environ["PATH_INFO"]) in ret and ret.split(
                        environ["PATH_INFO"])[1] == "":
                    query = parse_qs(environ["QUERY_STRING"])
                    sid = query["sid"][0]
                    came_from = self.outstanding_queries[sid]
            except:
                pass
            # remember the request
            self.outstanding_queries[_sid] = came_from

            if not ht_args["data"] and ht_args["headers"][0][0] == "Location":
                logger.debug("redirect to: %s", ht_args["headers"][0][1])
                return HTTPSeeOther(headers=ht_args["headers"])
            else:
                return ht_args["data"]
Beispiel #15
0
__author__ = 'roland'

conf = config.SPConfig()
conf.load_file("server_conf")
client = Saml2Client(conf)

# place a certificate in an authn request
cert = read_cert_from_file(full_path("test.pem"), "pem")

spcertenc = SPCertEnc(
    x509_data=ds.X509Data(
        x509_certificate=ds.X509Certificate(text=cert)))

extensions = Extensions(
    extension_elements=[element_to_extension_element(spcertenc)])

req_id, req = client.create_authn_request(
    "http://www.example.com/sso",
    "urn:mace:example.com:it:tek",
    nameid_format=saml.NAMEID_FORMAT_PERSISTENT,
    message_id="666",
    extensions=extensions)


print(req)

# Get a certificate from an authn request

xml = "%s" % req
Beispiel #16
0
def ecp_auth_request(cls, entityid=None, relay_state="", sign=False):
    """ Makes an authentication request.

    :param entityid: The entity ID of the IdP to send the request to
    :param relay_state: To where the user should be returned after
        successfull log in.
    :param sign: Whether the request should be signed or not.
    :return: AuthnRequest response
    """

    eelist = []

    # ----------------------------------------
    # <paos:Request>
    # ----------------------------------------
    my_url = cls.service_urls(BINDING_PAOS)[0]

    # must_understand and actor according to the standard
    #
    paos_request = paos.Request(must_understand="1",
                                actor=ACTOR,
                                response_consumer_url=my_url,
                                service=SERVICE)

    eelist.append(element_to_extension_element(paos_request))

    # ----------------------------------------
    # <samlp:AuthnRequest>
    # ----------------------------------------

    logger.info("entityid: %s, binding: %s" % (entityid, BINDING_SOAP))

    location = cls._sso_location(entityid, binding=BINDING_SOAP)
    req_id, authn_req = cls.create_authn_request(
        location, binding=BINDING_PAOS, service_url_binding=BINDING_PAOS)

    body = soapenv.Body()
    body.extension_elements = [element_to_extension_element(authn_req)]

    # ----------------------------------------
    # <ecp:Request>
    # ----------------------------------------

    #        idp = samlp.IDPEntry(
    #            provider_id = "https://idp.example.org/entity",
    #            name = "Example identity provider",
    #            loc = "https://idp.example.org/saml2/sso",
    #            )
    #
    #        idp_list = samlp.IDPList(idp_entry= [idp])

    idp_list = None
    ecp_request = ecp.Request(actor=ACTOR,
                              must_understand="1",
                              provider_name=None,
                              issuer=saml.Issuer(text=authn_req.issuer.text),
                              idp_list=idp_list)

    eelist.append(element_to_extension_element(ecp_request))

    # ----------------------------------------
    # <ecp:RelayState>
    # ----------------------------------------

    relay_state = ecp.RelayState(actor=ACTOR,
                                 must_understand="1",
                                 text=relay_state)

    eelist.append(element_to_extension_element(relay_state))

    header = soapenv.Header()
    header.extension_elements = eelist

    # ----------------------------------------
    # The SOAP envelope
    # ----------------------------------------

    soap_envelope = soapenv.Envelope(header=header, body=body)

    return req_id, "%s" % soap_envelope