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 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 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
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
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
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_))])
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 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
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
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 _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_))])
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