Exemple #1
0
def authn_statement(authn_class=None,
                    authn_auth=None,
                    authn_decl=None,
                    authn_decl_ref=None,
                    authn_instant="",
                    subject_locality="",
                    session_not_on_or_after=None):
    """
    Construct the AuthnStatement
    :param authn_class: Authentication Context Class reference
    :param authn_auth: Authenticating Authority
    :param authn_decl: Authentication Context Declaration
    :param authn_decl_ref: Authentication Context Declaration reference
    :param authn_instant: When the Authentication was performed.
        Assumed to be seconds since the Epoch.
    :param subject_locality: Specifies the DNS domain name and IP address
        for the system from which the assertion subject was apparently
        authenticated.
    :return: An AuthnContext instance
    """
    if authn_instant:
        _instant = instant(time_stamp=authn_instant)
    else:
        _instant = instant()

    if authn_class:
        res = factory(saml.AuthnStatement,
                      authn_instant=_instant,
                      session_index=sid(),
                      session_not_on_or_after=session_not_on_or_after,
                      authn_context=_authn_context_class_ref(
                          authn_class, authn_auth))
    elif authn_decl:
        res = factory(saml.AuthnStatement,
                      authn_instant=_instant,
                      session_index=sid(),
                      session_not_on_or_after=session_not_on_or_after,
                      authn_context=_authn_context_decl(
                          authn_decl, authn_auth))
    elif authn_decl_ref:
        res = factory(saml.AuthnStatement,
                      authn_instant=_instant,
                      session_index=sid(),
                      session_not_on_or_after=session_not_on_or_after,
                      authn_context=_authn_context_decl_ref(
                          authn_decl_ref, authn_auth))
    else:
        res = factory(saml.AuthnStatement,
                      authn_instant=_instant,
                      session_index=sid(),
                      session_not_on_or_after=session_not_on_or_after)

    if subject_locality:
        res.subject_locality = saml.SubjectLocality(text=subject_locality)

    return res
Exemple #2
0
def authn_statement(authn_class=None, authn_auth=None,
                    authn_decl=None, authn_decl_ref=None, authn_instant="",
                    subject_locality="", session_not_on_or_after=None):
    """
    Construct the AuthnStatement
    :param authn_class: Authentication Context Class reference
    :param authn_auth: Authenticating Authority
    :param authn_decl: Authentication Context Declaration
    :param authn_decl_ref: Authentication Context Declaration reference
    :param authn_instant: When the Authentication was performed.
        Assumed to be seconds since the Epoch.
    :param subject_locality: Specifies the DNS domain name and IP address
        for the system from which the assertion subject was apparently
        authenticated.
    :return: An AuthnContext instance
    """
    if authn_instant:
        _instant = instant(time_stamp=authn_instant)
    else:
        _instant = instant()

    if authn_class:
        res = factory(
            saml.AuthnStatement,
            authn_instant=_instant,
            session_index=sid(),
            session_not_on_or_after=session_not_on_or_after,
            authn_context=_authn_context_class_ref(
                authn_class, authn_auth))
    elif authn_decl:
        res = factory(
            saml.AuthnStatement,
            authn_instant=_instant,
            session_index=sid(),
            session_not_on_or_after=session_not_on_or_after,
            authn_context=_authn_context_decl(authn_decl, authn_auth))
    elif authn_decl_ref:
        res = factory(
            saml.AuthnStatement,
            authn_instant=_instant,
            session_index=sid(),
            session_not_on_or_after=session_not_on_or_after,
            authn_context=_authn_context_decl_ref(authn_decl_ref,
                                                  authn_auth))
    else:
        res = factory(
            saml.AuthnStatement,
            authn_instant=_instant,
            session_index=sid(),
            session_not_on_or_after=session_not_on_or_after)

    if subject_locality:
        res.subject_locality = saml.SubjectLocality(text=subject_locality)

    return res
def test_create_artifact_resolve():
    b64art = create_artifact(SP, "aabbccddeeffgghhiijj", 1)
    artifact = base64.b64decode(b64art)

    #assert artifact[:2] == '\x00\x04'
    #assert int(artifact[2:4]) == 0
    #
    s = sha1(SP.encode('ascii'))
    assert artifact[4:24] == s.digest()

    with closing(Server(config_file="idp_all_conf")) as idp:
        typecode = artifact[:2]
        assert typecode == ARTIFACT_TYPECODE

        destination = idp.artifact2destination(b64art, "spsso")

        msg_id, msg = idp.create_artifact_resolve(b64art, destination, sid())

        print(msg)

        args = idp.use_soap(msg, destination, None, False)

        sp = Saml2Client(config_file="servera_conf")

        ar = sp.parse_artifact_resolve(args["data"])

        print(ar)

        assert ar.artifact.text == b64art
