Assertion ID Request was wrongly defined to use SOAP binding.
Now changed to be URI binding as it should be.
This commit is contained in:
@@ -513,9 +513,10 @@ class Base(Entity):
|
|||||||
kwargs = {"entity_id": self.config.entityid,
|
kwargs = {"entity_id": self.config.entityid,
|
||||||
"attribute_converters": self.config.attribute_converters}
|
"attribute_converters": self.config.attribute_converters}
|
||||||
|
|
||||||
return self._parse_response(response, AssertionIDResponse, "", binding,
|
res = self._parse_response(response, AssertionIDResponse, "", binding,
|
||||||
**kwargs)
|
**kwargs)
|
||||||
|
return res
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
# ------------------------------------------------------------------------
|
||||||
|
|
||||||
def parse_attribute_query_response(self, response, binding):
|
def parse_attribute_query_response(self, response, binding):
|
||||||
|
@@ -10,7 +10,7 @@ import logging.handlers
|
|||||||
|
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
|
|
||||||
from saml2 import root_logger
|
from saml2 import root_logger, BINDING_URI
|
||||||
from saml2 import BINDING_SOAP
|
from saml2 import BINDING_SOAP
|
||||||
from saml2 import BINDING_HTTP_REDIRECT
|
from saml2 import BINDING_HTTP_REDIRECT
|
||||||
from saml2 import BINDING_HTTP_POST
|
from saml2 import BINDING_HTTP_POST
|
||||||
@@ -137,7 +137,7 @@ PREFERRED_BINDING={
|
|||||||
"authn_query_service": [BINDING_SOAP],
|
"authn_query_service": [BINDING_SOAP],
|
||||||
"attribute_service": [BINDING_SOAP],
|
"attribute_service": [BINDING_SOAP],
|
||||||
"authz_service": [BINDING_SOAP],
|
"authz_service": [BINDING_SOAP],
|
||||||
"assertion_id_request_service": [BINDING_SOAP],
|
"assertion_id_request_service": [BINDING_URI],
|
||||||
"artifact_resolution_service": [BINDING_HTTP_REDIRECT, BINDING_HTTP_POST],
|
"artifact_resolution_service": [BINDING_HTTP_REDIRECT, BINDING_HTTP_POST],
|
||||||
"attribute_consuming_service": _RPA
|
"attribute_consuming_service": _RPA
|
||||||
}
|
}
|
||||||
|
@@ -4,7 +4,7 @@ from hashlib import sha1
|
|||||||
from saml2.metadata import ENDPOINTS
|
from saml2.metadata import ENDPOINTS
|
||||||
from saml2.soap import parse_soap_enveloped_saml_artifact_resolve
|
from saml2.soap import parse_soap_enveloped_saml_artifact_resolve
|
||||||
|
|
||||||
from saml2 import samlp, saml, response
|
from saml2 import samlp, saml, response, BINDING_URI
|
||||||
from saml2 import request
|
from saml2 import request
|
||||||
from saml2 import soap
|
from saml2 import soap
|
||||||
from saml2 import element_to_extension_element
|
from saml2 import element_to_extension_element
|
||||||
@@ -120,9 +120,18 @@ class Entity(HTTPBase):
|
|||||||
return Issuer(text=self.config.entityid,
|
return Issuer(text=self.config.entityid,
|
||||||
format=NAMEID_FORMAT_ENTITY)
|
format=NAMEID_FORMAT_ENTITY)
|
||||||
|
|
||||||
def apply_binding(self, binding, msg_str, destination, relay_state,
|
def apply_binding(self, binding, msg_str, destination="", relay_state="",
|
||||||
typ="SAMLRequest"):
|
typ="SAMLRequest"):
|
||||||
|
"""
|
||||||
|
Construct the necessary HTTP arguments dependent on Binding
|
||||||
|
|
||||||
|
:param binding: Which binding to use
|
||||||
|
:param msg_str: The return message as a string (XML)
|
||||||
|
:param destination: Where to send the message
|
||||||
|
:param relay_state: Relay_state if provided
|
||||||
|
:param typ: Which type of message this is
|
||||||
|
:return: A dictionary
|
||||||
|
"""
|
||||||
if binding == BINDING_HTTP_POST:
|
if binding == BINDING_HTTP_POST:
|
||||||
logger.info("HTTP POST")
|
logger.info("HTTP POST")
|
||||||
info = self.use_http_form_post(msg_str, destination,
|
info = self.use_http_form_post(msg_str, destination,
|
||||||
@@ -136,6 +145,8 @@ class Entity(HTTPBase):
|
|||||||
info["method"] = "GET"
|
info["method"] = "GET"
|
||||||
elif binding == BINDING_SOAP:
|
elif binding == BINDING_SOAP:
|
||||||
info = self.use_soap(msg_str, destination)
|
info = self.use_soap(msg_str, destination)
|
||||||
|
elif binding == BINDING_URI:
|
||||||
|
info = self.use_http_uri(msg_str, typ, destination)
|
||||||
else:
|
else:
|
||||||
raise Exception("Unknown binding type: %s" % binding)
|
raise Exception("Unknown binding type: %s" % binding)
|
||||||
|
|
||||||
@@ -227,8 +238,10 @@ class Entity(HTTPBase):
|
|||||||
elif binding == BINDING_SOAP:
|
elif binding == BINDING_SOAP:
|
||||||
func = getattr(soap, "parse_soap_enveloped_saml_%s" % msgtype)
|
func = getattr(soap, "parse_soap_enveloped_saml_%s" % msgtype)
|
||||||
xmlstr = func(txt)
|
xmlstr = func(txt)
|
||||||
|
elif binding == BINDING_URI:
|
||||||
|
xmlstr = txt
|
||||||
else:
|
else:
|
||||||
raise ValueError("Don't know how to handle '%s'")
|
raise ValueError("Don't know how to handle '%s'" % binding)
|
||||||
|
|
||||||
return xmlstr
|
return xmlstr
|
||||||
|
|
||||||
|
@@ -13,7 +13,6 @@ from saml2.pack import make_soap_enveloped_saml_thingy
|
|||||||
from saml2.pack import http_redirect_message
|
from saml2.pack import http_redirect_message
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from saml2.soap import parse_soap_enveloped_saml_response
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -204,6 +203,29 @@ class HTTPBase(object):
|
|||||||
|
|
||||||
return http_redirect_message(message, destination, relay_state, typ)
|
return http_redirect_message(message, destination, relay_state, typ)
|
||||||
|
|
||||||
|
def use_http_uri(self, message, typ, destination=""):
|
||||||
|
if typ == "SAMLResponse":
|
||||||
|
info = {
|
||||||
|
"data": message.split("\n")[1],
|
||||||
|
"headers": [
|
||||||
|
("Content-Type", "application/samlassertion+xml"),
|
||||||
|
("Cache-Control", "no-cache, no-store"),
|
||||||
|
("Pragma", "no-cache")
|
||||||
|
]
|
||||||
|
}
|
||||||
|
elif typ == "SAMLRequest":
|
||||||
|
# msg should be an identifier
|
||||||
|
info = {
|
||||||
|
"data": "",
|
||||||
|
"headers": [
|
||||||
|
("Location", "%s?ID=%s" % (destination, message))
|
||||||
|
]
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
raise NotImplemented
|
||||||
|
|
||||||
|
return info
|
||||||
|
|
||||||
def use_soap(self, request, destination="", headers=None, sign=False):
|
def use_soap(self, request, destination="", headers=None, sign=False):
|
||||||
"""
|
"""
|
||||||
Construct the necessary information for using SOAP+POST
|
Construct the necessary information for using SOAP+POST
|
||||||
|
@@ -8,6 +8,7 @@ from urllib import unquote
|
|||||||
from saml2.s_utils import rndstr
|
from saml2.s_utils import rndstr
|
||||||
from saml2.s_utils import PolicyError
|
from saml2.s_utils import PolicyError
|
||||||
from saml2.saml import NameID
|
from saml2.saml import NameID
|
||||||
|
from saml2.saml import NAMEID_FORMAT_PERSISTENT
|
||||||
from saml2.saml import NAMEID_FORMAT_TRANSIENT
|
from saml2.saml import NAMEID_FORMAT_TRANSIENT
|
||||||
from saml2.saml import NAMEID_FORMAT_EMAILADDRESS
|
from saml2.saml import NAMEID_FORMAT_EMAILADDRESS
|
||||||
|
|
||||||
@@ -156,13 +157,21 @@ class IdentDB(object):
|
|||||||
:param sp_name_qualifier: The 'user'/-s of the name_id
|
:param sp_name_qualifier: The 'user'/-s of the name_id
|
||||||
:param name_id_policy: The policy the server on the other side wants
|
:param name_id_policy: The policy the server on the other side wants
|
||||||
us to follow.
|
us to follow.
|
||||||
:param sp_nid: Name ID Formats from the SPs metadata
|
:param name_qualifier: A domain qualifier
|
||||||
:return: NameID instance precursor
|
:return: NameID instance precursor
|
||||||
"""
|
"""
|
||||||
|
|
||||||
args = self.nim_args(local_policy, sp_name_qualifier, name_id_policy)
|
args = self.nim_args(local_policy, sp_name_qualifier, name_id_policy)
|
||||||
return self.get_nameid(userid, **args)
|
return self.get_nameid(userid, **args)
|
||||||
|
|
||||||
|
def transient_nameid(self, userid, sp_name_qualifier="", name_qualifier=""):
|
||||||
|
return self.get_nameid(userid, NAMEID_FORMAT_TRANSIENT,
|
||||||
|
sp_name_qualifier, name_qualifier)
|
||||||
|
|
||||||
|
def permanent_nameid(self, userid, sp_name_qualifier="", name_qualifier=""):
|
||||||
|
return self.get_nameid(userid, NAMEID_FORMAT_PERSISTENT,
|
||||||
|
sp_name_qualifier, name_qualifier)
|
||||||
|
|
||||||
def find_local_id(self, name_id):
|
def find_local_id(self, name_id):
|
||||||
"""
|
"""
|
||||||
Only find on persistent IDs
|
Only find on persistent IDs
|
||||||
|
@@ -154,17 +154,10 @@ class StatusResponse(object):
|
|||||||
return self._postamble()
|
return self._postamble()
|
||||||
|
|
||||||
def _loads(self, xmldata, decode=True, origxml=None):
|
def _loads(self, xmldata, decode=True, origxml=None):
|
||||||
# if decode:
|
|
||||||
# decoded_xml = base64.b64decode(xmldata)
|
|
||||||
# else:
|
|
||||||
# decoded_xml = xmldata
|
|
||||||
|
|
||||||
# own copy
|
# own copy
|
||||||
self.xmlstr = xmldata[:]
|
self.xmlstr = xmldata[:]
|
||||||
logger.debug("xmlstr: %s" % (self.xmlstr,))
|
logger.debug("xmlstr: %s" % (self.xmlstr,))
|
||||||
# fil = open("response.xml", "w")
|
|
||||||
# fil.write(self.xmlstr)
|
|
||||||
# fil.close()
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.response = self.signature_check(xmldata, origdoc=origxml)
|
self.response = self.signature_check(xmldata, origdoc=origxml)
|
||||||
@@ -641,20 +634,6 @@ class AuthnResponse(StatusResponse):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s" % self.xmlstr
|
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):
|
class AuthnQueryResponse(AuthnResponse):
|
||||||
msgtype = "authn_query_response"
|
msgtype = "authn_query_response"
|
||||||
|
|
||||||
@@ -747,3 +726,62 @@ def response_factory(xmlstr, conf, return_addr=None,
|
|||||||
return logoutresp
|
return logoutresp
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
# ===========================================================================
|
||||||
|
# A class of it's own
|
||||||
|
|
||||||
|
class AssertionIDResponse(object):
|
||||||
|
msgtype = "assertion_id_response"
|
||||||
|
|
||||||
|
def __init__(self, sec_context, attribute_converters, timeslack=0,
|
||||||
|
**kwargs):
|
||||||
|
|
||||||
|
self.sec = sec_context
|
||||||
|
self.timeslack = timeslack
|
||||||
|
self.xmlstr = ""
|
||||||
|
self.name_id = ""
|
||||||
|
self.response = None
|
||||||
|
self.not_signed = False
|
||||||
|
self.attribute_converters = attribute_converters
|
||||||
|
self.assertion = None
|
||||||
|
self.context = "AssertionIdResponse"
|
||||||
|
self.signature_check = self.sec.correctly_signed_assertion_id_response
|
||||||
|
|
||||||
|
def loads(self, xmldata, decode=True, origxml=None):
|
||||||
|
# own copy
|
||||||
|
self.xmlstr = xmldata[:]
|
||||||
|
logger.debug("xmlstr: %s" % (self.xmlstr,))
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.response = self.signature_check(xmldata, origdoc=origxml)
|
||||||
|
self.assertion = self.response
|
||||||
|
except TypeError:
|
||||||
|
raise
|
||||||
|
except SignatureError:
|
||||||
|
raise
|
||||||
|
except Exception, excp:
|
||||||
|
logger.exception("EXCEPTION: %s", excp)
|
||||||
|
raise
|
||||||
|
|
||||||
|
#print "<", self.response
|
||||||
|
|
||||||
|
return self._postamble()
|
||||||
|
|
||||||
|
def verify(self):
|
||||||
|
try:
|
||||||
|
valid_instance(self.response)
|
||||||
|
except NotValid, exc:
|
||||||
|
logger.error("Not valid response: %s" % exc.args[0])
|
||||||
|
raise
|
||||||
|
return self
|
||||||
|
|
||||||
|
def _postamble(self):
|
||||||
|
if not self.response:
|
||||||
|
logger.error("Response was not correctly signed")
|
||||||
|
if self.xmlstr:
|
||||||
|
logger.info(self.xmlstr)
|
||||||
|
raise IncorrectlySigned()
|
||||||
|
|
||||||
|
logger.debug("response: %s" % (self.response,))
|
||||||
|
|
||||||
|
return self
|
||||||
|
@@ -29,8 +29,11 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class VersionMismatch(Exception):
|
class VersionMismatch(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class UnknownPrincipal(Exception):
|
class Unknown(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class UnknownPrincipal(Unknown):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class UnsupportedBinding(Exception):
|
class UnsupportedBinding(Exception):
|
||||||
@@ -45,6 +48,10 @@ class MissingValue(Exception):
|
|||||||
class PolicyError(Exception):
|
class PolicyError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class BadRequest(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
EXCEPTION2STATUS = {
|
EXCEPTION2STATUS = {
|
||||||
VersionMismatch: samlp.STATUS_VERSION_MISMATCH,
|
VersionMismatch: samlp.STATUS_VERSION_MISMATCH,
|
||||||
UnknownPrincipal: samlp.STATUS_UNKNOWN_PRINCIPAL,
|
UnknownPrincipal: samlp.STATUS_UNKNOWN_PRINCIPAL,
|
||||||
|
@@ -39,10 +39,11 @@ from saml2.request import NameIDMappingRequest
|
|||||||
from saml2.request import AuthzDecisionQuery
|
from saml2.request import AuthzDecisionQuery
|
||||||
from saml2.request import AuthnQuery
|
from saml2.request import AuthnQuery
|
||||||
|
|
||||||
from saml2.s_utils import MissingValue
|
from saml2.s_utils import MissingValue, Unknown
|
||||||
|
from saml2.s_utils import BadRequest
|
||||||
from saml2.s_utils import error_status_factory
|
from saml2.s_utils import error_status_factory
|
||||||
|
|
||||||
from saml2.sigver import pre_signature_part
|
from saml2.sigver import pre_signature_part, signed_instance_factory
|
||||||
|
|
||||||
from saml2.assertion import Assertion
|
from saml2.assertion import Assertion
|
||||||
from saml2.assertion import Policy
|
from saml2.assertion import Policy
|
||||||
@@ -461,9 +462,7 @@ class Server(Entity):
|
|||||||
return self.create_error_response(in_response_to, destination,
|
return self.create_error_response(in_response_to, destination,
|
||||||
sp_entity_id, exc, name_id)
|
sp_entity_id, exc, name_id)
|
||||||
|
|
||||||
def create_assertion_id_request_response(self, assertion_id, in_response_to,
|
def create_assertion_id_request_response(self, assertion_id, sign=False):
|
||||||
issuer=None, sign_response=False,
|
|
||||||
status=None):
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
:param assertion_id:
|
:param assertion_id:
|
||||||
@@ -473,23 +472,20 @@ class Server(Entity):
|
|||||||
:param status:
|
:param status:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
# Done over SOAP
|
|
||||||
args = {}
|
|
||||||
to_sign = []
|
|
||||||
|
|
||||||
for aid in assertion_id:
|
try:
|
||||||
try:
|
(assertion, to_sign) = self.get_assertion(assertion_id)
|
||||||
(assertion, to_sign) = self.get_assertion(aid)
|
except KeyError:
|
||||||
to_sign.extend(to_sign)
|
raise Unknown
|
||||||
try:
|
|
||||||
args["assertion"].append(assertion)
|
|
||||||
except KeyError:
|
|
||||||
args["assertion"] = [assertion]
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return self._response(in_response_to, "", status, issuer,
|
if to_sign:
|
||||||
sign_response, to_sign, **args)
|
if assertion.signature is None:
|
||||||
|
assertion.signature = pre_signature_part(assertion.id,
|
||||||
|
self.sec.my_cert, 1)
|
||||||
|
|
||||||
|
return signed_instance_factory(assertion, self.sec, to_sign)
|
||||||
|
else:
|
||||||
|
return assertion
|
||||||
|
|
||||||
def create_name_id_mapping_response(self, name_id=None, encrypted_id=None,
|
def create_name_id_mapping_response(self, name_id=None, encrypted_id=None,
|
||||||
in_response_to=None,
|
in_response_to=None,
|
||||||
|
@@ -746,7 +746,11 @@ class SecurityContext(object):
|
|||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_func = getattr(samlp, "%s_from_string" % msgtype)
|
try:
|
||||||
|
_func = getattr(samlp, "%s_from_string" % msgtype)
|
||||||
|
except AttributeError:
|
||||||
|
_func = getattr(saml, "%s_from_string" % msgtype)
|
||||||
|
|
||||||
msg = _func(decoded_xml)
|
msg = _func(decoded_xml)
|
||||||
if not msg:
|
if not msg:
|
||||||
raise TypeError("Not a %s" % msgtype)
|
raise TypeError("Not a %s" % msgtype)
|
||||||
@@ -839,6 +843,11 @@ class SecurityContext(object):
|
|||||||
"assertion_id_request", must,
|
"assertion_id_request", must,
|
||||||
origdoc)
|
origdoc)
|
||||||
|
|
||||||
|
def correctly_signed_assertion_id_response(self, decoded_xml, must=False,
|
||||||
|
origdoc=None):
|
||||||
|
return self.correctly_signed_message(decoded_xml, "assertion", must,
|
||||||
|
origdoc)
|
||||||
|
|
||||||
def correctly_signed_response(self, decoded_xml, must=False, origdoc=None):
|
def correctly_signed_response(self, decoded_xml, must=False, origdoc=None):
|
||||||
""" Check if a instance is correctly signed, if we have metadata for
|
""" 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
|
the IdP that sent the info use that, if not use the key that are in
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
from saml2 import BINDING_SOAP
|
from saml2 import BINDING_SOAP, BINDING_URI
|
||||||
from saml2 import BINDING_HTTP_REDIRECT
|
from saml2 import BINDING_HTTP_REDIRECT
|
||||||
from saml2 import BINDING_HTTP_POST
|
from saml2 import BINDING_HTTP_POST
|
||||||
from saml2 import BINDING_HTTP_ARTIFACT
|
from saml2 import BINDING_HTTP_ARTIFACT
|
||||||
@@ -51,7 +51,7 @@ CONFIG = {
|
|||||||
("%s/ars" % BASE, BINDING_SOAP)
|
("%s/ars" % BASE, BINDING_SOAP)
|
||||||
],
|
],
|
||||||
"assertion_id_request_service": [
|
"assertion_id_request_service": [
|
||||||
("%s/airs" % BASE, BINDING_SOAP)
|
("%s/airs" % BASE, BINDING_URI)
|
||||||
],
|
],
|
||||||
"authn_query_service": [
|
"authn_query_service": [
|
||||||
("%s/aqs" % BASE, BINDING_SOAP)
|
("%s/aqs" % BASE, BINDING_SOAP)
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
from saml2 import samlp
|
from saml2 import samlp
|
||||||
from saml2.saml import NAMEID_FORMAT_PERSISTENT, NAMEID_FORMAT_TRANSIENT
|
from saml2.saml import NAMEID_FORMAT_PERSISTENT, NAMEID_FORMAT_TRANSIENT
|
||||||
from saml2.config import IdPConfig
|
from saml2.config import IdPConfig
|
||||||
from saml2.server import Identifier
|
from saml2.ident import IdentDB
|
||||||
from saml2.assertion import Policy
|
from saml2.assertion import Policy
|
||||||
|
|
||||||
def _eq(l1,l2):
|
def _eq(l1,l2):
|
||||||
@@ -54,7 +54,7 @@ NAME_ID_POLICY_2 = """<?xml version="1.0" encoding="utf-8"?>
|
|||||||
|
|
||||||
class TestIdentifier():
|
class TestIdentifier():
|
||||||
def setup_class(self):
|
def setup_class(self):
|
||||||
self.id = Identifier("subject.db", CONFIG.vorg)
|
self.id = IdentDB("subject.db", "example.com", "example")
|
||||||
|
|
||||||
def test_persistent_1(self):
|
def test_persistent_1(self):
|
||||||
policy = Policy({
|
policy = Policy({
|
||||||
@@ -67,21 +67,18 @@ class TestIdentifier():
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
nameid = self.id.construct_nameid(policy, "foobar",
|
nameid = self.id.construct_nameid("foobar", policy,
|
||||||
"urn:mace:example.com:sp:1")
|
"urn:mace:example.com:sp:1")
|
||||||
|
|
||||||
assert _eq(nameid.keys(), ['text', 'sp_provided_id',
|
assert _eq(nameid.keyswv(), ['format', 'text', 'sp_name_qualifier',
|
||||||
'sp_name_qualifier', 'name_qualifier', 'format'])
|
'name_qualifier'])
|
||||||
assert _eq(nameid.keyswv(), ['format', 'text', 'sp_name_qualifier'])
|
|
||||||
assert nameid.sp_name_qualifier == "urn:mace:example.com:sp:1"
|
assert nameid.sp_name_qualifier == "urn:mace:example.com:sp:1"
|
||||||
assert nameid.format == NAMEID_FORMAT_PERSISTENT
|
assert nameid.format == NAMEID_FORMAT_PERSISTENT
|
||||||
|
|
||||||
nameid_2 = self.id.construct_nameid(policy, "foobar",
|
id = self.id.find_local_id(nameid)
|
||||||
"urn:mace:example.com:sp:1")
|
|
||||||
|
|
||||||
assert nameid != nameid_2
|
|
||||||
assert nameid.text == nameid_2.text
|
|
||||||
|
|
||||||
|
assert id == "foobar"
|
||||||
|
|
||||||
def test_transient_1(self):
|
def test_transient_1(self):
|
||||||
policy = Policy({
|
policy = Policy({
|
||||||
"default": {
|
"default": {
|
||||||
@@ -92,10 +89,11 @@ class TestIdentifier():
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
nameid = self.id.construct_nameid(policy, "foobar",
|
nameid = self.id.construct_nameid("foobar", policy,
|
||||||
"urn:mace:example.com:sp:1")
|
"urn:mace:example.com:sp:1")
|
||||||
|
|
||||||
assert _eq(nameid.keyswv(), ['text', 'format', 'sp_name_qualifier'])
|
assert _eq(nameid.keyswv(), ['text', 'format', 'sp_name_qualifier',
|
||||||
|
'name_qualifier'])
|
||||||
assert nameid.format == NAMEID_FORMAT_TRANSIENT
|
assert nameid.format == NAMEID_FORMAT_TRANSIENT
|
||||||
|
|
||||||
def test_vo_1(self):
|
def test_vo_1(self):
|
||||||
@@ -111,17 +109,16 @@ class TestIdentifier():
|
|||||||
|
|
||||||
name_id_policy = samlp.name_id_policy_from_string(NAME_ID_POLICY_1)
|
name_id_policy = samlp.name_id_policy_from_string(NAME_ID_POLICY_1)
|
||||||
print name_id_policy
|
print name_id_policy
|
||||||
print self.id.voconf
|
nameid = self.id.construct_nameid("foobar", policy,
|
||||||
nameid = self.id.construct_nameid(policy, "foobar",
|
'http://vo.example.org/biomed',
|
||||||
"urn:mace:example.com:sp:1",
|
name_id_policy)
|
||||||
{"uid": "foobar01"},
|
|
||||||
name_id_policy)
|
|
||||||
|
|
||||||
print nameid
|
print nameid
|
||||||
assert _eq(nameid.keyswv(), ['text', 'sp_name_qualifier', 'format'])
|
assert _eq(nameid.keyswv(), ['text', 'sp_name_qualifier', 'format',
|
||||||
|
'name_qualifier'])
|
||||||
assert nameid.sp_name_qualifier == 'http://vo.example.org/biomed'
|
assert nameid.sp_name_qualifier == 'http://vo.example.org/biomed'
|
||||||
assert nameid.format == 'urn:oid:2.16.756.1.2.5.1.1.1-NameID'
|
assert nameid.format == 'urn:oid:2.16.756.1.2.5.1.1.1-NameID'
|
||||||
assert nameid.text == "foobar01"
|
assert nameid.text == "foobar"
|
||||||
|
|
||||||
def test_vo_2(self):
|
def test_vo_2(self):
|
||||||
policy = Policy({
|
policy = Policy({
|
||||||
@@ -136,8 +133,8 @@ class TestIdentifier():
|
|||||||
|
|
||||||
name_id_policy = samlp.name_id_policy_from_string(NAME_ID_POLICY_2)
|
name_id_policy = samlp.name_id_policy_from_string(NAME_ID_POLICY_2)
|
||||||
|
|
||||||
nameid = self.id.construct_nameid(policy, "foobar",
|
nameid = self.id.construct_nameid("foobar", policy,
|
||||||
"urn:mace:example.com:sp:1",
|
"urn:mace:example.com:sp:1",
|
||||||
{"uid": "foobar01"},
|
{"uid": "foobar01"},
|
||||||
name_id_policy)
|
name_id_policy)
|
||||||
|
|
||||||
|
@@ -2,9 +2,10 @@ from urlparse import parse_qs
|
|||||||
from urlparse import urlparse
|
from urlparse import urlparse
|
||||||
from saml2.samlp import AuthnRequest
|
from saml2.samlp import AuthnRequest
|
||||||
from saml2.samlp import NameIDPolicy
|
from saml2.samlp import NameIDPolicy
|
||||||
from saml2.saml import AUTHN_PASSWORD
|
from saml2.saml import AUTHN_PASSWORD, Assertion
|
||||||
from saml2.saml import NAMEID_FORMAT_TRANSIENT
|
from saml2.saml import NAMEID_FORMAT_TRANSIENT
|
||||||
from saml2 import BINDING_HTTP_POST
|
from saml2 import BINDING_HTTP_POST
|
||||||
|
from saml2 import BINDING_URI
|
||||||
from saml2 import BINDING_SOAP
|
from saml2 import BINDING_SOAP
|
||||||
from saml2.client import Saml2Client
|
from saml2.client import Saml2Client
|
||||||
from saml2.server import Server
|
from saml2.server import Server
|
||||||
@@ -13,20 +14,28 @@ __author__ = 'rolandh'
|
|||||||
|
|
||||||
TAG1 = "name=\"SAMLRequest\" value="
|
TAG1 = "name=\"SAMLRequest\" value="
|
||||||
|
|
||||||
def get_msg(hinfo, binding):
|
def get_msg(hinfo, binding, response=False):
|
||||||
if binding == BINDING_SOAP:
|
if binding == BINDING_SOAP:
|
||||||
xmlstr = hinfo["data"]
|
msg = hinfo["data"]
|
||||||
elif binding == BINDING_HTTP_POST:
|
elif binding == BINDING_HTTP_POST:
|
||||||
_inp = hinfo["data"][3]
|
_inp = hinfo["data"][3]
|
||||||
i = _inp.find(TAG1)
|
i = _inp.find(TAG1)
|
||||||
i += len(TAG1) + 1
|
i += len(TAG1) + 1
|
||||||
j = _inp.find('"', i)
|
j = _inp.find('"', i)
|
||||||
xmlstr = _inp[i:j]
|
msg = _inp[i:j]
|
||||||
|
elif binding == BINDING_URI:
|
||||||
|
if response:
|
||||||
|
msg = hinfo["data"]
|
||||||
|
else:
|
||||||
|
msg = ""
|
||||||
|
for header, val in hinfo["headers"]:
|
||||||
|
if header == "Location":
|
||||||
|
return parse_qs(val.split("?")[1])["ID"][0]
|
||||||
else: # BINDING_HTTP_REDIRECT
|
else: # BINDING_HTTP_REDIRECT
|
||||||
parts = urlparse(hinfo["headers"][0][1])
|
parts = urlparse(hinfo["headers"][0][1])
|
||||||
xmlstr = parse_qs(parts.query)["SAMLRequest"][0]
|
msg = parse_qs(parts.query)["SAMLRequest"][0]
|
||||||
|
|
||||||
return xmlstr
|
return msg
|
||||||
|
|
||||||
def test_basic_flow():
|
def test_basic_flow():
|
||||||
sp = Saml2Client(config_file="servera_conf")
|
sp = Saml2Client(config_file="servera_conf")
|
||||||
@@ -42,7 +51,8 @@ def test_basic_flow():
|
|||||||
|
|
||||||
# == Create an AuthnRequest response
|
# == Create an AuthnRequest response
|
||||||
|
|
||||||
name_id = idp.ident.transient_nameid(sp.config.entityid, "id12")
|
name_id = idp.ident.transient_nameid("id12", sp.config.entityid)
|
||||||
|
|
||||||
binding, destination = idp.pick_binding("assertion_consumer_service",
|
binding, destination = idp.pick_binding("assertion_consumer_service",
|
||||||
entity_id=sp.config.entityid)
|
entity_id=sp.config.entityid)
|
||||||
resp = idp.create_authn_response({"eduPersonEntitlement": "Short stop",
|
resp = idp.create_authn_response({"eduPersonEntitlement": "Short stop",
|
||||||
@@ -73,32 +83,23 @@ def test_basic_flow():
|
|||||||
binding, destination = sp.pick_binding("assertion_id_request_service",
|
binding, destination = sp.pick_binding("assertion_id_request_service",
|
||||||
entity_id=idp.config.entityid)
|
entity_id=idp.config.entityid)
|
||||||
|
|
||||||
_req = sp.create_assertion_id_request([asid], destination)
|
hinfo = sp.apply_binding(binding, asid, destination)
|
||||||
|
|
||||||
hinfo = sp.apply_binding(binding, "%s" % _req, destination,
|
|
||||||
"realy_stat")
|
|
||||||
|
|
||||||
# ---------- @IDP ------------
|
# ---------- @IDP ------------
|
||||||
|
|
||||||
xmlstr = get_msg(hinfo, binding)
|
aid = get_msg(hinfo, binding, response=False)
|
||||||
|
|
||||||
rr = idp.parse_assertion_id_request(xmlstr, binding)
|
|
||||||
|
|
||||||
print rr
|
|
||||||
|
|
||||||
# == construct response
|
# == construct response
|
||||||
|
|
||||||
aids = [x.text for x in rr.message.assertion_id_ref]
|
resp = idp.create_assertion_id_request_response(aid)
|
||||||
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")
|
hinfo = idp.apply_binding(binding, "%s" % resp, None, "", "SAMLResponse")
|
||||||
|
|
||||||
# ----------- @SP -------------
|
# ----------- @SP -------------
|
||||||
|
|
||||||
xmlstr = get_msg(hinfo, binding)
|
xmlstr = get_msg(hinfo, binding, response=True)
|
||||||
|
|
||||||
final = sp.parse_assertion_id_request_response(xmlstr, binding)
|
final = sp.parse_assertion_id_request_response(xmlstr, binding)
|
||||||
|
|
||||||
print final
|
print final.response
|
||||||
|
assert isinstance(final.response, Assertion)
|
Reference in New Issue
Block a user