Fixed manage_name_id request-response
Added assert_id request-response
This commit is contained in:
parent
05d514f10e
commit
85b3fc307c
@ -24,7 +24,6 @@ from saml2.mdstore import destinations
|
||||
from saml2.saml import AssertionIDRef
|
||||
from saml2.saml import NAMEID_FORMAT_TRANSIENT
|
||||
from saml2.samlp import AuthnQuery
|
||||
from saml2.samlp import Response
|
||||
from saml2.samlp import AssertionIDRequest
|
||||
from saml2.samlp import NameIDMappingRequest
|
||||
from saml2.samlp import AttributeQuery
|
||||
@ -48,6 +47,9 @@ from saml2 import saml
|
||||
from saml2.population import Population
|
||||
|
||||
from saml2.response import AttributeResponse
|
||||
from saml2.response import AuthzResponse
|
||||
from saml2.response import AssertionIDResponse
|
||||
from saml2.response import AuthnQueryResponse
|
||||
from saml2.response import NameIDMappingResponse
|
||||
from saml2.response import AuthnResponse
|
||||
|
||||
@ -491,17 +493,21 @@ class Base(Entity):
|
||||
""" Verify that the response is OK
|
||||
"""
|
||||
|
||||
return self._parse_response(response, Response, "", binding)
|
||||
return self._parse_response(response, AuthzResponse, "", binding)
|
||||
|
||||
def parse_authn_query_response(self, response, binding=BINDING_SOAP):
|
||||
""" Verify that the response is OK
|
||||
"""
|
||||
return self._parse_response(response, Response, "", binding)
|
||||
return self._parse_response(response, AuthnQueryResponse, "", binding)
|
||||
|
||||
def parse_assertion_id_request_response(self, response, binding):
|
||||
""" Verify that the response is OK
|
||||
"""
|
||||
return self._parse_response(response, Response, "", binding)
|
||||
kwargs = {"entity_id": self.config.entityid,
|
||||
"attribute_converters": self.config.attribute_converters}
|
||||
|
||||
return self._parse_response(response, AssertionIDResponse, "", binding,
|
||||
**kwargs)
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
|
||||
|
@ -146,6 +146,7 @@ class Entity(HTTPBase):
|
||||
entity_id = request.issuer.text.strip()
|
||||
|
||||
sfunc = getattr(self.metadata, service)
|
||||
|
||||
if bindings is None:
|
||||
bindings = self.config.preferred_binding[service]
|
||||
|
||||
@ -175,8 +176,9 @@ class Entity(HTTPBase):
|
||||
return {"id":id, "version":VERSION,
|
||||
"issue_instant":instant(), "issuer":self._issuer()}
|
||||
|
||||
def response_args(self, message, bindings, descr_type=""):
|
||||
def response_args(self, message, bindings=None, descr_type=""):
|
||||
info = {"in_response_to": message.id}
|
||||
|
||||
if isinstance(message, AuthnRequest):
|
||||
rsrv = "assertion_consumer_service"
|
||||
descr_type = "sp_sso"
|
||||
@ -189,7 +191,9 @@ class Entity(HTTPBase):
|
||||
descr_type = "sp_sso"
|
||||
elif isinstance(message, ManageNameIDRequest):
|
||||
rsrv = "manage_name_id_service"
|
||||
# The once below are solely SOAP
|
||||
# The once below are solely SOAP so no return destination needed
|
||||
elif isinstance(message, AssertionIDRequest):
|
||||
rsrv = ""
|
||||
elif isinstance(message, ArtifactResolve):
|
||||
rsrv = ""
|
||||
elif isinstance(message, AssertionIDRequest):
|
||||
@ -209,6 +213,7 @@ class Entity(HTTPBase):
|
||||
binding, destination = self.pick_binding(rsrv, bindings,
|
||||
descr_type=descr_type,
|
||||
request=message)
|
||||
#info["binding"] = binding
|
||||
info["destination"] = destination
|
||||
|
||||
return info
|
||||
@ -488,7 +493,8 @@ class Entity(HTTPBase):
|
||||
def create_manage_name_id_request(self, destination, id=0, consent=None,
|
||||
extensions=None, sign=False,
|
||||
name_id=None, new_id=None,
|
||||
encrypted_id=None, new_encrypted_id=None):
|
||||
encrypted_id=None, new_encrypted_id=None,
|
||||
terminate=None):
|
||||
"""
|
||||
|
||||
:param destination:
|
||||
@ -500,6 +506,7 @@ class Entity(HTTPBase):
|
||||
:param new_id:
|
||||
:param encrypted_id:
|
||||
:param new_encrypted_id:
|
||||
:param terminate:
|
||||
:return:
|
||||
"""
|
||||
kwargs = self.message_args(id)
|
||||
@ -515,8 +522,10 @@ class Entity(HTTPBase):
|
||||
kwargs["new_id"] = new_id
|
||||
elif new_encrypted_id:
|
||||
kwargs["new_encrypted_id"] = new_encrypted_id
|
||||
elif terminate:
|
||||
kwargs["terminate"] = terminate
|
||||
else:
|
||||
kwargs["terminate"] = ""
|
||||
raise AttributeError("One of NewID, NewEncryptedNameID or Terminate has to be provided")
|
||||
|
||||
return self._message(ManageNameIDRequest, destination, consent=consent,
|
||||
extensions=extensions, sign=sign, **kwargs)
|
||||
@ -534,13 +543,13 @@ class Entity(HTTPBase):
|
||||
return self._parse_request(xmlstr, request.ManageNameIDRequest,
|
||||
"manage_name_id_service", binding)
|
||||
|
||||
def create_manage_name_id_response(self, request, bindings, status=None,
|
||||
sign=False, issuer=None):
|
||||
def create_manage_name_id_response(self, request, bindings=None,
|
||||
status=None, sign=False, issuer=None):
|
||||
|
||||
rinfo = self.response_args(request, bindings)
|
||||
|
||||
response = self._status_response(samlp.ManageNameIDResponse, issuer,
|
||||
status, sign=False, **rinfo)
|
||||
status, sign, **rinfo)
|
||||
|
||||
logger.info("Response: %s" % (response,))
|
||||
|
||||
@ -582,7 +591,7 @@ class Entity(HTTPBase):
|
||||
response = response_cls(self.sec, **kwargs)
|
||||
except Exception, exc:
|
||||
logger.info("%s" % exc)
|
||||
return None
|
||||
raise
|
||||
|
||||
xmlstr = self.unravel(xmlstr, binding, response_cls.msgtype)
|
||||
|
||||
@ -699,3 +708,7 @@ class Entity(HTTPBase):
|
||||
elems = extension_elements_to_elements(resp.response.extension_elements,
|
||||
[samlp, saml])
|
||||
return elems[0]
|
||||
|
||||
def parse_manage_name_id_response(self, xmlstr, binding=BINDING_SOAP):
|
||||
return self._parse_response(xmlstr, response.ManageNameIDResponse,
|
||||
"manage_name_id_service", binding)
|
||||
|
@ -157,6 +157,18 @@ class AuthnRequest(Request):
|
||||
return to_local(self.attribute_converters, self.message)
|
||||
|
||||
|
||||
class AssertionIDRequest(Request):
|
||||
msgtype = "assertion_id_request"
|
||||
def __init__(self, sec_context, receiver_addrs, attribute_converters,
|
||||
timeslack=0):
|
||||
Request.__init__(self, sec_context, receiver_addrs,
|
||||
attribute_converters, timeslack)
|
||||
self.signature_check = self.sec.correctly_signed_assertion_id_request
|
||||
|
||||
def attributes(self):
|
||||
return to_local(self.attribute_converters, self.message)
|
||||
|
||||
|
||||
class AuthzDecisionQuery(Request):
|
||||
msgtype = "authz_decision_query"
|
||||
def __init__(self, sec_context, receiver_addrs,
|
||||
|
@ -256,6 +256,16 @@ class NameIDMappingResponse(StatusResponse):
|
||||
request_id, asynchop)
|
||||
self.signature_check = self.sec.correctly_signed_name_id_mapping_response
|
||||
|
||||
class ManageNameIDResponse(StatusResponse):
|
||||
msgtype = "manage_name_id_response"
|
||||
|
||||
def __init__(self, sec_context, return_addr=None, timeslack=0,
|
||||
request_id=0, asynchop=True):
|
||||
StatusResponse.__init__(self, sec_context, return_addr, timeslack,
|
||||
request_id, asynchop)
|
||||
self.signature_check = self.sec.correctly_signed_manage_name_id_response
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
class AuthnResponse(StatusResponse):
|
||||
@ -632,6 +642,35 @@ class AuthnResponse(StatusResponse):
|
||||
def __str__(self):
|
||||
return "%s" % self.xmlstr
|
||||
|
||||
class AssertionIDResponse(AuthnResponse):
|
||||
msgtype = "assertion_id_response"
|
||||
|
||||
def __init__(self, sec_context, attribute_converters, entity_id,
|
||||
return_addr=None, timeslack=0, asynchop=False, test=False):
|
||||
|
||||
AuthnResponse.__init__(self, sec_context, attribute_converters,
|
||||
entity_id, return_addr, timeslack=timeslack,
|
||||
asynchop=asynchop, test=test)
|
||||
self.entity_id = entity_id
|
||||
self.attribute_converters = attribute_converters
|
||||
self.assertion = None
|
||||
self.context = "AssertionIdResponse"
|
||||
|
||||
class AuthnQueryResponse(AuthnResponse):
|
||||
msgtype = "authn_query_response"
|
||||
|
||||
def __init__(self, sec_context, attribute_converters, entity_id,
|
||||
return_addr=None, timeslack=0, asynchop=False, test=False):
|
||||
|
||||
AuthnResponse.__init__(self, sec_context, attribute_converters,
|
||||
entity_id, return_addr, timeslack=timeslack,
|
||||
asynchop=asynchop, test=test)
|
||||
self.entity_id = entity_id
|
||||
self.attribute_converters = attribute_converters
|
||||
self.assertion = None
|
||||
self.context = "AuthnQueryResponse"
|
||||
|
||||
|
||||
class AttributeResponse(AuthnResponse):
|
||||
msgtype = "attribute_response"
|
||||
|
||||
|
@ -23,9 +23,8 @@ import logging
|
||||
import shelve
|
||||
import sys
|
||||
import memcache
|
||||
from saml2.soap import parse_soap_enveloped_saml_name_id_mapping_request
|
||||
from saml2.samlp import AuthzDecisionQuery, NameIDMappingResponse
|
||||
from saml2.samlp import AssertionIDRequest
|
||||
from saml2.samlp import AuthzDecisionQuery
|
||||
from saml2.samlp import NameIDMappingResponse
|
||||
from saml2.samlp import AuthnQuery
|
||||
from saml2.entity import Entity
|
||||
|
||||
@ -34,6 +33,7 @@ from saml2 import class_name
|
||||
from saml2 import BINDING_HTTP_REDIRECT
|
||||
|
||||
from saml2.request import AuthnRequest
|
||||
from saml2.request import AssertionIDRequest
|
||||
from saml2.request import AttributeQuery
|
||||
from saml2.request import NameIDMappingRequest
|
||||
|
||||
@ -619,3 +619,4 @@ class Server(Entity):
|
||||
else:
|
||||
logger.info("Message: %s" % _resp)
|
||||
return _resp
|
||||
|
||||
|
@ -822,6 +822,18 @@ class SecurityContext(object):
|
||||
"manage_name_id_request",
|
||||
must, origdoc)
|
||||
|
||||
def correctly_signed_manage_name_id_response(self, decoded_xml, must=False,
|
||||
origdoc=None):
|
||||
return self.correctly_signed_message(decoded_xml,
|
||||
"manage_name_id_response", must,
|
||||
origdoc)
|
||||
|
||||
def correctly_signed_assertion_id_request(self, decoded_xml, must=False,
|
||||
origdoc=None):
|
||||
return self.correctly_signed_message(decoded_xml,
|
||||
"assertion_id_request", must,
|
||||
origdoc)
|
||||
|
||||
def correctly_signed_response(self, decoded_xml, must=False, origdoc=None):
|
||||
""" Check if a instance is correctly signed, if we have metadata for
|
||||
the IdP that sent the info use that, if not use the key that are in
|
||||
|
@ -79,6 +79,19 @@ def parse_soap_enveloped_saml_manage_name_id_request(text):
|
||||
expected_tag = '{%s}ManageNameIDRequest' % SAMLP_NAMESPACE
|
||||
return parse_soap_enveloped_saml_thingy(text, [expected_tag])
|
||||
|
||||
def parse_soap_enveloped_saml_manage_name_id_response(text):
|
||||
expected_tag = '{%s}ManageNameIDResponse' % SAMLP_NAMESPACE
|
||||
return parse_soap_enveloped_saml_thingy(text, [expected_tag])
|
||||
|
||||
def parse_soap_enveloped_saml_assertion_id_request(text):
|
||||
expected_tag = '{%s}AssertionIDRequest' % SAMLP_NAMESPACE
|
||||
return parse_soap_enveloped_saml_thingy(text, [expected_tag])
|
||||
|
||||
def parse_soap_enveloped_saml_assertion_id_response(text):
|
||||
tags = ['{%s}Response' % SAMLP_NAMESPACE,
|
||||
'{%s}AssertionIDResponse' % SAMLP_NAMESPACE]
|
||||
return parse_soap_enveloped_saml_thingy(text, tags)
|
||||
|
||||
#def parse_soap_enveloped_saml_logout_response(text):
|
||||
# expected_tag = '{%s}LogoutResponse' % SAMLP_NAMESPACE
|
||||
# return parse_soap_enveloped_saml_thingy(text, [expected_tag])
|
||||
@ -110,7 +123,7 @@ def parse_soap_enveloped_saml_thingy(text, expected_tags):
|
||||
if saml_part.tag in expected_tags:
|
||||
return ElementTree.tostring(saml_part, encoding="UTF-8")
|
||||
else:
|
||||
raise WrongMessageType("Was '%s' expected '%s'" % (saml_part.tag,
|
||||
raise WrongMessageType("Was '%s' expected one of %s" % (saml_part.tag,
|
||||
expected_tags))
|
||||
|
||||
import re
|
||||
|
@ -1,3 +1,4 @@
|
||||
from saml2 import BINDING_SOAP
|
||||
from saml2.samlp import NewID
|
||||
from saml2.saml import NameID, NAMEID_FORMAT_TRANSIENT
|
||||
from saml2.client import Saml2Client
|
||||
@ -29,3 +30,45 @@ def test_basic():
|
||||
print _req.message
|
||||
|
||||
assert mid.id == _req.message.id
|
||||
|
||||
def test_flow():
|
||||
sp = Saml2Client(config_file="servera_conf")
|
||||
idp = Server(config_file="idp_all_conf")
|
||||
|
||||
binding, destination = sp.pick_binding("manage_name_id_service",
|
||||
entity_id=idp.config.entityid)
|
||||
|
||||
nameid = NameID(format=NAMEID_FORMAT_TRANSIENT, text="foobar")
|
||||
newid = NewID(text="Barfoo")
|
||||
|
||||
mid = sp.create_manage_name_id_request(destination, name_id=nameid,
|
||||
new_id=newid)
|
||||
|
||||
print mid
|
||||
rargs = sp.apply_binding(binding, "%s" % mid, destination, "")
|
||||
|
||||
# --------- @IDP --------------
|
||||
|
||||
_req = idp.parse_manage_name_id_request(rargs["data"], binding)
|
||||
|
||||
print _req.message
|
||||
|
||||
mnir = idp.create_manage_name_id_response(_req.message, None)
|
||||
|
||||
if binding != BINDING_SOAP:
|
||||
binding, destination = idp.pick_binding("manage_name_id_service",
|
||||
entity_id=sp.config.entityid)
|
||||
else:
|
||||
destination = ""
|
||||
|
||||
respargs = idp.apply_binding(binding, "%s" % mnir, destination, "")
|
||||
|
||||
print respargs
|
||||
|
||||
# ---------- @SP ---------------
|
||||
|
||||
_response = sp.parse_manage_name_id_response(respargs["data"], binding)
|
||||
|
||||
print _response.response
|
||||
|
||||
assert _response.response.id == mnir.id
|
104
tests/test_68_assertion_id.py
Normal file
104
tests/test_68_assertion_id.py
Normal file
@ -0,0 +1,104 @@
|
||||
from urlparse import parse_qs
|
||||
from urlparse import urlparse
|
||||
from saml2.samlp import AuthnRequest
|
||||
from saml2.samlp import NameIDPolicy
|
||||
from saml2.saml import AUTHN_PASSWORD
|
||||
from saml2.saml import NAMEID_FORMAT_TRANSIENT
|
||||
from saml2 import BINDING_HTTP_POST
|
||||
from saml2 import BINDING_SOAP
|
||||
from saml2.client import Saml2Client
|
||||
from saml2.server import Server
|
||||
|
||||
__author__ = 'rolandh'
|
||||
|
||||
TAG1 = "name=\"SAMLRequest\" value="
|
||||
|
||||
def get_msg(hinfo, binding):
|
||||
if binding == BINDING_SOAP:
|
||||
xmlstr = hinfo["data"]
|
||||
elif binding == BINDING_HTTP_POST:
|
||||
_inp = hinfo["data"][3]
|
||||
i = _inp.find(TAG1)
|
||||
i += len(TAG1) + 1
|
||||
j = _inp.find('"', i)
|
||||
xmlstr = _inp[i:j]
|
||||
else: # BINDING_HTTP_REDIRECT
|
||||
parts = urlparse(hinfo["headers"][0][1])
|
||||
xmlstr = parse_qs(parts.query)["SAMLRequest"][0]
|
||||
|
||||
return xmlstr
|
||||
|
||||
def test_basic_flow():
|
||||
sp = Saml2Client(config_file="servera_conf")
|
||||
idp = Server(config_file="idp_all_conf")
|
||||
|
||||
# -------- @IDP -------------
|
||||
|
||||
relay_state = "FOO"
|
||||
# -- dummy request ---
|
||||
orig_req = AuthnRequest(issuer=sp._issuer(),
|
||||
name_id_policy=NameIDPolicy(allow_create="true",
|
||||
format=NAMEID_FORMAT_TRANSIENT))
|
||||
|
||||
# == Create an AuthnRequest response
|
||||
|
||||
name_id = idp.ident.transient_nameid(sp.config.entityid, "id12")
|
||||
binding, destination = idp.pick_binding("assertion_consumer_service",
|
||||
entity_id=sp.config.entityid)
|
||||
resp = idp.create_authn_response({"eduPersonEntitlement": "Short stop",
|
||||
"surName": "Jeter",
|
||||
"givenName": "Derek",
|
||||
"mail": "derek.jeter@nyy.mlb.com",
|
||||
"title": "The man"},
|
||||
"id-123456789",
|
||||
destination,
|
||||
sp.config.entityid,
|
||||
name_id=name_id,
|
||||
authn=(AUTHN_PASSWORD,
|
||||
"http://www.example.com/login"))
|
||||
|
||||
hinfo = idp.apply_binding(binding, "%s" % resp, destination, relay_state)
|
||||
|
||||
# --------- @SP -------------
|
||||
|
||||
xmlstr = get_msg(hinfo, binding)
|
||||
|
||||
aresp = sp.parse_authn_request_response(xmlstr, binding,
|
||||
{resp.in_response_to :"/"})
|
||||
|
||||
# == Look for assertion X
|
||||
|
||||
asid = aresp.assertion.id
|
||||
|
||||
binding, destination = sp.pick_binding("assertion_id_request_service",
|
||||
entity_id=idp.config.entityid)
|
||||
|
||||
_req = sp.create_assertion_id_request([asid], destination)
|
||||
|
||||
hinfo = sp.apply_binding(binding, "%s" % _req, destination,
|
||||
"realy_stat")
|
||||
|
||||
# ---------- @IDP ------------
|
||||
|
||||
xmlstr = get_msg(hinfo, binding)
|
||||
|
||||
rr = idp.parse_assertion_id_request(xmlstr, binding)
|
||||
|
||||
print rr
|
||||
|
||||
# == construct response
|
||||
|
||||
aids = [x.text for x in rr.message.assertion_id_ref]
|
||||
resp_args = idp.response_args(rr.message)
|
||||
|
||||
resp = idp.create_assertion_id_request_response(aids, **resp_args)
|
||||
|
||||
hinfo = idp.apply_binding(binding, "%s" % resp, None, "", "SAMLResponse")
|
||||
|
||||
# ----------- @SP -------------
|
||||
|
||||
xmlstr = get_msg(hinfo, binding)
|
||||
|
||||
final = sp.parse_assertion_id_request_response(xmlstr, binding)
|
||||
|
||||
print final
|
Loading…
Reference in New Issue
Block a user