def test_create_artifact_resolve():
    b64art = create_artifact(SP, "aabbccddeeffgghhiijj", 1)
    artifact = base64.b64decode(b64art)

    #assert artifact[:2] == '\x00\x04'
    #assert int(artifact[2:4]) == 0
    #
    s = sha1(SP.encode('ascii'))
    assert artifact[4:24] == s.digest()

    with closing(Server(config_file="idp_all_conf")) as idp:
        typecode = artifact[:2]
        assert typecode == ARTIFACT_TYPECODE

        destination = idp.artifact2destination(b64art, "spsso")

        msg_id, msg = idp.create_artifact_resolve(b64art, destination, sid())

        print(msg)

        args = idp.use_soap(msg, destination, None, False)

        sp = Saml2Client(config_file="servera_conf")

        ar = sp.parse_artifact_resolve(args["data"])

        print(ar)

        assert ar.artifact.text == b64art
Exemple #5
0
def entities_descriptor(eds, valid_for, name, ident, sign, secc, sign_alg=None,
                        digest_alg=None):
    entities = md.EntitiesDescriptor(entity_descriptor=eds)
    if valid_for:
        entities.valid_until = in_a_while(hours=valid_for)
    if name:
        entities.name = name
    if ident:
        entities.id = ident

    if sign:
        if not ident:
            ident = sid()

        if not secc.key_file:
            raise SAMLError("If you want to do signing you should define " +
                            "a key to sign with")

        if not secc.my_cert:
            raise SAMLError("If you want to do signing you should define " +
                            "where your public key are")

        entities.signature = pre_signature_part(ident, secc.my_cert, 1,
                                                sign_alg=sign_alg,
                                                digest_alg=digest_alg)
        entities.id = ident
        xmldoc = secc.sign_statement("%s" % entities, class_name(entities))
        entities = md.entities_descriptor_from_string(xmldoc)
    else:
        xmldoc = None

    return entities, xmldoc
Exemple #6
0
def sign_entity_descriptor(edesc, ident, secc, sign_alg=None, digest_alg=None):
    """

    :param edesc: EntityDescriptor instance
    :param ident: EntityDescriptor identifier
    :param secc: Security context
    :return: Tuple with EntityDescriptor instance and Signed XML document
    """

    if not ident:
        ident = sid()

    edesc.signature = pre_signature_part(ident, secc.my_cert, 1, sign_alg=sign_alg, digest_alg=digest_alg)
    edesc.id = ident
    xmldoc = secc.sign_statement("%s" % edesc, class_name(edesc))
    edesc = md.entity_descriptor_from_string(xmldoc)
    return edesc, xmldoc
Exemple #7
0
def sign_entity_descriptor(edesc, ident, secc, sign_alg=None, digest_alg=None):
    """

    :param edesc: EntityDescriptor instance
    :param ident: EntityDescriptor identifier
    :param secc: Security context
    :return: Tuple with EntityDescriptor instance and Signed XML document
    """

    if not ident:
        ident = sid()

    edesc.signature = pre_signature_part(ident,
                                         secc.my_cert,
                                         1,
                                         sign_alg=sign_alg,
                                         digest_alg=digest_alg)
    edesc.id = ident
    xmldoc = secc.sign_statement("%s" % edesc, class_name(edesc))
    edesc = md.entity_descriptor_from_string(xmldoc)
    return edesc, xmldoc
Exemple #8
0
def entities_descriptor(eds,
                        valid_for,
                        name,
                        ident,
                        sign,
                        secc,
                        sign_alg=None,
                        digest_alg=None):
    entities = md.EntitiesDescriptor(entity_descriptor=eds)
    if valid_for:
        entities.valid_until = in_a_while(hours=valid_for)
    if name:
        entities.name = name
    if ident:
        entities.id = ident

    if sign:
        if not ident:
            ident = sid()

        if not secc.key_file:
            raise SAMLError("If you want to do signing you should define " +
                            "a key to sign with")

        if not secc.my_cert:
            raise SAMLError("If you want to do signing you should define " +
                            "where your public key are")

        entities.signature = pre_signature_part(ident,
                                                secc.my_cert,
                                                1,
                                                sign_alg=sign_alg,
                                                digest_alg=digest_alg)
        entities.id = ident
        xmldoc = secc.sign_statement("%s" % entities, class_name(entities))
        entities = md.entities_descriptor_from_string(xmldoc)
    else:
        xmldoc = None

    return entities, xmldoc
Exemple #9
0
    def _pick_idp(self, environ, came_from):
        """
        If more than one idp and if none is selected, I have to do wayf or
        disco
        """

        # check headers to see if it's an ECP request
        #        headers = {
        #                    'Accept' : 'text/html; application/vnd.paos+xml',
        #                    'PAOS'   : 'ver="%s";"%s"' % (paos.NAMESPACE,
        # SERVICE)
        #                    }

        _cli = self.saml_client

        logger.info("[_pick_idp] %s", environ)
        if "HTTP_PAOS" in environ:
            if environ["HTTP_PAOS"] == PAOS_HEADER_INFO:
                if MIME_PAOS in environ["HTTP_ACCEPT"]:
                    # Where should I redirect the user to
                    # entityid -> the IdP to use
                    # relay_state -> when back from authentication

                    logger.info("- ECP client detected -")

                    _relay_state = construct_came_from(environ)
                    _entityid = _cli.config.ecp_endpoint(environ["REMOTE_ADDR"])

                    if not _entityid:
                        return -1, HTTPInternalServerError(detail="No IdP to talk to")
                    logger.info("IdP to talk to: %s", _entityid)
                    return ecp.ecp_auth_request(_cli, _entityid, _relay_state)
                else:
                    return -1, HTTPInternalServerError(detail="Faulty Accept header")
            else:
                return -1, HTTPInternalServerError(detail="unknown ECP version")

        idps = self.metadata.with_descriptor("idpsso")

        logger.info("IdP URL: %s", idps)

        idp_entity_id = query = None

        for key in ["s2repoze.body", "QUERY_STRING"]:
            query = environ.get(key)
            if query:
                try:
                    _idp_entity_id = dict(parse_qs(query))[self.idp_query_param][0]
                    if _idp_entity_id in idps:
                        idp_entity_id = _idp_entity_id
                    break
                except KeyError:
                    logger.debug("No IdP entity ID in query: %s", query)
                    pass

        if idp_entity_id is None:
            if len(idps) == 1:
                # idps is a dictionary
                idp_entity_id = idps.keys()[0]
            elif not len(idps):
                return -1, HTTPInternalServerError(detail="Misconfiguration")
            else:
                idp_entity_id = ""
                logger.info("ENVIRON: %s", environ)

                if self.wayf:
                    if query:
                        try:
                            wayf_selected = dict(parse_qs(query))["wayf_selected"][0]
                        except KeyError:
                            return self._wayf_redirect(came_from)
                        idp_entity_id = wayf_selected
                    else:
                        return self._wayf_redirect(came_from)
                elif self.discosrv:
                    if query:
                        idp_entity_id = _cli.parse_discovery_service_response(
                            query=environ.get("QUERY_STRING")
                        )
                    else:
                        sid_ = sid()
                        self.outstanding_queries[sid_] = came_from
                        logger.debug("Redirect to Discovery Service function")
                        eid = _cli.config.entityid
                        ret = _cli.config.getattr("endpoints", "sp")[
                            "discovery_response"
                        ][0][0]
                        ret += "?sid=%s" % sid_
                        loc = _cli.create_discovery_service_request(
                            self.discosrv, eid, **{"return": ret}
                        )
                        return -1, SeeOther(loc)

                else:
                    return -1, HTTPNotImplemented(detail="No WAYF or DJ present!")

        logger.info("Chosen IdP: '%s'", idp_entity_id)
        return 0, idp_entity_id
Exemple #10
0
 def _wayf_redirect(self, came_from):
     sid_ = sid()
     self.outstanding_queries[sid_] = came_from
     logger.info("Redirect to WAYF function: %s", self.wayf)
     return -1, HTTPSeeOther(headers=[("Location", "%s?%s" % (self.wayf, sid_))])
Exemple #11
0
    def _pick_idp(self, came_from):
        """
        If more than one idp and if none is selected, I have to do wayf or
        disco
        """

        _cli = self.sp

        logger.debug("[_pick_idp] %s", self.environ)
        if "HTTP_PAOS" in self.environ:
            if self.environ["HTTP_PAOS"] == PAOS_HEADER_INFO:
                if MIME_PAOS in self.environ["HTTP_ACCEPT"]:
                    # Where should I redirect the user to
                    # entityid -> the IdP to use
                    # relay_state -> when back from authentication

                    logger.debug("- ECP client detected -")

                    _rstate = rndstr()
                    self.cache.relay_state[_rstate] = geturl(self.environ)
                    _entityid = _cli.config.ecp_endpoint(self.environ["REMOTE_ADDR"])

                    if not _entityid:
                        return -1, ServiceError("No IdP to talk to")
                    logger.debug("IdP to talk to: %s", _entityid)
                    return ecp.ecp_auth_request(_cli, _entityid, _rstate)
                else:
                    return -1, ServiceError("Faulty Accept header")
            else:
                return -1, ServiceError("unknown ECP version")

        # Find all IdPs
        idps = self.sp.metadata.with_descriptor("idpsso")

        idp_entity_id = None

        kaka = self.environ.get("HTTP_COOKIE", "")
        if kaka:
            try:
                (idp_entity_id, _) = parse_cookie("ve_disco", "SEED_SAW", kaka)
            except ValueError:
                pass
            except TypeError:
                pass

        # Any specific IdP specified in a query part
        query = self.environ.get("QUERY_STRING")
        if not idp_entity_id and query:
            try:
                _idp_entity_id = dict(parse_qs(query))[self.idp_query_param][0]
                if _idp_entity_id in idps:
                    idp_entity_id = _idp_entity_id
            except KeyError:
                logger.debug("No IdP entity ID in query: %s", query)
                pass

        if not idp_entity_id:

            if self.wayf:
                if query:
                    try:
                        wayf_selected = dict(parse_qs(query))["wayf_selected"][0]
                    except KeyError:
                        return self._wayf_redirect(came_from)
                    idp_entity_id = wayf_selected
                else:
                    return self._wayf_redirect(came_from)
            elif self.discosrv:
                if query:
                    idp_entity_id = _cli.parse_discovery_service_response(
                        query=self.environ.get("QUERY_STRING")
                    )
                if not idp_entity_id:
                    sid_ = sid()
                    self.cache.outstanding_queries[sid_] = came_from
                    logger.debug("Redirect to Discovery Service function")
                    eid = _cli.config.entityid
                    ret = _cli.config.getattr("endpoints", "sp")["discovery_response"][
                        0
                    ][0]
                    ret += "?sid=%s" % sid_
                    loc = _cli.create_discovery_service_request(
                        self.discosrv, eid, **{"return": ret}
                    )
                    return -1, SeeOther(loc)
            elif len(idps) == 1:
                # idps is a dictionary
                idp_entity_id = list(idps.keys())[0]
            elif not len(idps):
                return -1, ServiceError("Misconfiguration")
            else:
                return -1, NotImplemented("No WAYF or DS present!")

        logger.info("Chosen IdP: '%s'", idp_entity_id)
        return 0, idp_entity_id
Exemple #12
0
    def do_attribute_query(self, entityid, subject_id,
                           attribute=None, sp_name_qualifier=None,
                           name_qualifier=None, nameid_format=None,
                           real_id=None, consent=None, extensions=None,
                           sign=False, binding=BINDING_SOAP, nsprefix=None):
        """ Does a attribute request to an attribute authority, this is
        by default done over SOAP.

        :param entityid: To whom the query should be sent
        :param subject_id: The identifier of the subject
        :param attribute: A dictionary of attributes and values that is
            asked for
        :param sp_name_qualifier: The unique identifier of the
            service provider or affiliation of providers for whom the
            identifier was generated.
        :param name_qualifier: The unique identifier of the identity
            provider that generated the identifier.
        :param nameid_format: The format of the name ID
        :param real_id: The identifier which is the key to this entity in the
            identity database
        :param binding: Which binding to use
        :param nsprefix: Namespace prefixes preferred before those automatically
            produced.
        :return: The attributes returned if BINDING_SOAP was used.
            HTTP args if BINDING_HTT_POST was used.
        """

        if real_id:
            response_args = {"real_id": real_id}
        else:
            response_args = {}

        if not binding:
            binding, destination = self.pick_binding("attribute_service",
                                                     None,
                                                     "attribute_authority",
                                                     entity_id=entityid)
        else:
            srvs = self.metadata.attribute_service(entityid, binding)
            if srvs is []:
                raise SAMLError("No attribute service support at entity")

            destination = destinations(srvs)[0]

        if binding == BINDING_SOAP:
            return self._use_soap(destination, "attribute_query",
                                  consent=consent, extensions=extensions,
                                  sign=sign, subject_id=subject_id,
                                  attribute=attribute,
                                  sp_name_qualifier=sp_name_qualifier,
                                  name_qualifier=name_qualifier,
                                  format=nameid_format,
                                  response_args=response_args)
        elif binding == BINDING_HTTP_POST:
            mid = sid()
            query = self.create_attribute_query(destination, subject_id,
                                                attribute, mid, consent,
                                                extensions, sign, nsprefix)
            self.state[query.id] = {"entity_id": entityid,
                                    "operation": "AttributeQuery",
                                    "subject_id": subject_id,
                                    "sign": sign}
            relay_state = self._relay_state(query.id)
            return self.apply_binding(binding, "%s" % query, destination,
                                      relay_state, sign=sign)
        else:
            raise SAMLError("Unsupported binding")
def test_artifact_flow():
    #SP = 'urn:mace:example.com:saml:roland:sp'
    sp = Saml2Client(config_file="servera_conf")

    with closing(Server(config_file="idp_all_conf")) as idp:
        # original request

        binding, destination = sp.pick_binding("single_sign_on_service",
                                               entity_id=idp.config.entityid)
        relay_state = "RS0"
        req_id, req = sp.create_authn_request(destination, id="id1")

        artifact = sp.use_artifact(req, 1)

        binding, destination = sp.pick_binding("single_sign_on_service",
                                               [BINDING_HTTP_ARTIFACT],
                                               entity_id=idp.config.entityid)

        hinfo = sp.apply_binding(binding, artifact, destination, relay_state)

        # ========== @IDP ============

        artifact2 = get_msg(hinfo, binding)

        assert artifact == artifact2

        # The IDP now wants to replace the artifact with the real request

        destination = idp.artifact2destination(artifact2, "spsso")

        msg_id, msg = idp.create_artifact_resolve(artifact2, destination, sid())

        hinfo = idp.use_soap(msg, destination, None, False)

        # ======== @SP ==========

        msg = get_msg(hinfo, BINDING_SOAP)

        ar = sp.parse_artifact_resolve(msg)

        assert ar.artifact.text == artifact

        # The SP picks the request out of the repository with the artifact as the key
        oreq = sp.artifact[ar.artifact.text]
        # Should be the same as req above

        # Returns the information over the existing SOAP connection so
        # no transport information needed

        msg = sp.create_artifact_response(ar, ar.artifact.text)
        hinfo = sp.use_soap(msg, destination)

        # ========== @IDP ============

        msg = get_msg(hinfo, BINDING_SOAP)

        # The IDP untangles the request from the artifact resolve response
        spreq = idp.parse_artifact_resolve_response(msg)

        # should be the same as req above

        assert spreq.id == req.id

        # That was one way, the Request from the SP
        # ---------------------------------------------#
        # Now for the other, the response from the IDP

        name_id = idp.ident.transient_nameid(sp.config.entityid, "derek")

        resp_args = idp.response_args(spreq, [BINDING_HTTP_POST])

        response = idp.create_authn_response({"eduPersonEntitlement": "Short stop",
                                              "surName": "Jeter", "givenName": "Derek",
                                              "mail": "*****@*****.**",
                                              "title": "The man"},
                                             name_id=name_id,
                                             authn=AUTHN,
                                             **resp_args)

        print(response)

        # with the response in hand create an artifact

        artifact = idp.use_artifact(response, 1)

        binding, destination = sp.pick_binding("single_sign_on_service",
                                               [BINDING_HTTP_ARTIFACT],
                                               entity_id=idp.config.entityid)

        hinfo = sp.apply_binding(binding, "%s" % artifact, destination, relay_state,
                                 response=True)

        # ========== SP =========

        artifact3 = get_msg(hinfo, binding)

        assert artifact == artifact3

        destination = sp.artifact2destination(artifact3, "idpsso")

        # Got an artifact want to replace it with the real message
        msg_id, msg = sp.create_artifact_resolve(artifact3, destination, sid())

        print(msg)

        hinfo = sp.use_soap(msg, destination, None, False)

        # ======== IDP ==========

        msg = get_msg(hinfo, BINDING_SOAP)

        ar = idp.parse_artifact_resolve(msg)

        print(ar)

        assert ar.artifact.text == artifact3

        # The IDP retrieves the response from the database using the artifact as the key
        #oreq = idp.artifact[ar.artifact.text]

        binding, destination = idp.pick_binding("artifact_resolution_service",
                                                entity_id=sp.config.entityid)

        resp = idp.create_artifact_response(ar, ar.artifact.text)
        hinfo = idp.use_soap(resp, destination)

        # ========== SP ============

        msg = get_msg(hinfo, BINDING_SOAP)
        sp_resp = sp.parse_artifact_resolve_response(msg)

        assert sp_resp.id == response.id
Exemple #14
0
    def _pick_idp(self, environ, came_from):
        """
        If more than one idp and if none is selected, I have to do wayf or
        disco
        """

        # check headers to see if it's an ECP request
        #        headers = {
        #                    'Accept' : 'text/html; application/vnd.paos+xml',
        #                    'PAOS'   : 'ver="%s";"%s"' % (paos.NAMESPACE,
        # SERVICE)
        #                    }

        _cli = self.saml_client

        logger.info("[_pick_idp] %s", environ)
        if "HTTP_PAOS" in environ:
            if environ["HTTP_PAOS"] == PAOS_HEADER_INFO:
                if MIME_PAOS in environ["HTTP_ACCEPT"]:
                    # Where should I redirect the user to
                    # entityid -> the IdP to use
                    # relay_state -> when back from authentication

                    logger.info("- ECP client detected -")

                    _relay_state = construct_came_from(environ)
                    _entityid = _cli.config.ecp_endpoint(
                        environ["REMOTE_ADDR"])

                    if not _entityid:
                        return -1, HTTPInternalServerError(
                            detail="No IdP to talk to")
                    logger.info("IdP to talk to: %s", _entityid)
                    return ecp.ecp_auth_request(_cli, _entityid, _relay_state)
                else:
                    return -1, HTTPInternalServerError(
                        detail="Faulty Accept header")
            else:
                return -1, HTTPInternalServerError(
                    detail="unknown ECP version")

        idps = self.metadata.with_descriptor("idpsso")

        logger.info("IdP URL: %s", idps)

        idp_entity_id = query = None

        for key in ["s2repoze.body", "QUERY_STRING"]:
            query = environ.get(key)
            if query:
                try:
                    _idp_entity_id = dict(
                        parse_qs(query))[self.idp_query_param][0]
                    if _idp_entity_id in idps:
                        idp_entity_id = _idp_entity_id
                    break
                except KeyError:
                    logger.debug("No IdP entity ID in query: %s", query)
                    pass

        if idp_entity_id is None:
            if len(idps) == 1:
                # idps is a dictionary
                idp_entity_id = idps.keys()[0]
            elif not len(idps):
                return -1, HTTPInternalServerError(detail="Misconfiguration")
            else:
                idp_entity_id = ""
                logger.info("ENVIRON: %s", environ)

                if self.wayf:
                    if query:
                        try:
                            wayf_selected = dict(
                                parse_qs(query))["wayf_selected"][0]
                        except KeyError:
                            return self._wayf_redirect(came_from)
                        idp_entity_id = wayf_selected
                    else:
                        return self._wayf_redirect(came_from)
                elif self.discosrv:
                    if query:
                        idp_entity_id = _cli.parse_discovery_service_response(
                            query=environ.get("QUERY_STRING"))
                    else:
                        sid_ = sid()
                        self.outstanding_queries[sid_] = came_from
                        logger.debug("Redirect to Discovery Service function")
                        eid = _cli.config.entityid
                        ret = _cli.config.getattr(
                            "endpoints", "sp")["discovery_response"][0][0]
                        ret += "?sid=%s" % sid_
                        loc = _cli.create_discovery_service_request(
                            self.discosrv, eid, **{"return": ret})
                        return -1, SeeOther(loc)

                else:
                    return -1, HTTPNotImplemented(
                        detail="No WAYF or DJ present!")

        logger.info("Chosen IdP: '%s'", idp_entity_id)
        return 0, idp_entity_id
Exemple #15
0
    def do_attribute_query(self,
                           entityid,
                           subject_id,
                           attribute=None,
                           sp_name_qualifier=None,
                           name_qualifier=None,
                           nameid_format=None,
                           real_id=None,
                           consent=None,
                           extensions=None,
                           sign=False,
                           binding=BINDING_SOAP,
                           nsprefix=None):
        """ Does a attribute request to an attribute authority, this is
        by default done over SOAP.

        :param entityid: To whom the query should be sent
        :param subject_id: The identifier of the subject
        :param attribute: A dictionary of attributes and values that is
            asked for
        :param sp_name_qualifier: The unique identifier of the
            service provider or affiliation of providers for whom the
            identifier was generated.
        :param name_qualifier: The unique identifier of the identity
            provider that generated the identifier.
        :param nameid_format: The format of the name ID
        :param real_id: The identifier which is the key to this entity in the
            identity database
        :param binding: Which binding to use
        :param nsprefix: Namespace prefixes preferred before those automatically
            produced.
        :return: The attributes returned if BINDING_SOAP was used.
            HTTP args if BINDING_HTT_POST was used.
        """

        if real_id:
            response_args = {"real_id": real_id}
        else:
            response_args = {}

        if not binding:
            binding, destination = self.pick_binding("attribute_service",
                                                     None,
                                                     "attribute_authority",
                                                     entity_id=entityid)
        else:
            srvs = self.metadata.attribute_service(entityid, binding)
            if srvs is []:
                raise SAMLError("No attribute service support at entity")

            destination = destinations(srvs)[0]

        if binding == BINDING_SOAP:
            return self._use_soap(destination,
                                  "attribute_query",
                                  consent=consent,
                                  extensions=extensions,
                                  sign=sign,
                                  subject_id=subject_id,
                                  attribute=attribute,
                                  sp_name_qualifier=sp_name_qualifier,
                                  name_qualifier=name_qualifier,
                                  format=nameid_format,
                                  response_args=response_args)
        elif binding == BINDING_HTTP_POST:
            mid = sid()
            query = self.create_attribute_query(destination, subject_id,
                                                attribute, mid, consent,
                                                extensions, sign, nsprefix)
            self.state[query.id] = {
                "entity_id": entityid,
                "operation": "AttributeQuery",
                "subject_id": subject_id,
                "sign": sign
            }
            relay_state = self._relay_state(query.id)
            return self.apply_binding(binding,
                                      "%s" % query,
                                      destination,
                                      relay_state,
                                      sign=sign)
        else:
            raise SAMLError("Unsupported binding")
Exemple #16
0
 def _wayf_redirect(self, came_from):
     sid_ = sid()
     self.cache.outstanding_queries[sid_] = came_from
     logger.debug("Redirect to WAYF function: %s", self.wayf)
     return -1, SeeOther(headers=[("Location",
                                   "%s?%s" % (self.wayf, sid_))])
Exemple #17
0
    def _pick_idp(self, came_from):
        """
        If more than one idp and if none is selected, I have to do wayf or
        disco
        """

        _cli = self.sp

        logger.debug("[_pick_idp] %s", self.environ)
        if "HTTP_PAOS" in self.environ:
            if self.environ["HTTP_PAOS"] == PAOS_HEADER_INFO:
                if MIME_PAOS in self.environ["HTTP_ACCEPT"]:
                    # Where should I redirect the user to
                    # entityid -> the IdP to use
                    # relay_state -> when back from authentication

                    logger.debug("- ECP client detected -")

                    _rstate = rndstr()
                    self.cache.relay_state[_rstate] = geturl(self.environ)
                    _entityid = _cli.config.ecp_endpoint(
                        self.environ["REMOTE_ADDR"])

                    if not _entityid:
                        return -1, ServiceError("No IdP to talk to")
                    logger.debug("IdP to talk to: %s", _entityid)
                    return ecp.ecp_auth_request(_cli, _entityid, _rstate)
                else:
                    return -1, ServiceError("Faulty Accept header")
            else:
                return -1, ServiceError("unknown ECP version")

        # Find all IdPs
        idps = self.sp.metadata.with_descriptor("idpsso")

        idp_entity_id = None

        kaka = self.environ.get("HTTP_COOKIE", "")
        if kaka:
            try:
                (idp_entity_id, _) = parse_cookie("ve_disco", "SEED_SAW", kaka)
            except ValueError:
                pass
            except TypeError:
                pass

        # Any specific IdP specified in a query part
        query = self.environ.get("QUERY_STRING")
        if not idp_entity_id and query:
            try:
                _idp_entity_id = dict(parse_qs(query))[self.idp_query_param][0]
                if _idp_entity_id in idps:
                    idp_entity_id = _idp_entity_id
            except KeyError:
                logger.debug("No IdP entity ID in query: %s", query)
                pass

        if not idp_entity_id:

            if self.wayf:
                if query:
                    try:
                        wayf_selected = dict(
                            parse_qs(query))["wayf_selected"][0]
                    except KeyError:
                        return self._wayf_redirect(came_from)
                    idp_entity_id = wayf_selected
                else:
                    return self._wayf_redirect(came_from)
            elif self.discosrv:
                if query:
                    idp_entity_id = _cli.parse_discovery_service_response(
                        query=self.environ.get("QUERY_STRING"))
                if not idp_entity_id:
                    sid_ = sid()
                    self.cache.outstanding_queries[sid_] = came_from
                    logger.debug("Redirect to Discovery Service function")
                    eid = _cli.config.entityid
                    ret = _cli.config.getattr("endpoints",
                                              "sp")["discovery_response"][0][0]
                    ret += "?sid=%s" % sid_
                    loc = _cli.create_discovery_service_request(
                        self.discosrv, eid, **{"return": ret})
                    return -1, SeeOther(loc)
            elif len(idps) == 1:
                # idps is a dictionary
                idp_entity_id = list(idps.keys())[0]
            elif not len(idps):
                return -1, ServiceError("Misconfiguration")
            else:
                return -1, NotImplemented("No WAYF or DS present!")

        logger.info("Chosen IdP: '%s'", idp_entity_id)
        return 0, idp_entity_id
def test_artifact_flow():
    #SP = 'urn:mace:example.com:saml:roland:sp'
    sp = Saml2Client(config_file="servera_conf")

    with closing(Server(config_file="idp_all_conf")) as idp:
        # original request

        binding, destination = sp.pick_binding("single_sign_on_service",
                                               entity_id=idp.config.entityid)
        relay_state = "RS0"
        req_id, req = sp.create_authn_request(destination, id="id1")

        artifact = sp.use_artifact(req, 1)

        binding, destination = sp.pick_binding("single_sign_on_service",
                                               [BINDING_HTTP_ARTIFACT],
                                               entity_id=idp.config.entityid)

        hinfo = sp.apply_binding(binding, artifact, destination, relay_state)

        # ========== @IDP ============

        artifact2 = get_msg(hinfo, binding)

        assert artifact == artifact2

        # The IDP now wants to replace the artifact with the real request

        destination = idp.artifact2destination(artifact2, "spsso")

        msg_id, msg = idp.create_artifact_resolve(artifact2, destination,
                                                  sid())

        hinfo = idp.use_soap(msg, destination, None, False)

        # ======== @SP ==========

        msg = get_msg(hinfo, BINDING_SOAP)

        ar = sp.parse_artifact_resolve(msg)

        assert ar.artifact.text == artifact

        # The SP picks the request out of the repository with the artifact as the key
        oreq = sp.artifact[ar.artifact.text]
        # Should be the same as req above

        # Returns the information over the existing SOAP connection so
        # no transport information needed

        msg = sp.create_artifact_response(ar, ar.artifact.text)
        hinfo = sp.use_soap(msg, destination)

        # ========== @IDP ============

        msg = get_msg(hinfo, BINDING_SOAP)

        # The IDP untangles the request from the artifact resolve response
        spreq = idp.parse_artifact_resolve_response(msg)

        # should be the same as req above

        assert spreq.id == req.id

        # That was one way, the Request from the SP
        # ---------------------------------------------#
        # Now for the other, the response from the IDP

        name_id = idp.ident.transient_nameid(sp.config.entityid, "derek")

        resp_args = idp.response_args(spreq, [BINDING_HTTP_POST])

        response = idp.create_authn_response(
            {
                "eduPersonEntitlement": "Short stop",
                "surName": "Jeter",
                "givenName": "Derek",
                "mail": "*****@*****.**",
                "title": "The man"
            },
            name_id=name_id,
            authn=AUTHN,
            **resp_args)

        print(response)

        # with the response in hand create an artifact

        artifact = idp.use_artifact(response, 1)

        binding, destination = sp.pick_binding("single_sign_on_service",
                                               [BINDING_HTTP_ARTIFACT],
                                               entity_id=idp.config.entityid)

        hinfo = sp.apply_binding(binding,
                                 "%s" % artifact,
                                 destination,
                                 relay_state,
                                 response=True)

        # ========== SP =========

        artifact3 = get_msg(hinfo, binding)

        assert artifact == artifact3

        destination = sp.artifact2destination(artifact3, "idpsso")

        # Got an artifact want to replace it with the real message
        msg_id, msg = sp.create_artifact_resolve(artifact3, destination, sid())

        print(msg)

        hinfo = sp.use_soap(msg, destination, None, False)

        # ======== IDP ==========

        msg = get_msg(hinfo, BINDING_SOAP)

        ar = idp.parse_artifact_resolve(msg)

        print(ar)

        assert ar.artifact.text == artifact3

        # The IDP retrieves the response from the database using the artifact as the key
        #oreq = idp.artifact[ar.artifact.text]

        binding, destination = idp.pick_binding("artifact_resolution_service",
                                                entity_id=sp.config.entityid)

        resp = idp.create_artifact_response(ar, ar.artifact.text)
        hinfo = idp.use_soap(resp, destination)

        # ========== SP ============

        msg = get_msg(hinfo, BINDING_SOAP)
        sp_resp = sp.parse_artifact_resolve_response(msg)

        assert sp_resp.id == response.id