All tests works now
This commit is contained in:
@@ -18,7 +18,7 @@ Sure you can send a AuthenticationRequest to an IdentityProvider or a
|
||||
AttributeQuery to an AttributeAuthority but in order to get what they
|
||||
return you have to sit behind a Web server. Well that is not really true since
|
||||
the AttributeQuery would be over SOAP and you would get the result over the
|
||||
conenction you have to the AttributeAuthority.
|
||||
connection you have to the AttributeAuthority.
|
||||
|
||||
But anyway, you may get my point. This is middleware stuff !
|
||||
|
||||
|
@@ -67,7 +67,7 @@ def filter_on_attributes(ava, required=None, optional=None):
|
||||
:param ava: An attribute value assertion as a dictionary
|
||||
:param required: list of RequestedAttribute instances defined to be
|
||||
required
|
||||
:param optional: list of RequestedAttribute instances defined to be
|
||||
:param optional: list of RequestedAttribute instances defined to be
|
||||
optional
|
||||
:return: The modified attribute value assertion
|
||||
"""
|
||||
@@ -79,13 +79,15 @@ def filter_on_attributes(ava, required=None, optional=None):
|
||||
for attr in required:
|
||||
if attr.friendly_name in ava:
|
||||
values = [av.text for av in attr.attribute_value]
|
||||
res[attr.friendly_name] = _filter_values(ava[attr.friendly_name], values, True)
|
||||
res[attr.friendly_name] = _filter_values(ava[attr.friendly_name],
|
||||
values, True)
|
||||
elif attr.name in ava:
|
||||
values = [av.text for av in attr.attribute_value]
|
||||
res[attr.name] = _filter_values(ava[attr.name], values, True)
|
||||
else:
|
||||
_name = attr.friendly_name or attr.name
|
||||
print >> sys.stderr, ava.keys()
|
||||
raise MissingValue("Required attribute missing: '%s'" % (attr.friendly_name,))
|
||||
raise MissingValue("Required attribute missing: '%s'" % (_name,))
|
||||
|
||||
if optional is None:
|
||||
optional = []
|
||||
@@ -94,9 +96,11 @@ def filter_on_attributes(ava, required=None, optional=None):
|
||||
if attr.friendly_name in ava:
|
||||
values = [av.text for av in attr.attribute_value]
|
||||
try:
|
||||
res[attr.friendly_name].extend(_filter_values(ava[attr.friendly_name], values))
|
||||
res[attr.friendly_name].extend(_filter_values(ava[attr.friendly_name],
|
||||
values))
|
||||
except KeyError:
|
||||
res[attr.friendly_name] = _filter_values(ava[attr.friendly_name], values)
|
||||
res[attr.friendly_name] = _filter_values(ava[attr.friendly_name],
|
||||
values)
|
||||
elif attr.name in ava:
|
||||
values = [av.text for av in attr.attribute_value]
|
||||
try:
|
||||
@@ -379,8 +383,7 @@ class Policy(object):
|
||||
If the requirements can't be met an exception is raised.
|
||||
"""
|
||||
if metadata:
|
||||
(required, optional) = metadata.attribute_consumer(sp_entity_id)
|
||||
#(required, optional) = metadata.wants(sp_entity_id)
|
||||
(required, optional) = metadata.attribute_requirement(sp_entity_id)
|
||||
else:
|
||||
required = optional = None
|
||||
|
||||
|
@@ -20,7 +20,7 @@ to conclude its tasks.
|
||||
"""
|
||||
|
||||
import saml2
|
||||
from saml2.saml import AssertionIDRef
|
||||
from saml2.saml import AssertionIDRef, NAMEID_FORMAT_PERSISTENT
|
||||
|
||||
try:
|
||||
from urlparse import parse_qs
|
||||
@@ -51,6 +51,7 @@ class Saml2Client(Base):
|
||||
|
||||
def do_authenticate(self, entityid=None, relay_state="",
|
||||
binding=saml2.BINDING_HTTP_REDIRECT, vorg="",
|
||||
nameid_format=NAMEID_FORMAT_PERSISTENT,
|
||||
scoping=None, consent=None, extensions=None, sign=None):
|
||||
""" Makes an authentication request.
|
||||
|
||||
@@ -68,11 +69,10 @@ class Saml2Client(Base):
|
||||
|
||||
location = self._sso_location(entityid, binding)
|
||||
|
||||
session_id, _req_str = "%s" % self.create_authn_request(location, vorg,
|
||||
scoping,
|
||||
consent,
|
||||
extensions,
|
||||
sign)
|
||||
req = self.create_authn_request(location, vorg, scoping, binding,
|
||||
nameid_format, consent, extensions,
|
||||
sign)
|
||||
_req_str = "%s" % req
|
||||
|
||||
logger.info("AuthNReq: %s" % _req_str)
|
||||
|
||||
@@ -90,7 +90,7 @@ class Saml2Client(Base):
|
||||
else:
|
||||
raise Exception("Unknown binding type: %s" % binding)
|
||||
|
||||
return session_id, response
|
||||
return response
|
||||
|
||||
def global_logout(self, subject_id, reason="", expire=None, sign=None,
|
||||
return_to="/"):
|
||||
@@ -146,8 +146,9 @@ class Saml2Client(Base):
|
||||
destination = destinations[0]
|
||||
|
||||
logger.info("destination to provider: %s" % destination)
|
||||
request = self.create_logout_request(subject_id, destination,
|
||||
entity_id, reason, expire)
|
||||
request = self.create_logout_request(subject_id,
|
||||
destination, entity_id,
|
||||
reason, expire)
|
||||
|
||||
to_sign = []
|
||||
#if sign and binding != BINDING_HTTP_REDIRECT:
|
||||
@@ -311,8 +312,13 @@ class Saml2Client(Base):
|
||||
def _soap_query_response(self, destination, query_type, **kwargs):
|
||||
_create_func = getattr(self, "create_%s" % query_type)
|
||||
_response_func = getattr(self, "%s_response" % query_type)
|
||||
try:
|
||||
response_args = kwargs["response_args"]
|
||||
del kwargs["response_args"]
|
||||
except KeyError:
|
||||
response_args = None
|
||||
|
||||
id, query = _create_func(destination, **kwargs)
|
||||
query = _create_func(destination, **kwargs)
|
||||
|
||||
response = send_using_soap(query, destination,
|
||||
self.config.key_file,
|
||||
@@ -321,8 +327,8 @@ class Saml2Client(Base):
|
||||
|
||||
if response:
|
||||
logger.info("Verifying response")
|
||||
if "response_args" in kwargs:
|
||||
response = _response_func(response, **kwargs["response_args"])
|
||||
if response_args:
|
||||
response = _response_func(response, **response_args)
|
||||
else:
|
||||
response = _response_func(response)
|
||||
|
||||
@@ -387,7 +393,7 @@ class Saml2Client(Base):
|
||||
consent=consent, extensions=extensions,
|
||||
sign=sign)
|
||||
|
||||
def do_attribute_query(self, subject_id, entityid,
|
||||
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,
|
||||
@@ -396,8 +402,8 @@ class Saml2Client(Base):
|
||||
by default done over SOAP. Other bindings could be used but not
|
||||
supported right now.
|
||||
|
||||
:param subject_id: The identifier of the subject
|
||||
: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
|
||||
|
@@ -18,8 +18,7 @@
|
||||
"""Contains classes and functions that a SAML2.0 Service Provider (SP) may use
|
||||
to conclude its tasks.
|
||||
"""
|
||||
from random import random
|
||||
from saml2.saml import AssertionIDRef
|
||||
from saml2.saml import AssertionIDRef, NAMEID_FORMAT_TRANSIENT
|
||||
from saml2.samlp import AuthnQuery, LogoutRequest, AssertionIDRequest
|
||||
from saml2.samlp import AttributeQuery
|
||||
from saml2.samlp import AuthzDecisionQuery
|
||||
@@ -35,7 +34,7 @@ except ImportError:
|
||||
from cgi import parse_qs
|
||||
|
||||
from saml2.time_util import instant
|
||||
from saml2.s_utils import signature
|
||||
from saml2.s_utils import signature, rndstr
|
||||
from saml2.s_utils import sid
|
||||
from saml2.s_utils import do_attributes
|
||||
from saml2.s_utils import decode_base64_and_inflate
|
||||
@@ -123,23 +122,17 @@ class Base(object):
|
||||
else:
|
||||
self.vorg = None
|
||||
|
||||
if "allow_unsolicited" in self.config:
|
||||
self.allow_unsolicited = self.config.allow_unsolicited
|
||||
else:
|
||||
self.allow_unsolicited = False
|
||||
|
||||
if getattr(self.config, 'authn_requests_signed', 'false') == 'true':
|
||||
self.authn_requests_signed_default = True
|
||||
else:
|
||||
self.authn_requests_signed_default = False
|
||||
|
||||
if getattr(self.config, 'logout_requests_signed', 'false') == 'true':
|
||||
self.logout_requests_signed_default = True
|
||||
else:
|
||||
self.logout_requests_signed_default = False
|
||||
for foo in ["allow_unsolicited", "authn_requests_signed",
|
||||
"logout_requests_signed"]:
|
||||
if self.config.getattr("sp", foo) == 'true':
|
||||
setattr(self, foo, True)
|
||||
else:
|
||||
setattr(self, foo, False)
|
||||
|
||||
# extra randomness
|
||||
self.seed = random()
|
||||
self.seed = rndstr(32)
|
||||
self.logout_requests_signed_default = True
|
||||
self.allow_unsolicited = self.config.getattr("allow_unsolicited", "sp")
|
||||
|
||||
#
|
||||
# Private methods
|
||||
@@ -223,7 +216,7 @@ class Base(object):
|
||||
return True
|
||||
|
||||
def service_url(self, binding=BINDING_HTTP_POST):
|
||||
_res = self.config.endpoint("assertion_consumer_service", binding)
|
||||
_res = self.config.endpoint("assertion_consumer_service", binding, "sp")
|
||||
if _res:
|
||||
return _res[0]
|
||||
else:
|
||||
@@ -266,23 +259,23 @@ class Base(object):
|
||||
|
||||
logger.info("REQUEST: %s" % req)
|
||||
|
||||
return id, signed_instance_factory(req, self.sec, to_sign)
|
||||
return signed_instance_factory(req, self.sec, to_sign)
|
||||
|
||||
def create_authn_request(self, destination, id=0, vorg="", scoping=None,
|
||||
def create_authn_request(self, destination, vorg="", scoping=None,
|
||||
binding=saml2.BINDING_HTTP_POST,
|
||||
nameid_format=saml.NAMEID_FORMAT_TRANSIENT,
|
||||
nameid_format=NAMEID_FORMAT_TRANSIENT,
|
||||
service_url_binding=None,
|
||||
consent=None, extensions=None, sign=None):
|
||||
id=0, consent=None, extensions=None, sign=None):
|
||||
""" Creates an authentication request.
|
||||
|
||||
:param destination: Where the request should be sent.
|
||||
:param id: The identifier for this request
|
||||
:param vorg: The virtual organization the service belongs to.
|
||||
:param scoping: The scope of the request
|
||||
:param binding: The protocol to use for the Response !!
|
||||
:param nameid_format: Format of the NameID
|
||||
:param service_url_binding: Where the reply should be sent dependent
|
||||
on reply binding.
|
||||
:param id: The identifier for this request
|
||||
:param consent: Whether the principal have given her consent
|
||||
:param extensions: Possible extensions
|
||||
:param sign: Whether the request should be signed or not.
|
||||
@@ -301,11 +294,12 @@ class Base(object):
|
||||
my_name = self._my_name()
|
||||
|
||||
# Profile stuff, should be configurable
|
||||
if nameid_format == saml.NAMEID_FORMAT_TRANSIENT:
|
||||
if nameid_format is None or nameid_format == NAMEID_FORMAT_TRANSIENT:
|
||||
name_id_policy = samlp.NameIDPolicy(allow_create="true",
|
||||
format=nameid_format)
|
||||
format=NAMEID_FORMAT_TRANSIENT)
|
||||
else:
|
||||
name_id_policy = samlp.NameIDPolicy(format=nameid_format)
|
||||
name_id_policy = samlp.NameIDPolicy(allow_create="false",
|
||||
format=nameid_format)
|
||||
|
||||
if vorg:
|
||||
try:
|
||||
@@ -317,20 +311,20 @@ class Base(object):
|
||||
return self._message(AuthnRequest, destination, id, consent,
|
||||
extensions, sign,
|
||||
assertion_consumer_service_url=service_url,
|
||||
protocol_binding= binding,
|
||||
name_id_policy = name_id_policy,
|
||||
provider_name = my_name,
|
||||
scoping = scoping)
|
||||
protocol_binding=binding,
|
||||
name_id_policy=name_id_policy,
|
||||
provider_name=my_name,
|
||||
scoping=scoping)
|
||||
|
||||
|
||||
def create_attribute_query(self, destination, id, subject_id,
|
||||
def create_attribute_query(self, destination, subject_id,
|
||||
attribute=None, sp_name_qualifier=None,
|
||||
name_qualifier=None, nameid_format=None,
|
||||
consent=None, extensions=None, sign=False):
|
||||
id=0, consent=None, extensions=None, sign=False,
|
||||
**kwargs):
|
||||
""" Constructs an AttributeQuery
|
||||
|
||||
:param destination: To whom the query should be sent
|
||||
:param id: The identifier of the session
|
||||
:param subject_id: The identifier of the subject
|
||||
:param attribute: A dictionary of attributes and values that is
|
||||
asked for. The key are one of 4 variants:
|
||||
@@ -344,6 +338,7 @@ class Base(object):
|
||||
:param name_qualifier: The unique identifier of the identity
|
||||
provider that generated the identifier.
|
||||
:param nameid_format: The format of the name ID
|
||||
:param id: The identifier of the session
|
||||
:param consent: Whether the principal have given her consent
|
||||
:param extensions: Possible extensions
|
||||
:param sign: Whether the query should be signed or not.
|
||||
@@ -365,13 +360,12 @@ class Base(object):
|
||||
attribute=attribute)
|
||||
|
||||
|
||||
def create_logout_request(self, destination, id, subject_id,
|
||||
issuer_entity_id, reason=None, expire=None,
|
||||
consent=None, extensions=None, sign=False):
|
||||
def create_logout_request(self, destination, subject_id, issuer_entity_id,
|
||||
reason=None, expire=None,
|
||||
id=0, consent=None, extensions=None, sign=False):
|
||||
""" Constructs a LogoutRequest
|
||||
|
||||
:param destination: Destination of the request
|
||||
:param id: Request identifier
|
||||
:param subject_id: The identifier of the subject
|
||||
:param issuer_entity_id: The entity ID of the IdP the request is
|
||||
target at.
|
||||
@@ -379,6 +373,7 @@ class Base(object):
|
||||
form of a URI reference.
|
||||
:param expire: The time at which the request expires,
|
||||
after which the recipient may discard the message.
|
||||
:param id: Request identifier
|
||||
:param consent: Whether the principal have given her consent
|
||||
:param extensions: Possible extensions
|
||||
:param sign: Whether the query should be signed or not.
|
||||
@@ -419,21 +414,21 @@ class Base(object):
|
||||
# AssertionIDRequest, SubjectQuery,
|
||||
# AuthnQuery, AttributeQuery, or AuthzDecisionQuery
|
||||
|
||||
def create_authz_decision_query(self, destination, action, id=0,
|
||||
def create_authz_decision_query(self, destination, action,
|
||||
evidence=None, resource=None, subject=None,
|
||||
sign=None, consent=None,
|
||||
extensions=None):
|
||||
id=0, consent=None, extensions=None,
|
||||
sign=None):
|
||||
""" Creates an authz decision query.
|
||||
|
||||
:param destination: The IdP endpoint
|
||||
:param action: The action you want to perform (has to be at least one)
|
||||
:param id: Message identifier
|
||||
:param evidence: Why you should be able to perform the action
|
||||
:param resource: The resource you want to perform the action on
|
||||
:param subject: Who wants to do the thing
|
||||
:param sign: Whether the request should be signed or not.
|
||||
:param id: Message identifier
|
||||
:param consent: If the principal gave her consent to this request
|
||||
:param extensions: Possible request extensions
|
||||
:param sign: Whether the request should be signed or not.
|
||||
:return: AuthzDecisionQuery instance
|
||||
"""
|
||||
|
||||
@@ -443,17 +438,20 @@ class Base(object):
|
||||
|
||||
def create_authz_decision_query_using_assertion(self, destination, assertion,
|
||||
action=None, resource=None,
|
||||
subject=None,
|
||||
binding=BINDING_HTTP_REDIRECT,
|
||||
subject=None, id=0,
|
||||
consent=None,
|
||||
extensions=None,
|
||||
sign=False):
|
||||
""" Makes an authz decision query.
|
||||
|
||||
:param destination: The IdP endpoint to send the request to
|
||||
:param assertion:
|
||||
:param action:
|
||||
:param resource:
|
||||
:param subject:
|
||||
:param binding: Which binding to use for sending the request
|
||||
:param assertion: An Assertion instance
|
||||
:param action: The action you want to perform (has to be at least one)
|
||||
:param resource: The resource you want to perform the action on
|
||||
:param subject: Who wants to do the thing
|
||||
:param id: Message identifier
|
||||
:param consent: If the principal gave her consent to this request
|
||||
:param extensions: Possible request extensions
|
||||
:param sign: Whether the request should be signed or not.
|
||||
:return: AuthzDecisionQuery instance
|
||||
"""
|
||||
@@ -469,24 +467,46 @@ class Base(object):
|
||||
return self.create_authz_decision_query(destination,
|
||||
_action,
|
||||
saml.Evidence(assertion=assertion),
|
||||
resource, subject, binding,
|
||||
sign)
|
||||
resource, subject,
|
||||
id=id,
|
||||
consent=consent,
|
||||
extensions=extensions,
|
||||
sign=sign)
|
||||
|
||||
def create_assertion_id_request(self, assertion_id_refs, destination=None,
|
||||
id=0, consent=None, extensions=None,
|
||||
sign=False):
|
||||
"""
|
||||
|
||||
:param assertion_id_refs:
|
||||
:param destination: The IdP endpoint to send the request to
|
||||
:param id: Message identifier
|
||||
:param consent: If the principal gave her consent to this request
|
||||
:param extensions: Possible request extensions
|
||||
:param sign: Whether the request should be signed or not.
|
||||
:return: AssertionIDRequest instance
|
||||
"""
|
||||
id_refs = [AssertionIDRef(text=s) for s in assertion_id_refs]
|
||||
|
||||
return self._message(AssertionIDRequest, destination, id, consent,
|
||||
extensions, sign, assertion_id_refs=id_refs )
|
||||
|
||||
|
||||
def create_authn_query(self, subject, destination=None, id=0,
|
||||
def create_authn_query(self, subject, destination=None,
|
||||
authn_context=None, session_index="",
|
||||
consent=None, sign=False,
|
||||
extensions=None):
|
||||
id=0, consent=None, extensions=None, sign=False):
|
||||
"""
|
||||
|
||||
:param subject:
|
||||
:param destination: The IdP endpoint to send the request to
|
||||
:param authn_context:
|
||||
:param session_index:
|
||||
:param id: Message identifier
|
||||
:param consent: If the principal gave her consent to this request
|
||||
:param extensions: Possible request extensions
|
||||
:param sign: Whether the request should be signed or not.
|
||||
:return:
|
||||
"""
|
||||
return self._message(AuthnQuery, destination, id, consent, extensions,
|
||||
sign, subject=subject, session_index=session_index,
|
||||
requested_auth_context=authn_context)
|
||||
|
@@ -22,7 +22,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
COMMON_ARGS = ["entityid", "xmlsec_binary", "debug", "key_file", "cert_file",
|
||||
"secret", "accepted_time_diff", "name", "ca_certs",
|
||||
"description",
|
||||
"description", "valid_for",
|
||||
"organization",
|
||||
"contact_person",
|
||||
"name_form",
|
||||
@@ -97,48 +97,61 @@ class Config(object):
|
||||
def_context = ""
|
||||
|
||||
def __init__(self):
|
||||
self._attr = {"": {}, "sp": {}, "idp": {}, "aa": {}, "pdp": {}}
|
||||
self.entityid = None
|
||||
self.xmlsec_binary= None
|
||||
self.debug=False
|
||||
self.key_file=None
|
||||
self.cert_file=None
|
||||
self.secret=None
|
||||
self.accepted_time_diff=None
|
||||
self.name=None
|
||||
self.ca_certs=None
|
||||
self.description=None
|
||||
self.valid_for=None
|
||||
self.organization=None
|
||||
self.contact_person=None
|
||||
self.name_form=None
|
||||
self.virtual_organization=None
|
||||
self.logger=None
|
||||
self.only_use_keys_in_metadata=None
|
||||
self.logout_requests_signed=None
|
||||
self.disable_ssl_certificate_validation=None
|
||||
self.context = ""
|
||||
self.attribute_converters=None
|
||||
self.metadata=None
|
||||
self.policy=None
|
||||
self.serves = []
|
||||
|
||||
def serves(self):
|
||||
return [t for t in ["sp", "idp", "aa", "pdp"] if self._attr[t]]
|
||||
|
||||
def copy_into(self, typ=""):
|
||||
if typ == "sp":
|
||||
copy = SPConfig()
|
||||
elif typ in ["idp", "aa"]:
|
||||
copy = IdPConfig()
|
||||
else:
|
||||
copy = Config()
|
||||
copy.context = typ
|
||||
copy._attr = self._attr.copy()
|
||||
return copy
|
||||
|
||||
def __getattribute__(self, item):
|
||||
if item == "context":
|
||||
return object.__getattribute__(self, item)
|
||||
|
||||
_context = self.context
|
||||
if item in ALL:
|
||||
try:
|
||||
return self._attr[_context][item]
|
||||
except KeyError:
|
||||
if _context:
|
||||
try:
|
||||
return self._attr[""][item]
|
||||
except KeyError:
|
||||
pass
|
||||
return None
|
||||
else:
|
||||
return object.__getattribute__(self, item)
|
||||
# def copy_into(self, typ=""):
|
||||
# if typ == "sp":
|
||||
# copy = SPConfig()
|
||||
# elif typ in ["idp", "aa"]:
|
||||
# copy = IdPConfig()
|
||||
# else:
|
||||
# copy = Config()
|
||||
# copy.context = typ
|
||||
# copy._attr = self._attr.copy()
|
||||
# return copy
|
||||
|
||||
def setattr(self, context, attr, val):
|
||||
self._attr[context][attr] = val
|
||||
if context == "":
|
||||
setattr(self, attr, val)
|
||||
else:
|
||||
setattr(self, "_%s_%s" % (context,attr), val)
|
||||
|
||||
def getattr(self, attr, context=None):
|
||||
if context is None:
|
||||
context = self.context
|
||||
|
||||
if context == "":
|
||||
return getattr(self, attr, None)
|
||||
else:
|
||||
return getattr(self, "_%s_%s" % (context,attr), None)
|
||||
|
||||
def load_special(self, cnf, typ, metadata_construction=False):
|
||||
for arg in SPEC[typ]:
|
||||
try:
|
||||
self._attr[typ][arg] = cnf[arg]
|
||||
self.setattr(typ, arg, cnf[arg])
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
@@ -147,9 +160,8 @@ class Config(object):
|
||||
self.context = self.def_context
|
||||
|
||||
def load_complex(self, cnf, typ="", metadata_construction=False):
|
||||
_attr_typ = self._attr[typ]
|
||||
try:
|
||||
_attr_typ["policy"] = Policy(cnf["policy"])
|
||||
self.setattr(typ, "policy", Policy(cnf["policy"]))
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
@@ -162,16 +174,20 @@ class Config(object):
|
||||
if not acs:
|
||||
raise Exception(("No attribute converters, ",
|
||||
"something is wrong!!"))
|
||||
try:
|
||||
_attr_typ["attribute_converters"].extend(acs)
|
||||
except KeyError:
|
||||
_attr_typ["attribute_converters"] = acs
|
||||
|
||||
_acs = self.getattr("attribute_converters", typ)
|
||||
if _acs:
|
||||
_acs.extend(acs)
|
||||
else:
|
||||
self.setattr(typ, "attribute_converters", acs)
|
||||
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
if not metadata_construction:
|
||||
try:
|
||||
_attr_typ["metadata"] = self.load_metadata(cnf["metadata"])
|
||||
self.setattr(typ, "metadata",
|
||||
self.load_metadata(cnf["metadata"]))
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
@@ -185,7 +201,7 @@ class Config(object):
|
||||
"""
|
||||
for arg in COMMON_ARGS:
|
||||
try:
|
||||
self._attr[""][arg] = cnf[arg]
|
||||
setattr(self, arg, cnf[arg])
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
@@ -194,19 +210,19 @@ class Config(object):
|
||||
try:
|
||||
self.load_special(cnf["service"][typ], typ,
|
||||
metadata_construction=metadata_construction)
|
||||
|
||||
self.serves.append(typ)
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
if not metadata_construction:
|
||||
if "xmlsec_binary" not in self._attr[""]:
|
||||
self._attr[""]["xmlsec_binary"] = get_xmlsec_binary()
|
||||
if not self.xmlsec_binary:
|
||||
self.xmlsec_binary = get_xmlsec_binary()
|
||||
|
||||
# verify that xmlsec is where it's supposed to be
|
||||
if not os.path.exists(self._attr[""]["xmlsec_binary"]):
|
||||
if not os.path.exists(self.xmlsec_binary):
|
||||
#if not os.access(, os.F_OK):
|
||||
raise Exception("xmlsec binary not in '%s' !" % (
|
||||
self._attr[""]["xmlsec_binary"]))
|
||||
self.xmlsec_binary))
|
||||
|
||||
self.load_complex(cnf, metadata_construction=metadata_construction)
|
||||
self.context = self.def_context
|
||||
@@ -258,7 +274,7 @@ class Config(object):
|
||||
metad.import_external_metadata(spec["url"], cert)
|
||||
return metad
|
||||
|
||||
def endpoint(self, service, binding=None):
|
||||
def endpoint(self, service, binding=None, context=None):
|
||||
""" Goes through the list of endpoint specifications for the
|
||||
given type of service and returnes the first endpoint that matches
|
||||
the given binding. If no binding is given any endpoint for that
|
||||
@@ -270,13 +286,15 @@ class Config(object):
|
||||
"""
|
||||
spec = []
|
||||
unspec = []
|
||||
for endpspec in self.endpoints[service]:
|
||||
try:
|
||||
endp, bind = endpspec
|
||||
if binding is None or bind == binding:
|
||||
spec.append(endp)
|
||||
except ValueError:
|
||||
unspec.append(endpspec)
|
||||
endps = self.getattr("endpoints")
|
||||
if endps and service in endps:
|
||||
for endpspec in endps[service]:
|
||||
try:
|
||||
endp, bind = endpspec
|
||||
if binding is None or bind == binding:
|
||||
spec.append(endp)
|
||||
except ValueError:
|
||||
unspec.append(endpspec)
|
||||
|
||||
if spec:
|
||||
return spec
|
||||
@@ -327,9 +345,8 @@ class Config(object):
|
||||
if root_logger.level != logging.NOTSET: # Someone got there before me
|
||||
return root_logger
|
||||
|
||||
try:
|
||||
_logconf = self._attr[""]["logger"]
|
||||
except KeyError:
|
||||
_logconf = self.logger
|
||||
if _logconf is None:
|
||||
return root_logger
|
||||
|
||||
try:
|
||||
@@ -340,21 +357,7 @@ class Config(object):
|
||||
root_logger.addHandler(self.log_handler())
|
||||
root_logger.info("Logging started")
|
||||
return root_logger
|
||||
|
||||
def keys(self):
|
||||
keys = []
|
||||
|
||||
for dir in ["", "sp", "idp", "aa"]:
|
||||
keys.extend(self._attr[dir].keys())
|
||||
|
||||
return list(set(keys))
|
||||
|
||||
def __contains__(self, item):
|
||||
for dir in ["", "sp", "idp", "aa"]:
|
||||
if item in self._attr[dir]:
|
||||
return True
|
||||
return False
|
||||
|
||||
class SPConfig(Config):
|
||||
def_context = "sp"
|
||||
|
||||
@@ -393,16 +396,20 @@ class SPConfig(Config):
|
||||
"""
|
||||
|
||||
res = []
|
||||
if self.aa is None or entity_id in self.aa:
|
||||
for aad in self.metadata.attribute_authority(entity_id):
|
||||
for attrserv in aad.attribute_service:
|
||||
if attrserv.binding == binding:
|
||||
res.append(attrserv)
|
||||
aa_eid = self.getattr("entity_id")
|
||||
if aa_eid:
|
||||
if entity_id in aa_eid:
|
||||
for aad in self.metadata.attribute_authority(entity_id):
|
||||
for attrserv in aad.attribute_service:
|
||||
if attrserv.binding == binding:
|
||||
res.append(attrserv)
|
||||
else:
|
||||
return self.metadata.attribute_authority()
|
||||
|
||||
return res
|
||||
|
||||
def idps(self, langpref=None):
|
||||
""" Returns a dictionary of usefull IdPs, the keys being the
|
||||
""" Returns a dictionary of useful IdPs, the keys being the
|
||||
entity ID of the service and the names of the services as values
|
||||
|
||||
:param langpref: The preferred languages of the name, the first match
|
||||
@@ -411,12 +418,14 @@ class SPConfig(Config):
|
||||
"""
|
||||
if langpref is None:
|
||||
langpref = ["en"]
|
||||
|
||||
if self.idp:
|
||||
|
||||
eidp = self.getattr("entity_id")
|
||||
if eidp:
|
||||
return dict([(e, nd[0]) for (e,
|
||||
nd) in self.metadata.idps(langpref).items() if e in self.idp])
|
||||
nd) in self.metadata.idps(langpref).items() if e in eidp])
|
||||
else:
|
||||
return self.metadata.idps()
|
||||
return dict([(e, nd[0]) for (e,
|
||||
nd) in self.metadata.idps(langpref).items()])
|
||||
|
||||
def vo_conf(self, vo_name):
|
||||
try:
|
||||
@@ -431,8 +440,9 @@ class SPConfig(Config):
|
||||
:param ipaddress: The IP address of the user client
|
||||
:return: IdP entity ID or None
|
||||
"""
|
||||
if "ecp" in self._attr["sp"]:
|
||||
for key, eid in self._attr["sp"]["ecp"].items():
|
||||
_ecp = self.getattr("ecp")
|
||||
if _ecp:
|
||||
for key, eid in _ecp.items():
|
||||
if re.match(key, ipaddress):
|
||||
return eid
|
||||
|
||||
|
@@ -32,7 +32,6 @@ from saml2.profile import ecp
|
||||
from saml2.server import Server
|
||||
|
||||
from saml2.schema import soapenv
|
||||
from saml2.s_utils import sid
|
||||
|
||||
from saml2.response import authn_response
|
||||
|
||||
@@ -115,9 +114,9 @@ def ecp_auth_request(cls, entityid=None, relay_state="", sign=False):
|
||||
logger.info("entityid: %s, binding: %s" % (entityid, BINDING_SOAP))
|
||||
|
||||
location = cls._sso_location(entityid, binding=BINDING_SOAP)
|
||||
session_id = sid()
|
||||
authn_req = cls.authn(location, session_id, binding=BINDING_PAOS,
|
||||
service_url_binding=BINDING_PAOS)
|
||||
authn_req = cls.create_authn_request(location,
|
||||
binding=BINDING_PAOS,
|
||||
service_url_binding=BINDING_PAOS)
|
||||
|
||||
body = soapenv.Body()
|
||||
body.extension_elements = [element_to_extension_element(authn_req)]
|
||||
@@ -128,7 +127,7 @@ def ecp_auth_request(cls, entityid=None, relay_state="", sign=False):
|
||||
|
||||
soap_envelope = soapenv.Envelope(header=header, body=body)
|
||||
|
||||
return session_id, "%s" % soap_envelope
|
||||
return authn_req.id, "%s" % soap_envelope
|
||||
|
||||
|
||||
def handle_ecp_authn_response(cls, soap_message, outstanding=None):
|
||||
|
@@ -51,7 +51,6 @@ from saml2.saml import NAME_FORMAT_URI
|
||||
from saml2.time_util import in_a_while
|
||||
from saml2.time_util import valid
|
||||
from saml2.attribute_converter import from_local_name
|
||||
from saml2.attribute_converter import ava_fro
|
||||
from saml2.sigver import pre_signature_part
|
||||
from saml2.sigver import make_temp, cert_from_key_info, verify_signature
|
||||
from saml2.sigver import pem_format
|
||||
@@ -104,7 +103,6 @@ class MetaData(object):
|
||||
self.http = httplib2.Http(ca_certs=ca_certs,
|
||||
disable_ssl_certificate_validation=disable_ssl_certificate_validation)
|
||||
self._import = {}
|
||||
self._wants = {}
|
||||
self._keys = {}
|
||||
self._extension_modules = metadata_extension_modules()
|
||||
self.post_load_process = post_load_process
|
||||
@@ -152,7 +150,7 @@ class MetaData(object):
|
||||
except KeyError:
|
||||
self._loc_key[ident] = {use: certs}
|
||||
|
||||
def _vo_metadata(self, entity_descr, entity, tag):
|
||||
def _affiliation(self, entity_descr, entity, tag):
|
||||
"""
|
||||
Pick out the Affiliation descriptors from an entity
|
||||
descriptor and store the information in a way which is easily
|
||||
@@ -169,61 +167,26 @@ class MetaData(object):
|
||||
|
||||
if members:
|
||||
entity[tag] = members
|
||||
|
||||
def _sp_metadata(self, entity_descr, entity, tag):
|
||||
|
||||
afd._certs = self._certs(afd.key_descriptor, "pem")
|
||||
self._add_certs(entity_descr.entity_id, afd._certs)
|
||||
|
||||
def _spsso(self, dp, entity_descr):
|
||||
"""
|
||||
Pick out the SP SSO descriptors from an entity
|
||||
descriptor and store the information in a way which is easily
|
||||
accessible.
|
||||
|
||||
:param entity_descr: A EntityDescriptor instance
|
||||
|
||||
:param dp:
|
||||
:param entity_descr
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
ssd = entity_descr.spsso_descriptor
|
||||
except AttributeError:
|
||||
return
|
||||
|
||||
ssds = []
|
||||
required = []
|
||||
optional = []
|
||||
#print "..... %s ..... " % entity_descriptor.entity_id
|
||||
for tssd in ssd:
|
||||
# Only want to talk to SAML 2.0 entities
|
||||
if samlp.NAMESPACE not in \
|
||||
tssd.protocol_support_enumeration.split(" "):
|
||||
#print "<<<", idp.protocol_support_enumeration
|
||||
continue
|
||||
|
||||
ssds.append(tssd)
|
||||
certs = self._certs(tssd.key_descriptor, "pem")
|
||||
self._add_certs(entity_descr.entity_id, certs)
|
||||
dp = self._role(dp, entity_descr)
|
||||
|
||||
self._extensions(tssd)
|
||||
if dp._certs:
|
||||
for acs in dp.assertion_consumer_service:
|
||||
self._add_certs(acs.location, dp._certs)
|
||||
|
||||
for acs in tssd.attribute_consuming_service:
|
||||
for attr in acs.requested_attribute:
|
||||
#print "==", attr
|
||||
if attr.is_required == "true":
|
||||
required.append(attr)
|
||||
else:
|
||||
optional.append(attr)
|
||||
|
||||
for acs in tssd.assertion_consumer_service:
|
||||
self._add_certs(acs.location, certs)
|
||||
return dp
|
||||
|
||||
|
||||
if required or optional:
|
||||
#print "REQ",required
|
||||
#print "OPT",optional
|
||||
self._wants[entity_descr.entity_id] = (ava_fro(self.attrconv,
|
||||
required),
|
||||
ava_fro(self.attrconv,
|
||||
optional))
|
||||
|
||||
if ssds:
|
||||
entity[tag] = ssds
|
||||
|
||||
def _idp_metadata(self, entity_descr, entity, tag):
|
||||
def _idpsso(self, dp, entity_descr):
|
||||
"""
|
||||
Pick out the IdP SSO descriptors from an entity
|
||||
descriptor and store the information in a way which is easily
|
||||
@@ -231,32 +194,17 @@ class MetaData(object):
|
||||
|
||||
:param entity_descr: A EntityDescriptor instance
|
||||
"""
|
||||
try:
|
||||
isd = entity_descr.idpsso_descriptor
|
||||
except AttributeError:
|
||||
return
|
||||
|
||||
idps = []
|
||||
for tidp in isd:
|
||||
if samlp.NAMESPACE not in \
|
||||
tidp.protocol_support_enumeration.split(" "):
|
||||
#print "<<<", idp.protocol_support_enumeration
|
||||
continue
|
||||
|
||||
idps.append(tidp)
|
||||
|
||||
certs = self._certs(tidp.key_descriptor, "pem")
|
||||
dp = self._role(dp, entity_descr)
|
||||
if dp._certs:
|
||||
for sso in dp.single_sign_on_service:
|
||||
self._add_certs(sso.location, dp._certs)
|
||||
|
||||
self._add_certs(entity_descr.entity_id, certs)
|
||||
for sso in tidp.single_sign_on_service:
|
||||
self._add_certs(sso.location, certs)
|
||||
self._extensions(dp)
|
||||
|
||||
self._extensions(tidp)
|
||||
return dp
|
||||
|
||||
if idps:
|
||||
entity[tag] = idps
|
||||
|
||||
def _aad_metadata(self, entity_descr, entity, tag):
|
||||
def _attribute_authority(self, dp, entity_descr):
|
||||
"""
|
||||
Pick out the attribute authority descriptors from an entity
|
||||
descriptor and store the information in a way which is easily
|
||||
@@ -264,105 +212,107 @@ class MetaData(object):
|
||||
|
||||
:param entity_descr: A EntityDescriptor instance
|
||||
"""
|
||||
try:
|
||||
attr_auth_descr = entity_descr.attribute_authority_descriptor
|
||||
except AttributeError:
|
||||
#print "No Attribute AD: %s" % entity_descr.entity_id
|
||||
return
|
||||
|
||||
aads = []
|
||||
for taad in attr_auth_descr:
|
||||
# Remove everyone that doesn't talk SAML 2.0
|
||||
#print "supported protocols", taad.protocol_support_enumeration
|
||||
if samlp.NAMESPACE not in \
|
||||
taad.protocol_support_enumeration.split(" "):
|
||||
continue
|
||||
|
||||
# remove the bindings I can't handle
|
||||
aserv = []
|
||||
for attr_serv in taad.attribute_service:
|
||||
#print "binding", attr_serv.binding
|
||||
if attr_serv.binding == BINDING_SOAP:
|
||||
aserv.append(attr_serv)
|
||||
|
||||
if not aserv:
|
||||
continue
|
||||
|
||||
taad.attribute_service = aserv
|
||||
self._extensions(taad)
|
||||
|
||||
# gather all the certs and place them in temporary files
|
||||
certs = self._certs(taad.key_descriptor, "pem")
|
||||
self._add_certs(entity_descr.entity_id, certs)
|
||||
# remove the bindings I can't handle
|
||||
aserv = []
|
||||
for attr_serv in dp.attribute_service:
|
||||
#print "binding", attr_serv.binding
|
||||
if attr_serv.binding == BINDING_SOAP:
|
||||
aserv.append(attr_serv)
|
||||
|
||||
for sso in taad.attribute_service:
|
||||
self._add_certs(sso.location, certs)
|
||||
|
||||
aads.append(taad)
|
||||
|
||||
if aads:
|
||||
entity[tag] = aads
|
||||
|
||||
def _pdp_metadata(self, entity_descr, entity, tag):
|
||||
if not aserv:
|
||||
return None
|
||||
|
||||
dp.attribute_service = aserv
|
||||
dp = self._role(dp, entity_descr)
|
||||
|
||||
if dp._certs:
|
||||
for attr_serv in dp.attribute_service:
|
||||
self._add_certs(attr_serv.location, dp._certs)
|
||||
|
||||
return dp
|
||||
|
||||
def _pdp(self, dp, entity_descr):
|
||||
aserv = []
|
||||
for authz_serv in dp.authz_service:
|
||||
#print "binding", attr_serv.binding
|
||||
if authz_serv.binding == BINDING_SOAP:
|
||||
aserv.append(authz_serv)
|
||||
|
||||
if not aserv:
|
||||
return None
|
||||
|
||||
dp.authz_service = aserv
|
||||
dp = self._role(dp, entity_descr)
|
||||
|
||||
if dp._certs:
|
||||
for aus in dp.authz_service:
|
||||
self._add_certs(aus.location, dp._certs)
|
||||
|
||||
return dp
|
||||
|
||||
def _authn_authority(self, dp, entity_descr):
|
||||
"""
|
||||
Pick out the PDP descriptors from an entity
|
||||
AuthnAuthorityDescriptor
|
||||
:return:
|
||||
"""
|
||||
|
||||
return self._role(dp, entity_descr)
|
||||
|
||||
|
||||
def _role(self, dp, entity_descr):
|
||||
"""
|
||||
RoleDescriptor
|
||||
:return:
|
||||
"""
|
||||
self._extensions(dp)
|
||||
|
||||
# gather all the certs and place them in temporary files
|
||||
dp._certs = self._certs(dp.key_descriptor, "pem")
|
||||
self._add_certs(entity_descr.entity_id, dp._certs)
|
||||
|
||||
return dp
|
||||
|
||||
def _roledescriptor(self, entity_descr, entity, tag, descriptor, func):
|
||||
"""
|
||||
Pick out a specific descriptor from an entity
|
||||
descriptor and store the information in a way which is easily
|
||||
accessible.
|
||||
|
||||
*authz_service=None,
|
||||
assertion_id_request_service=None,
|
||||
name_id_format=None,
|
||||
signature=None,
|
||||
extensions=None,
|
||||
key_descriptor=None,
|
||||
organization=None,
|
||||
contact_person=None,
|
||||
id=None,
|
||||
valid_until=None,
|
||||
cache_duration=None,
|
||||
*protocol_support_enumeration=None,
|
||||
error_url=None,
|
||||
|
||||
:param entity_descr: A EntityDescriptor instance
|
||||
:param entity: The whole entity
|
||||
:param tag: which tag to store the information under
|
||||
:param descriptor: The descriptor type
|
||||
:param func: A processing function specific for the descriptor type
|
||||
"""
|
||||
try:
|
||||
pdp_descr = entity_descr.pdp_descriptor
|
||||
_descr = getattr(entity_descr, descriptor)
|
||||
except AttributeError:
|
||||
#print "No Attribute AD: %s" % entity_descr.entity_id
|
||||
return
|
||||
|
||||
pdps = []
|
||||
for pdp in pdp_descr:
|
||||
dps = []
|
||||
if isinstance(_descr, list):
|
||||
for dp in _descr:
|
||||
# Remove everyone that doesn't talk SAML 2.0
|
||||
if samlp.NAMESPACE not in \
|
||||
dp.protocol_support_enumeration.split(" "):
|
||||
continue
|
||||
|
||||
dp = func(dp, entity_descr)
|
||||
if dp:
|
||||
dps.append(dp)
|
||||
elif _descr:
|
||||
dp = _descr
|
||||
# Remove everyone that doesn't talk SAML 2.0
|
||||
#print "supported protocols", taad.protocol_support_enumeration
|
||||
if samlp.NAMESPACE not in \
|
||||
pdp.protocol_support_enumeration.split(" "):
|
||||
continue
|
||||
if samlp.NAMESPACE in dp.protocol_support_enumeration.split(" "):
|
||||
dp = func(dp, entity_descr)
|
||||
if dp:
|
||||
dps.append(dp)
|
||||
|
||||
# remove the bindings I can't handle
|
||||
aserv = []
|
||||
for authz_serv in pdp.authz_service:
|
||||
#print "binding", attr_serv.binding
|
||||
if authz_serv.binding == BINDING_SOAP:
|
||||
aserv.append(authz_serv)
|
||||
if dps:
|
||||
entity[tag] = dps
|
||||
|
||||
if not aserv:
|
||||
continue
|
||||
|
||||
pdp.authz_service = aserv
|
||||
self._extensions(pdp)
|
||||
|
||||
# gather all the certs and place them in temporary files
|
||||
certs = self._certs(pdp.key_descriptor, "pem")
|
||||
self._add_certs(entity_descr.entity_id, certs)
|
||||
|
||||
for aus in pdp.authz_service:
|
||||
self._add_certs(aus.location, certs)
|
||||
|
||||
pdps.append(pdp)
|
||||
|
||||
if pdps:
|
||||
entity[tag] = pdps
|
||||
|
||||
def clear_from_source(self, source):
|
||||
""" Remove all the metadata references I have gotten from this source
|
||||
@@ -419,13 +369,16 @@ class MetaData(object):
|
||||
entity["valid_until"] = valid_until
|
||||
elif entity_descr.valid_until:
|
||||
entity["valid_until"] = entity_descr.valid_until
|
||||
|
||||
self._idp_metadata(entity_descr, entity, "idp_sso")
|
||||
self._sp_metadata(entity_descr, entity, "sp_sso")
|
||||
self._aad_metadata(entity_descr, entity,
|
||||
"attribute_authority")
|
||||
self._vo_metadata(entity_descr, entity, "affiliation")
|
||||
self._pdp_metadata(entity_descr, entity, "pdp")
|
||||
|
||||
# go through the different types of descriptors
|
||||
for descr in ["idpsso", "attribute_authority", "authn_authority",
|
||||
"pdp", "role", "spsso"]:
|
||||
func = getattr(self, "_%s" % descr)
|
||||
self._roledescriptor(entity_descr, entity, descr,
|
||||
"%s_descriptor" % descr, func)
|
||||
|
||||
self._affiliation(entity_descr, entity, "affiliation")
|
||||
|
||||
try:
|
||||
entity["organization"] = entity_descr.organization
|
||||
except AttributeError:
|
||||
@@ -492,7 +445,7 @@ class MetaData(object):
|
||||
@keep_updated
|
||||
def idp_services(self, entity_id, typ, binding=None):
|
||||
""" depreceated """
|
||||
idps = self.entity[entity_id]["idp_sso"]
|
||||
idps = self.entity[entity_id]["idpsso"]
|
||||
|
||||
loc = {}
|
||||
for idp in idps: # None or one
|
||||
@@ -504,7 +457,7 @@ class MetaData(object):
|
||||
@keep_updated
|
||||
def sp_services(self, entity_id, typ, binding=None):
|
||||
""" deprecated """
|
||||
sps = self.entity[entity_id]["sp_sso"]
|
||||
sps = self.entity[entity_id]["spsso"]
|
||||
|
||||
loc = {}
|
||||
for sep in sps: # None or one
|
||||
@@ -527,7 +480,7 @@ class MetaData(object):
|
||||
|
||||
loc = []
|
||||
try:
|
||||
idps = self.entity[entity_id]["idp_sso"]
|
||||
idps = self.entity[entity_id]["idpsso"]
|
||||
except KeyError:
|
||||
return loc
|
||||
|
||||
@@ -554,7 +507,7 @@ class MetaData(object):
|
||||
|
||||
loc = []
|
||||
try:
|
||||
idps = self.entity[entity_id]["idp_sso"]
|
||||
idps = self.entity[entity_id]["idpsso"]
|
||||
except KeyError:
|
||||
return loc
|
||||
|
||||
@@ -590,7 +543,7 @@ class MetaData(object):
|
||||
loc = []
|
||||
|
||||
try:
|
||||
sss = self.entity[entity_id]["%s_sso" % typ]
|
||||
sss = self.entity[entity_id]["%ssso" % typ]
|
||||
except KeyError:
|
||||
return loc
|
||||
|
||||
@@ -663,7 +616,7 @@ class MetaData(object):
|
||||
@keep_updated
|
||||
def consumer_url(self, entity_id, binding=BINDING_HTTP_POST, _log=None):
|
||||
try:
|
||||
ssos = self.entity[entity_id]["sp_sso"]
|
||||
ssos = self.entity[entity_id]["spsso"]
|
||||
except KeyError:
|
||||
raise
|
||||
|
||||
@@ -683,7 +636,7 @@ class MetaData(object):
|
||||
@keep_updated
|
||||
def assertion_consumer_services(self, entity_id, binding=BINDING_HTTP_POST):
|
||||
try:
|
||||
ssos = self.entity[entity_id]["sp_sso"]
|
||||
ssos = self.entity[entity_id]["spsso"]
|
||||
except KeyError:
|
||||
raise
|
||||
|
||||
@@ -728,32 +681,40 @@ class MetaData(object):
|
||||
|
||||
return name
|
||||
|
||||
def req_opt(self, acs):
|
||||
req = []
|
||||
opt = []
|
||||
for attr in acs.requested_attribute:
|
||||
if attr.is_required == "true":
|
||||
req.append(attr)
|
||||
else:
|
||||
opt.append(attr)
|
||||
|
||||
return req, opt
|
||||
|
||||
@keep_updated
|
||||
def wants(self, entity_id):
|
||||
#def attribute_consumer(self, entity_id, index=None):
|
||||
def attribute_requirement(self, entity_id, index=None):
|
||||
try:
|
||||
return self._wants[entity_id]
|
||||
ssos = self.entity[entity_id]["spsso"]
|
||||
except KeyError:
|
||||
return [], []
|
||||
|
||||
@keep_updated
|
||||
def attribute_consumer(self, entity_id):
|
||||
try:
|
||||
ssos = self.entity[entity_id]["sp_sso"]
|
||||
except KeyError:
|
||||
return [], []
|
||||
|
||||
required = []
|
||||
optional = []
|
||||
# What if there is more than one ? Can't be ?
|
||||
for acs in ssos[0].attribute_consuming_service:
|
||||
for attr in acs.requested_attribute:
|
||||
if attr.is_required == "true":
|
||||
required.append(attr)
|
||||
else:
|
||||
optional.append(attr)
|
||||
|
||||
return required, optional
|
||||
|
||||
return {}, {}
|
||||
|
||||
acss = ssos[0].attribute_consuming_service
|
||||
if acss is None or acss == []:
|
||||
return {}, {}
|
||||
elif len(acss) == 1:
|
||||
return self.req_opt(acss[0])
|
||||
else:
|
||||
if index is None:
|
||||
for acs in acss:
|
||||
if acs.default:
|
||||
return self.req_opt(acs)
|
||||
# if I get here NO default was found, pick the first ?
|
||||
return self.req_opt(acss[0])
|
||||
else:
|
||||
return self.req_opt(acss[index])
|
||||
|
||||
def _orgname(self, org, langs=None):
|
||||
if not org:
|
||||
return ""
|
||||
@@ -792,20 +753,20 @@ class MetaData(object):
|
||||
langs = ["en"]
|
||||
|
||||
for entity_id, edict in self.entity.items():
|
||||
if "idp_sso" in edict:
|
||||
if "idpsso" in edict:
|
||||
#idp_aa_check self._valid(entity_id)
|
||||
name = None
|
||||
if "organization" in edict:
|
||||
name = self._orgname(edict["organization"], langs)
|
||||
|
||||
if not name:
|
||||
name = self._location(edict["idp_sso"])[0]
|
||||
idps[entity_id] = (name, edict["idp_sso"])
|
||||
name = self._location(edict["idpsso"])[0]
|
||||
idps[entity_id] = (name, edict["idpsso"])
|
||||
return idps
|
||||
|
||||
#noinspection PyUnusedLocal
|
||||
@keep_updated
|
||||
def ui_info(self, entity_id, service="idp_sso"):
|
||||
def ui_info(self, entity_id, service="idpsso"):
|
||||
inst = self.entity[entity_id][service]
|
||||
|
||||
def export_discojuice_json(self, lang=None):
|
||||
@@ -826,7 +787,7 @@ class MetaData(object):
|
||||
result = []
|
||||
for entity_id, entity in self.entity.items():
|
||||
try:
|
||||
for _sso in entity['idp_sso']:
|
||||
for _sso in entity['idpsso']:
|
||||
rdict = {'entityID': entity_id,
|
||||
'title': self._orgname(entity['organization'], lang)}
|
||||
|
||||
@@ -996,12 +957,7 @@ def do_requested_attribute(attributes, acs, is_required="false"):
|
||||
lista.append(md.RequestedAttribute(**args))
|
||||
return lista
|
||||
|
||||
def do_uiinfo(conf):
|
||||
try:
|
||||
_uiinfo = conf.ui_info
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
def do_uiinfo(_uiinfo):
|
||||
uii = mdui.UIInfo()
|
||||
for attr in ['display_name', 'description', "information_url",
|
||||
'privacy_statement_url']:
|
||||
@@ -1157,13 +1113,14 @@ DEFAULT = {
|
||||
"want_authn_requests_signed": "false",
|
||||
}
|
||||
|
||||
def do_sp_sso_descriptor(conf, cert=None):
|
||||
def do_spsso_descriptor(conf, cert=None):
|
||||
spsso = md.SPSSODescriptor()
|
||||
spsso.protocol_support_enumeration = samlp.NAMESPACE
|
||||
|
||||
if conf.endpoints:
|
||||
for (endpoint, instlist) in do_endpoints(conf.endpoints,
|
||||
ENDPOINTS["sp"]).items():
|
||||
endps = conf.getattr("endpoints", "sp")
|
||||
if endps:
|
||||
for (endpoint, instlist) in do_endpoints(endps,
|
||||
ENDPOINTS["sp"]).items():
|
||||
setattr(spsso, endpoint, instlist)
|
||||
|
||||
if cert:
|
||||
@@ -1171,7 +1128,7 @@ def do_sp_sso_descriptor(conf, cert=None):
|
||||
|
||||
for key in ["want_assertions_signed", "authn_requests_signed"]:
|
||||
try:
|
||||
val = getattr(conf, key)
|
||||
val = conf.getattr(key, "sp")
|
||||
if val is None:
|
||||
setattr(spsso, key, DEFAULT[key]) #default ?!
|
||||
else:
|
||||
@@ -1181,16 +1138,15 @@ def do_sp_sso_descriptor(conf, cert=None):
|
||||
setattr(spsso, key, DEFAULTS[key])
|
||||
|
||||
requested_attributes = []
|
||||
if conf.required_attributes:
|
||||
requested_attributes.extend(do_requested_attribute(
|
||||
conf.required_attributes,
|
||||
conf.attribute_converters,
|
||||
is_required="true"))
|
||||
acs = conf.attribute_converters
|
||||
req = conf.getattr("required_attributes", "sp")
|
||||
if req:
|
||||
requested_attributes.extend(do_requested_attribute(req, acs,
|
||||
is_required="true"))
|
||||
|
||||
if conf.optional_attributes:
|
||||
requested_attributes.extend(do_requested_attribute(
|
||||
conf.optional_attributes,
|
||||
conf.attribute_converters))
|
||||
opt=conf.getattr("optional_attributes", "sp")
|
||||
if opt:
|
||||
requested_attributes.extend(do_requested_attribute(opt, acs))
|
||||
|
||||
if requested_attributes:
|
||||
spsso.attribute_consuming_service = [md.AttributeConsumingService(
|
||||
@@ -1211,43 +1167,47 @@ def do_sp_sso_descriptor(conf, cert=None):
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
if conf.discovery_response:
|
||||
dresp = conf.getattr("discovery_response", "sp")
|
||||
if dresp:
|
||||
if spsso.extensions is None:
|
||||
spsso.extensions = md.Extensions()
|
||||
spsso.extensions.add_extension_element(do_idpdisc(conf.discovery_response))
|
||||
spsso.extensions.add_extension_element(do_idpdisc(dresp))
|
||||
|
||||
return spsso
|
||||
|
||||
def do_idp_sso_descriptor(conf, cert=None):
|
||||
def do_idpsso_descriptor(conf, cert=None):
|
||||
idpsso = md.IDPSSODescriptor()
|
||||
idpsso.protocol_support_enumeration = samlp.NAMESPACE
|
||||
|
||||
if conf.endpoints:
|
||||
for (endpoint, instlist) in do_endpoints(conf.endpoints,
|
||||
ENDPOINTS["idp"]).items():
|
||||
endps = conf.getattr("endpoints", "idp")
|
||||
if endps:
|
||||
for (endpoint, instlist) in do_endpoints(endps,
|
||||
ENDPOINTS["idp"]).items():
|
||||
setattr(idpsso, endpoint, instlist)
|
||||
|
||||
if conf.scope:
|
||||
scopes = conf.getattr("scope", "idp")
|
||||
if scopes:
|
||||
if idpsso.extensions is None:
|
||||
idpsso.extensions = md.Extensions()
|
||||
for scope in conf.scope:
|
||||
for scope in scopes:
|
||||
mdscope = shibmd.Scope()
|
||||
mdscope.text = scope
|
||||
# unless scope contains '*'/'+'/'?' assume non regexp ?
|
||||
mdscope.regexp = "false"
|
||||
idpsso.extensions.add_extension_element(mdscope)
|
||||
|
||||
if conf.ui_info:
|
||||
ui_info = conf.getattr("ui_info", "idp")
|
||||
if ui_info:
|
||||
if idpsso.extensions is None:
|
||||
idpsso.extensions = md.Extensions()
|
||||
idpsso.extensions.add_extension_element(do_uiinfo(conf))
|
||||
idpsso.extensions.add_extension_element(do_uiinfo(ui_info))
|
||||
|
||||
if cert:
|
||||
idpsso.key_descriptor = do_key_descriptor(cert)
|
||||
|
||||
for key in ["want_authn_requests_signed"]:
|
||||
try:
|
||||
val = getattr(conf,key)
|
||||
val = conf.getattr(key, "idp")
|
||||
if val is None:
|
||||
setattr(idpsso, key, DEFAULT["want_authn_requests_signed"])
|
||||
else:
|
||||
@@ -1294,41 +1254,31 @@ def do_pdp_descriptor(conf, cert):
|
||||
|
||||
return pdp
|
||||
|
||||
def entity_descriptor(confd, valid_for):
|
||||
def entity_descriptor(confd):
|
||||
mycert = "".join(open(confd.cert_file).readlines()[1:-1])
|
||||
|
||||
# if "attribute_map_dir" in confd:
|
||||
# attrconverters = ac_factory(confd.attribute_map_dir)
|
||||
# else:
|
||||
# attrconverters = [AttributeConverter()]
|
||||
|
||||
#if "attribute_maps" in confd:
|
||||
# (forward,backward) = parse_attribute_map(confd["attribute_maps"])
|
||||
#else:
|
||||
# backward = {}
|
||||
|
||||
entd = md.EntityDescriptor()
|
||||
entd.entity_id = confd.entityid
|
||||
|
||||
if valid_for:
|
||||
entd.valid_until = in_a_while(hours=valid_for)
|
||||
if confd.valid_for:
|
||||
entd.valid_until = in_a_while(hours=int(confd.valid_for))
|
||||
|
||||
if confd.organization is not None:
|
||||
entd.organization = do_organization_info(confd.organization)
|
||||
if confd.contact_person is not None:
|
||||
entd.contact_person = do_contact_person_info(confd.contact_person)
|
||||
|
||||
serves = confd.serves()
|
||||
serves = confd.serves
|
||||
if not serves:
|
||||
raise Exception(
|
||||
'No service type ("sp","idp","aa") provided in the configuration')
|
||||
|
||||
if "sp" in serves:
|
||||
confd.context = "sp"
|
||||
entd.spsso_descriptor = do_sp_sso_descriptor(confd, mycert)
|
||||
entd.spsso_descriptor = do_spsso_descriptor(confd, mycert)
|
||||
if "idp" in serves:
|
||||
confd.context = "idp"
|
||||
entd.idpsso_descriptor = do_idp_sso_descriptor(confd, mycert)
|
||||
entd.idpsso_descriptor = do_idpsso_descriptor(confd, mycert)
|
||||
if "aa" in serves:
|
||||
confd.context = "aa"
|
||||
entd.attribute_authority_descriptor = do_aa_descriptor(confd, mycert)
|
||||
@@ -1366,10 +1316,7 @@ def entities_descriptor(eds, valid_for, name, ident, sign, secc):
|
||||
entities = md.entities_descriptor_from_string(xmldoc)
|
||||
return entities
|
||||
|
||||
def sign_entity_descriptor(edesc, valid_for, ident, secc):
|
||||
if valid_for:
|
||||
edesc.valid_until = in_a_while(hours=valid_for)
|
||||
|
||||
def sign_entity_descriptor(edesc, ident, secc):
|
||||
if not ident:
|
||||
ident = sid()
|
||||
|
||||
|
@@ -1,5 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
import logging
|
||||
import random
|
||||
import string
|
||||
|
||||
import time
|
||||
import base64
|
||||
@@ -101,10 +103,21 @@ def deflate_and_base64_encode( string_val ):
|
||||
:return: The deflated and encoded string
|
||||
"""
|
||||
return base64.b64encode( zlib.compress( string_val )[2:-4] )
|
||||
|
||||
|
||||
def rndstr(size=16):
|
||||
"""
|
||||
Returns a string of random ascii characters or digits
|
||||
|
||||
:param size: The length of the string
|
||||
:return: string
|
||||
"""
|
||||
_basech = string.ascii_letters + string.digits
|
||||
return "".join([random.choice(_basech) for _ in range(size)])
|
||||
|
||||
def sid(seed=""):
|
||||
"""The hash of the server time + seed makes an unique SID for each session.
|
||||
|
||||
128-bits long so it fulfills the SAML2 requirements which states 128-160 bits
|
||||
|
||||
:param seed: A seed string
|
||||
:return: The hex version of the digest, prefixed by 'id-' to make it
|
||||
compliant with the NCName specification
|
||||
|
@@ -366,7 +366,8 @@ class AssertionIDRequestType_(RequestAbstractType_):
|
||||
c_attributes = RequestAbstractType_.c_attributes.copy()
|
||||
c_child_order = RequestAbstractType_.c_child_order[:]
|
||||
c_cardinality = RequestAbstractType_.c_cardinality.copy()
|
||||
c_children['{urn:oasis:names:tc:SAML:2.0:assertion}AssertionIDRef'] = ('assertion_id_ref', [saml.AssertionIDRef])
|
||||
c_children['{urn:oasis:names:tc:SAML:2.0:assertion}AssertionIDRef'] = (
|
||||
'assertion_id_ref', [saml.AssertionIDRef])
|
||||
c_cardinality['assertion_id_ref'] = {"min":1}
|
||||
c_child_order.extend(['assertion_id_ref'])
|
||||
|
||||
|
@@ -258,7 +258,7 @@ class Server(object):
|
||||
try:
|
||||
# subject information is stored in a database
|
||||
# default database is a shelve database which is OK in some setups
|
||||
dbspec = self.conf.subject_data
|
||||
dbspec = self.conf.getattr("subject_data", "idp")
|
||||
idb = None
|
||||
if isinstance(dbspec, basestring):
|
||||
idb = shelve.open(dbspec, writeback=True)
|
||||
@@ -376,14 +376,15 @@ class Server(object):
|
||||
|
||||
return response
|
||||
|
||||
def wants(self, sp_entity_id):
|
||||
""" Returns what attributes the SP requiers and which are optional
|
||||
def wants(self, sp_entity_id, index=None):
|
||||
""" Returns what attributes the SP requires and which are optional
|
||||
if any such demands are registered in the Metadata.
|
||||
|
||||
:param sp_entity_id: The entity id of the SP
|
||||
:param index: which of the attribute consumer services its all about
|
||||
:return: 2-tuple, list of required and list of optional attributes
|
||||
"""
|
||||
return self.metadata.requests(sp_entity_id)
|
||||
return self.metadata.attribute_requirement(sp_entity_id, index)
|
||||
|
||||
def parse_attribute_query(self, xml_string, decode=True):
|
||||
""" Parse an attribute query
|
||||
@@ -410,28 +411,20 @@ class Server(object):
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
|
||||
def _response(self, in_response_to, consumer_url=None, sp_entity_id=None,
|
||||
identity=None, name_id=None, status=None, sign=False,
|
||||
policy=Policy(), authn=None, authn_decl=None, issuer=None):
|
||||
def _response(self, in_response_to, consumer_url=None, status=None,
|
||||
issuer=None, sign=False, to_sign=None,
|
||||
**kwargs):
|
||||
""" Create a Response that adhers to the ??? profile.
|
||||
|
||||
:param in_response_to: The session identifier of the request
|
||||
:param consumer_url: The URL which should receive the response
|
||||
:param sp_entity_id: The entity identifier of the SP
|
||||
:param identity: A dictionary with attributes and values that are
|
||||
expected to be the bases for the assertion in the response.
|
||||
:param name_id: The identifier of the subject
|
||||
:param status: The status of the response
|
||||
:param sign: Whether the assertion should be signed or not
|
||||
:param policy: The attribute release policy for this instance
|
||||
:param authn: A 2-tuple denoting the authn class and the authn
|
||||
authority
|
||||
:param authn_decl:
|
||||
:param issuer: The issuer of the response
|
||||
:param sign: Whether the response should be signed or not
|
||||
:param to_sign: What other parts to sign
|
||||
:param kwargs: Extra key word arguments
|
||||
:return: A Response instance
|
||||
"""
|
||||
|
||||
to_sign = []
|
||||
|
||||
if not status:
|
||||
status = success_status_factory()
|
||||
@@ -447,57 +440,25 @@ class Server(object):
|
||||
if consumer_url:
|
||||
response.destination = consumer_url
|
||||
|
||||
if identity:
|
||||
ast = Assertion(identity)
|
||||
for key, val in kwargs.items():
|
||||
setattr(response, key, val)
|
||||
|
||||
if sign:
|
||||
try:
|
||||
ast.apply_policy(sp_entity_id, policy, self.metadata)
|
||||
except MissingValue, exc:
|
||||
return self.error_response(in_response_to, consumer_url,
|
||||
sp_entity_id, exc, name_id)
|
||||
to_sign.append((class_name(response), response.id))
|
||||
except AttributeError:
|
||||
to_sign = [(class_name(response), response.id)]
|
||||
|
||||
if authn: # expected to be a 2-tuple class+authority
|
||||
(authn_class, authn_authn) = authn
|
||||
assertion = ast.construct(sp_entity_id, in_response_to,
|
||||
consumer_url, name_id,
|
||||
self.conf.attribute_converters,
|
||||
policy, issuer=_issuer,
|
||||
authn_class=authn_class,
|
||||
authn_auth=authn_authn)
|
||||
elif authn_decl:
|
||||
assertion = ast.construct(sp_entity_id, in_response_to,
|
||||
consumer_url, name_id,
|
||||
self.conf.attribute_converters,
|
||||
policy, issuer=_issuer,
|
||||
authn_decl=authn_decl)
|
||||
else:
|
||||
assertion = ast.construct(sp_entity_id, in_response_to,
|
||||
consumer_url, name_id,
|
||||
self.conf.attribute_converters,
|
||||
policy, issuer=_issuer)
|
||||
|
||||
if sign:
|
||||
assertion.signature = pre_signature_part(assertion.id,
|
||||
self.sec.my_cert, 1)
|
||||
# Just the assertion or the response and the assertion ?
|
||||
to_sign = [(class_name(assertion), assertion.id)]
|
||||
|
||||
# Store which assertion that has been sent to which SP about which
|
||||
# subject.
|
||||
|
||||
# self.cache.set(assertion.subject.name_id.text,
|
||||
# sp_entity_id, {"ava": identity, "authn": authn},
|
||||
# assertion.conditions.not_on_or_after)
|
||||
|
||||
response.assertion = assertion
|
||||
|
||||
return signed_instance_factory(response, self.sec, to_sign)
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
|
||||
def do_response(self, in_response_to, consumer_url,
|
||||
sp_entity_id, identity=None, name_id=None,
|
||||
status=None, sign=False, authn=None, authn_decl=None,
|
||||
issuer=None):
|
||||
def create_response(self, in_response_to, consumer_url,
|
||||
sp_entity_id, identity=None, name_id=None,
|
||||
status=None, authn=None,
|
||||
authn_decl=None, issuer=None, policy=None,
|
||||
sign_assertion=False, sign_response=False):
|
||||
""" Create a response. A layer of indirection.
|
||||
|
||||
:param in_response_to: The session identifier of the request
|
||||
@@ -507,54 +468,92 @@ class Server(object):
|
||||
expected to be the bases for the assertion in the response.
|
||||
:param name_id: The identifier of the subject
|
||||
:param status: The status of the response
|
||||
:param sign: Whether the assertion should be signed or not
|
||||
:param authn: A 2-tuple denoting the authn class and the authn
|
||||
authority.
|
||||
:param authn_decl:
|
||||
:param issuer: The issuer of the response
|
||||
:return: A Response instance.
|
||||
:param sign_assertion: Whether the assertion should be signed or not
|
||||
:param sign_response: Whether the response should be signed or not
|
||||
:return: A response instance
|
||||
"""
|
||||
|
||||
policy = self.conf.policy
|
||||
to_sign = []
|
||||
args = {}
|
||||
if identity:
|
||||
_issuer = self.issuer(issuer)
|
||||
ast = Assertion(identity)
|
||||
if policy is None:
|
||||
policy = Policy()
|
||||
try:
|
||||
ast.apply_policy(sp_entity_id, policy, self.metadata)
|
||||
except MissingValue, exc:
|
||||
return self.create_error_response(in_response_to, consumer_url,
|
||||
exc, sign_response)
|
||||
|
||||
return self._response(in_response_to, consumer_url,
|
||||
sp_entity_id, identity, name_id,
|
||||
status, sign, policy, authn, authn_decl, issuer)
|
||||
if authn: # expected to be a 2-tuple class+authority
|
||||
(authn_class, authn_authn) = authn
|
||||
assertion = ast.construct(sp_entity_id, in_response_to,
|
||||
consumer_url, name_id,
|
||||
self.conf.attribute_converters,
|
||||
policy, issuer=_issuer,
|
||||
authn_class=authn_class,
|
||||
authn_auth=authn_authn)
|
||||
elif authn_decl:
|
||||
assertion = ast.construct(sp_entity_id, in_response_to,
|
||||
consumer_url, name_id,
|
||||
self.conf.attribute_converters,
|
||||
policy, issuer=_issuer,
|
||||
authn_decl=authn_decl)
|
||||
else:
|
||||
assertion = ast.construct(sp_entity_id, in_response_to,
|
||||
consumer_url, name_id,
|
||||
self.conf.attribute_converters,
|
||||
policy, issuer=_issuer)
|
||||
|
||||
if sign_assertion:
|
||||
assertion.signature = pre_signature_part(assertion.id,
|
||||
self.sec.my_cert, 1)
|
||||
# Just the assertion or the response and the assertion ?
|
||||
to_sign = [(class_name(assertion), assertion.id)]
|
||||
|
||||
# Store which assertion that has been sent to which SP about which
|
||||
# subject.
|
||||
|
||||
# self.cache.set(assertion.subject.name_id.text,
|
||||
# sp_entity_id, {"ava": identity, "authn": authn},
|
||||
# assertion.conditions.not_on_or_after)
|
||||
|
||||
args["assertion"] = assertion
|
||||
|
||||
return self._response(in_response_to, consumer_url, status, issuer,
|
||||
sign_response, to_sign, **args)
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
|
||||
def error_response(self, in_response_to, destination, spid, info,
|
||||
name_id=None, sign=False, issuer=None):
|
||||
def create_error_response(self, in_response_to, destination, info,
|
||||
sign=False, issuer=None):
|
||||
""" Create a error response.
|
||||
|
||||
:param in_response_to: The identifier of the message this is a response
|
||||
to.
|
||||
:param destination: The intended recipient of this message
|
||||
:param spid: The entitiy ID of the SP that will get this.
|
||||
:param destination: The intended recipient of this message
|
||||
:param info: Either an Exception instance or a 2-tuple consisting of
|
||||
error code and descriptive text
|
||||
:param name_id:
|
||||
:param sign: Whether the message should be signed or not
|
||||
:param sign: Whether the response should be signed or not
|
||||
:param issuer: The issuer of the response
|
||||
:return: A Response instance
|
||||
:return: A response instance
|
||||
"""
|
||||
status = error_status_factory(info)
|
||||
|
||||
return self._response(
|
||||
in_response_to, # in_response_to
|
||||
destination, # consumer_url
|
||||
spid, # sp_entity_id
|
||||
name_id=name_id,
|
||||
status=status,
|
||||
sign=sign,
|
||||
issuer=issuer
|
||||
)
|
||||
|
||||
return self._response(in_response_to, destination, status, issuer,
|
||||
sign)
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
#noinspection PyUnusedLocal
|
||||
def do_aa_response(self, in_response_to, consumer_url, sp_entity_id,
|
||||
identity=None, userid="", name_id=None, status=None,
|
||||
sign=False, _name_id_policy=None, issuer=None):
|
||||
def create_aa_response(self, in_response_to, consumer_url, sp_entity_id,
|
||||
identity=None, userid="", name_id=None, status=None,
|
||||
issuer=None, sign_assertion=False,
|
||||
sign_response=False):
|
||||
""" Create an attribute assertion response.
|
||||
|
||||
:param in_response_to: The session identifier of the request
|
||||
@@ -565,24 +564,27 @@ class Server(object):
|
||||
:param userid: A identifier of the user
|
||||
:param name_id: The identifier of the subject
|
||||
:param status: The status of the response
|
||||
:param sign: Whether the assertion should be signed or not
|
||||
:param _name_id_policy: Policy for NameID creation.
|
||||
:param issuer: The issuer of the response
|
||||
:return: A Response instance.
|
||||
:param sign_assertion: Whether the assertion should be signed or not
|
||||
:param sign_response: Whether the whole response should be signed
|
||||
:return: A response instance
|
||||
"""
|
||||
# name_id = self.ident.construct_nameid(self.conf.policy, userid,
|
||||
# sp_entity_id, identity)
|
||||
|
||||
return self._response(in_response_to, consumer_url,
|
||||
sp_entity_id, identity, name_id,
|
||||
status, sign, policy=self.conf.policy, issuer=issuer)
|
||||
|
||||
return self.create_response(in_response_to, consumer_url, sp_entity_id,
|
||||
identity, name_id, status,
|
||||
issuer=issuer,
|
||||
policy=self.conf.getattr("policy", "aa"),
|
||||
sign_assertion=sign_assertion,
|
||||
sign_response=sign_response)
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
|
||||
def authn_response(self, identity, in_response_to, destination,
|
||||
sp_entity_id, name_id_policy, userid, sign=False,
|
||||
authn=None, sign_response=False, authn_decl=None,
|
||||
issuer=None, instance=False):
|
||||
def create_authn_response(self, identity, in_response_to, destination,
|
||||
sp_entity_id, name_id_policy, userid,
|
||||
authn=None, authn_decl=None, issuer=None,
|
||||
sign_response=False, sign_assertion=False):
|
||||
""" Constructs an AuthenticationResponse
|
||||
|
||||
:param identity: Information about an user
|
||||
@@ -590,73 +592,50 @@ class Server(object):
|
||||
this response is an answer to.
|
||||
:param destination: Where the response should be sent
|
||||
:param sp_entity_id: The entity identifier of the Service Provider
|
||||
:param name_id_policy: ...
|
||||
:param name_id_policy: How the NameID should be constructed
|
||||
:param userid: The subject identifier
|
||||
:param sign: Whether the assertion should be signed or not. This is
|
||||
different from signing the response as such.
|
||||
:param authn: Information about the authentication
|
||||
:param sign_response: The response can be signed separately from the
|
||||
assertions.
|
||||
:param authn_decl:
|
||||
:param issuer: Issuer of the response
|
||||
:param instance: Whether to return the instance or a string
|
||||
representation
|
||||
:return: A XML string representing an authentication response
|
||||
:param sign_assertion: Whether the assertion should be signed or not.
|
||||
:param sign_response: Whether the response should be signed or not.
|
||||
:return: A response instance
|
||||
"""
|
||||
|
||||
name_id = None
|
||||
try:
|
||||
nid_formats = []
|
||||
for _sp in self.metadata.entity[sp_entity_id]["sp_sso"]:
|
||||
for _sp in self.metadata.entity[sp_entity_id]["spsso"]:
|
||||
nid_formats.extend([n.text for n in _sp.name_id_format])
|
||||
|
||||
policy = self.conf.policy
|
||||
policy = self.conf.getattr("policy", "idp")
|
||||
name_id = self.ident.construct_nameid(policy, userid, sp_entity_id,
|
||||
identity, name_id_policy,
|
||||
nid_formats)
|
||||
except IOError, exc:
|
||||
response = self.error_response(in_response_to, destination,
|
||||
sp_entity_id, exc, name_id)
|
||||
response = self.create_error_response(in_response_to, destination,
|
||||
sp_entity_id, exc, name_id)
|
||||
return ("%s" % response).split("\n")
|
||||
|
||||
try:
|
||||
response = self.do_response(
|
||||
in_response_to, # in_response_to
|
||||
destination, # consumer_url
|
||||
sp_entity_id, # sp_entity_id
|
||||
identity, # identity as dictionary
|
||||
name_id,
|
||||
sign=sign, # If the assertion should be signed
|
||||
authn=authn, # Information about the
|
||||
# authentication
|
||||
authn_decl=authn_decl,
|
||||
issuer=issuer
|
||||
)
|
||||
return self.create_response(in_response_to, # in_response_to
|
||||
destination, # consumer_url
|
||||
sp_entity_id, # sp_entity_id
|
||||
identity, # identity as dictionary
|
||||
name_id,
|
||||
authn=authn, # Information about the
|
||||
# authentication
|
||||
authn_decl=authn_decl,
|
||||
issuer=issuer,
|
||||
policy=policy,
|
||||
sign_assertion=sign_assertion,
|
||||
sign_response=sign_response)
|
||||
|
||||
except MissingValue, exc:
|
||||
response = self.error_response(in_response_to, destination,
|
||||
sp_entity_id, exc, name_id)
|
||||
return self.create_error_response(in_response_to, destination,
|
||||
sp_entity_id, exc, name_id)
|
||||
|
||||
|
||||
if sign_response:
|
||||
try:
|
||||
response.signature = pre_signature_part(response.id,
|
||||
self.sec.my_cert, 2)
|
||||
|
||||
return self.sec.sign_statement_using_xmlsec(response,
|
||||
class_name(response),
|
||||
nodeid=response.id)
|
||||
except Exception, exc:
|
||||
response = self.error_response(in_response_to, destination,
|
||||
sp_entity_id, exc, name_id)
|
||||
if instance:
|
||||
return response
|
||||
else:
|
||||
return ("%s" % response).split("\n")
|
||||
else:
|
||||
if instance:
|
||||
return response
|
||||
else:
|
||||
return ("%s" % response).split("\n")
|
||||
|
||||
def parse_logout_request(self, text, binding=BINDING_SOAP):
|
||||
"""Parse a Logout Request
|
||||
@@ -669,9 +648,9 @@ class Server(object):
|
||||
"""
|
||||
|
||||
try:
|
||||
slo = self.conf.endpoint("single_logout_service", binding)
|
||||
slo = self.conf.endpoint("single_logout_service", binding, "idp")
|
||||
except IndexError:
|
||||
logger.info("enpoints: %s" % (self.conf.endpoints,))
|
||||
logger.info("enpoints: %s" % self.conf.getattr("endpoints", "idp"))
|
||||
logger.info("binding wanted: %s" % (binding,))
|
||||
raise
|
||||
|
||||
@@ -703,8 +682,8 @@ class Server(object):
|
||||
return req
|
||||
|
||||
|
||||
def logout_response(self, request, bindings, status=None, sign=False,
|
||||
issuer=None):
|
||||
def create_logout_response(self, request, bindings, status=None,
|
||||
sign=False, issuer=None):
|
||||
""" Create a LogoutResponse. What is returned depends on which binding
|
||||
is used.
|
||||
|
||||
@@ -794,7 +773,7 @@ class Server(object):
|
||||
attribute - which attributes that the requestor wants back
|
||||
query - the whole query
|
||||
"""
|
||||
receiver_addresses = self.conf.endpoint("attribute_service")
|
||||
receiver_addresses = self.conf.endpoint("attribute_service", "idp")
|
||||
attribute_query = AttributeQuery( self.sec, receiver_addresses)
|
||||
|
||||
attribute_query = attribute_query.loads(xml_string)
|
||||
|
@@ -1006,7 +1006,7 @@ def logoutresponse_factory(sign=False, encrypt=False, **kwargs):
|
||||
|
||||
def response_factory(sign=False, encrypt=False, **kwargs):
|
||||
response = samlp.Response(id=sid(), version=VERSION,
|
||||
issue_instant=instant())
|
||||
issue_instant=instant())
|
||||
|
||||
if sign:
|
||||
response.signature = pre_signature_part(kwargs["id"])
|
||||
|
@@ -7,6 +7,7 @@ try:
|
||||
except ImportError:
|
||||
xmlsec_path = '/opt/local/bin/xmlsec1'
|
||||
|
||||
BASE = "http://localhost:8088"
|
||||
|
||||
CONFIG = {
|
||||
"entityid" : "urn:mace:example.com:saml:roland:idp",
|
||||
@@ -15,10 +16,10 @@ CONFIG = {
|
||||
"idp": {
|
||||
"endpoints" : {
|
||||
"single_sign_on_service" : [
|
||||
("http://localhost:8088/sso", BINDING_HTTP_REDIRECT)],
|
||||
("%s/sso" % BASE, BINDING_HTTP_REDIRECT)],
|
||||
"single_logout_service": [
|
||||
("http://localhost:8088/slo", BINDING_SOAP),
|
||||
("http://localhost:8088/slop",BINDING_HTTP_POST)]
|
||||
("%s/slo" % BASE, BINDING_SOAP),
|
||||
("%s/slop" % BASE,BINDING_HTTP_POST)]
|
||||
},
|
||||
"policy": {
|
||||
"default": {
|
||||
@@ -43,7 +44,7 @@ CONFIG = {
|
||||
"cert_file" : "test.pem",
|
||||
"xmlsec_binary" : xmlsec_path,
|
||||
"metadata": {
|
||||
"local": ["metadata.xml", "vo_metadata.xml"],
|
||||
"local": ["metadata_sp_1.xml", "vo_metadata.xml"],
|
||||
},
|
||||
"attribute_map_dir" : "attributemaps",
|
||||
"organization": {
|
||||
|
@@ -53,7 +53,7 @@ CONFIG = {
|
||||
"debug" : 1,
|
||||
"key_file" : "test.key",
|
||||
"cert_file" : "test.pem",
|
||||
#"xmlsec_binary" : xmlsec_path,
|
||||
"xmlsec_binary" : xmlsec_path,
|
||||
"metadata": {
|
||||
"local": ["metadata.xml", "vo_metadata.xml"],
|
||||
},
|
||||
|
@@ -21,7 +21,7 @@ CONFIG = {
|
||||
"debug" : 1,
|
||||
"key_file" : "test.key",
|
||||
"cert_file" : "test.pem",
|
||||
#"xmlsec_binary" : xmlsec_path,
|
||||
"xmlsec_binary" : xmlsec_path,
|
||||
"metadata": {
|
||||
"local": ["idp_aa.xml", "vo_metadata.xml"],
|
||||
},
|
||||
|
@@ -34,6 +34,7 @@ CONFIG={
|
||||
"subject_data": "subject_data.db",
|
||||
"accepted_time_diff": 60,
|
||||
"attribute_map_dir" : "attributemaps",
|
||||
"valid_for": 6,
|
||||
"organization": {
|
||||
"name": ("AB Exempel", "se"),
|
||||
"display_name": ("AB Exempel", "se"),
|
||||
|
@@ -20,7 +20,7 @@ CONFIG = {
|
||||
"debug" : 1,
|
||||
"key_file" : "test.key",
|
||||
"cert_file" : "test.pem",
|
||||
#"xmlsec_binary" : xmlsec_path,
|
||||
"xmlsec_binary" : xmlsec_path,
|
||||
"metadata": {
|
||||
"local": ["idp.xml", "vo_metadata.xml"],
|
||||
},
|
||||
|
@@ -8,7 +8,7 @@ from saml2 import BINDING_SOAP
|
||||
from saml2 import md, saml, samlp
|
||||
from saml2 import time_util
|
||||
from saml2.saml import NAMEID_FORMAT_TRANSIENT, NAME_FORMAT_URI
|
||||
from saml2.attribute_converter import ac_factory
|
||||
from saml2.attribute_converter import ac_factory, to_local_name
|
||||
|
||||
#from py.test import raises
|
||||
|
||||
@@ -48,38 +48,41 @@ def test_swami_1():
|
||||
md.import_metadata(_read_file(SWAMI_METADATA),"-")
|
||||
print len(md.entity)
|
||||
assert len(md.entity)
|
||||
idps = dict([(id,ent["idp_sso"]) for id,ent in md.entity.items() \
|
||||
if "idp_sso" in ent])
|
||||
idps = dict([(id,ent["idpsso"]) for id,ent in md.entity.items() \
|
||||
if "idpsso" in ent])
|
||||
print idps
|
||||
assert idps.keys()
|
||||
idp_sso = md.single_sign_on_services(
|
||||
idpsso = md.single_sign_on_services(
|
||||
'https://idp.umu.se/saml2/idp/metadata.php')
|
||||
assert md.name('https://idp.umu.se/saml2/idp/metadata.php') == (
|
||||
u'Ume\xe5 University (SAML2)')
|
||||
assert len(idp_sso) == 1
|
||||
assert idp_sso == ['https://idp.umu.se/saml2/idp/SSOService.php']
|
||||
assert len(idpsso) == 1
|
||||
assert idpsso == ['https://idp.umu.se/saml2/idp/SSOService.php']
|
||||
print md._loc_key['https://idp.umu.se/saml2/idp/SSOService.php']
|
||||
ssocerts = md.certs('https://idp.umu.se/saml2/idp/SSOService.php', "signing")
|
||||
print ssocerts
|
||||
assert len(ssocerts) == 1
|
||||
print md._wants.keys()
|
||||
assert _eq(md._wants.keys(),['https://sp.swamid.se/shibboleth',
|
||||
'https://connect8.sunet.se/shibboleth',
|
||||
'https://beta.lobber.se/shibboleth',
|
||||
'https://connect.uninett.no/shibboleth',
|
||||
'https://www.diva-portal.org/shibboleth',
|
||||
'https://connect.sunet.se/shibboleth',
|
||||
'https://crowd.nordu.net/shibboleth'])
|
||||
|
||||
print md.wants('https://www.diva-portal.org/shibboleth')
|
||||
assert _eq(md.wants('https://www.diva-portal.org/shibboleth')[1].keys(),
|
||||
sps = dict([(id,ent["spsso"]) for id,ent in md.entity.items()\
|
||||
if "spsso" in ent])
|
||||
|
||||
acs_sp = []
|
||||
for nam, desc in sps.items():
|
||||
if desc[0].attribute_consuming_service:
|
||||
acs_sp.append(nam)
|
||||
|
||||
#print md.wants('https://www.diva-portal.org/shibboleth')
|
||||
wants = md.attribute_requirement('https://connect8.sunet.se/shibboleth')
|
||||
lnamn = [to_local_name(md.attrconv, attr) for attr in wants[1]]
|
||||
assert _eq(lnamn,
|
||||
['mail', 'givenName', 'eduPersonPrincipalName', 'sn',
|
||||
'eduPersonScopedAffiliation'])
|
||||
|
||||
assert md.wants('https://connect.sunet.se/shibboleth')[0] == {}
|
||||
assert _eq(md.wants('https://connect.sunet.se/shibboleth')[1].keys(),
|
||||
['mail', 'givenName', 'eduPersonPrincipalName', 'sn',
|
||||
'eduPersonScopedAffiliation'])
|
||||
wants = md.attribute_requirement('https://beta.lobber.se/shibboleth')
|
||||
assert wants[0] == []
|
||||
lnamn = [to_local_name(md.attrconv, attr) for attr in wants[1]]
|
||||
assert _eq(lnamn,
|
||||
['eduPersonScopedAffiliation', 'eduPersonEntitlement',
|
||||
'eduPersonPrincipalName', 'sn', 'mail', 'givenName'])
|
||||
|
||||
def test_incommon_1():
|
||||
md = metadata.MetaData(attrconv=ATTRCONV)
|
||||
@@ -87,23 +90,39 @@ def test_incommon_1():
|
||||
print len(md.entity)
|
||||
assert len(md.entity) == 442
|
||||
idps = dict([
|
||||
(id,ent["idp_sso"]) for id,ent in md.entity.items() if "idp_sso" in ent])
|
||||
(id,ent["idpsso"]) for id,ent in md.entity.items() if "idpsso" in ent])
|
||||
print idps.keys()
|
||||
assert len(idps) == 53 # !!!!???? < 10%
|
||||
assert md.single_sign_on_services('urn:mace:incommon:uiuc.edu') == []
|
||||
idp_sso = md.single_sign_on_services('urn:mace:incommon:alaska.edu')
|
||||
assert len(idp_sso) == 1
|
||||
print idp_sso
|
||||
print md.wants
|
||||
assert idp_sso == ['https://idp.alaska.edu/idp/profile/SAML2/Redirect/SSO']
|
||||
|
||||
idpsso = md.single_sign_on_services('urn:mace:incommon:alaska.edu')
|
||||
assert len(idpsso) == 1
|
||||
print idpsso
|
||||
assert idpsso == ['https://idp.alaska.edu/idp/profile/SAML2/Redirect/SSO']
|
||||
|
||||
sps = dict([(id,ent["spsso"]) for id,ent in md.entity.items()\
|
||||
if "spsso" in ent])
|
||||
|
||||
acs_sp = []
|
||||
for nam, desc in sps.items():
|
||||
if desc[0].attribute_consuming_service:
|
||||
acs_sp.append(nam)
|
||||
|
||||
assert len(acs_sp) == 0
|
||||
|
||||
# Look for attribute authorities
|
||||
aas = dict([(id,ent["attribute_authority"]) for id,ent in md.entity.items()\
|
||||
if "attribute_authority" in ent])
|
||||
|
||||
print aas.keys()
|
||||
assert len(aas) == 53
|
||||
|
||||
def test_example():
|
||||
md = metadata.MetaData(attrconv=ATTRCONV)
|
||||
md.import_metadata(_read_file(EXAMPLE_METADATA), "-")
|
||||
print len(md.entity)
|
||||
assert len(md.entity) == 1
|
||||
idps = dict([(id,ent["idp_sso"]) for id,ent in md.entity.items() \
|
||||
if "idp_sso" in ent])
|
||||
idps = dict([(id,ent["idpsso"]) for id,ent in md.entity.items() \
|
||||
if "idpsso" in ent])
|
||||
assert idps.keys() == [
|
||||
'http://xenosmilus.umdc.umu.se/simplesaml/saml2/idp/metadata.php']
|
||||
print md._loc_key['http://xenosmilus.umdc.umu.se/simplesaml/saml2/idp/metadata.php']
|
||||
@@ -119,14 +138,14 @@ def test_switch_1():
|
||||
md.import_metadata(_read_file(SWITCH_METADATA), "-")
|
||||
print len(md.entity)
|
||||
assert len(md.entity) == 90
|
||||
idps = dict([(id,ent["idp_sso"]) for id,ent in md.entity.items() \
|
||||
if "idp_sso" in ent])
|
||||
idps = dict([(id,ent["idpsso"]) for id,ent in md.entity.items() \
|
||||
if "idpsso" in ent])
|
||||
print idps.keys()
|
||||
idp_sso = md.single_sign_on_services(
|
||||
idpsso = md.single_sign_on_services(
|
||||
'https://aai-demo-idp.switch.ch/idp/shibboleth')
|
||||
assert len(idp_sso) == 1
|
||||
print idp_sso
|
||||
assert idp_sso == [
|
||||
assert len(idpsso) == 1
|
||||
print idpsso
|
||||
assert idpsso == [
|
||||
'https://aai-demo-idp.switch.ch/idp/profile/SAML2/Redirect/SSO']
|
||||
assert len(idps) == 16
|
||||
aas = dict([(id,ent["attribute_authority"]) for id,ent in md.entity.items() \
|
||||
@@ -138,7 +157,7 @@ def test_switch_1():
|
||||
assert len(aad.attribute_service) == 1
|
||||
assert len(aad.name_id_format) == 2
|
||||
dual = dict([(id,ent) for id,ent in md.entity.items() \
|
||||
if "idp_sso" in ent and "sp_sso" in ent])
|
||||
if "idpsso" in ent and "spsso" in ent])
|
||||
print len(dual)
|
||||
assert len(dual) == 0
|
||||
|
||||
@@ -150,25 +169,18 @@ def test_sp_metadata():
|
||||
assert len(md.entity) == 1
|
||||
assert md.entity.keys() == ['urn:mace:umu.se:saml:roland:sp']
|
||||
assert _eq(md.entity['urn:mace:umu.se:saml:roland:sp'].keys(), [
|
||||
'valid_until',"organization","sp_sso",
|
||||
'valid_until',"organization","spsso",
|
||||
'contact_person'])
|
||||
print md.entity['urn:mace:umu.se:saml:roland:sp']["sp_sso"][0].keyswv()
|
||||
(req,opt) = md.attribute_consumer('urn:mace:umu.se:saml:roland:sp')
|
||||
print md.entity['urn:mace:umu.se:saml:roland:sp']["spsso"][0].keyswv()
|
||||
(req,opt) = md.attribute_requirement('urn:mace:umu.se:saml:roland:sp')
|
||||
print req
|
||||
assert len(req) == 3
|
||||
assert len(opt) == 1
|
||||
assert opt[0].name == 'urn:oid:2.5.4.12'
|
||||
assert opt[0].friendly_name == 'title'
|
||||
assert _eq([n.name for n in req],['urn:oid:2.5.4.4', 'urn:oid:2.5.4.42',
|
||||
'urn:oid:0.9.2342.19200300.100.1.3'])
|
||||
assert _eq([n.name for n in req],['urn:oid:2.5.4.4', 'urn:oid:2.5.4.42',
|
||||
'urn:oid:0.9.2342.19200300.100.1.3'])
|
||||
assert _eq([n.friendly_name for n in req],['surName', 'givenName', 'mail'])
|
||||
print md.wants
|
||||
|
||||
assert md._wants.keys() == ['urn:mace:umu.se:saml:roland:sp']
|
||||
assert _eq(md.wants('urn:mace:umu.se:saml:roland:sp')[0].keys(),
|
||||
["mail", "givenName", "sn"])
|
||||
assert _eq(md.wants('urn:mace:umu.se:saml:roland:sp')[1].keys(),
|
||||
["title"])
|
||||
|
||||
KALMAR2_URL = "https://kalmar2.org/simplesaml/module.php/aggregator/?id=kalmarcentral2&set=saml2"
|
||||
KALMAR2_CERT = "kalmar2.pem"
|
||||
@@ -180,7 +192,7 @@ KALMAR2_CERT = "kalmar2.pem"
|
||||
# print len(md.entity)
|
||||
# assert len(md.entity) > 20
|
||||
# idps = dict([
|
||||
# (id,ent["idp_sso"]) for id,ent in md.entity.items() if "idp_sso" in ent])
|
||||
# (id,ent["idpsso"]) for id,ent in md.entity.items() if "idpsso" in ent])
|
||||
# print idps.keys()
|
||||
# assert len(idps) > 1
|
||||
# assert "https://idp.umu.se/saml2/idp/metadata.php" in idps
|
||||
|
@@ -163,15 +163,15 @@ def test_1():
|
||||
c = SPConfig().load(sp1)
|
||||
c.context = "sp"
|
||||
print c
|
||||
assert c.endpoints
|
||||
assert c.name
|
||||
assert c.idp
|
||||
assert c._sp_endpoints
|
||||
assert c._sp_name
|
||||
assert c._sp_idp
|
||||
md = c.metadata
|
||||
assert isinstance(md, MetaData)
|
||||
|
||||
assert len(c.idp) == 1
|
||||
assert c.idp.keys() == ["urn:mace:example.com:saml:roland:idp"]
|
||||
assert c.idp.values() == [{'single_sign_on_service':
|
||||
assert len(c._sp_idp) == 1
|
||||
assert c._sp_idp.keys() == ["urn:mace:example.com:saml:roland:idp"]
|
||||
assert c._sp_idp.values() == [{'single_sign_on_service':
|
||||
{'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect':
|
||||
'http://localhost:8088/sso/'}}]
|
||||
|
||||
@@ -182,15 +182,16 @@ def test_2():
|
||||
c.context = "sp"
|
||||
|
||||
print c
|
||||
assert c.endpoints
|
||||
assert c.idp
|
||||
assert c.optional_attributes
|
||||
assert c._sp_endpoints
|
||||
assert c.getattr("endpoints", "sp")
|
||||
assert c._sp_idp
|
||||
assert c._sp_optional_attributes
|
||||
assert c.name
|
||||
assert c.required_attributes
|
||||
assert c._sp_required_attributes
|
||||
|
||||
assert len(c.idp) == 1
|
||||
assert c.idp.keys() == [""]
|
||||
assert c.idp.values() == ["https://example.com/saml2/idp/SSOService.php"]
|
||||
assert len(c._sp_idp) == 1
|
||||
assert c._sp_idp.keys() == [""]
|
||||
assert c._sp_idp.values() == ["https://example.com/saml2/idp/SSOService.php"]
|
||||
assert c.only_use_keys_in_metadata is None
|
||||
|
||||
def test_minimum():
|
||||
@@ -222,7 +223,7 @@ def test_idp_1():
|
||||
print c
|
||||
assert c.endpoint("single_sign_on_service")[0] == 'http://localhost:8088/'
|
||||
|
||||
attribute_restrictions = c.policy.get_attribute_restriction("")
|
||||
attribute_restrictions = c.getattr("policy","idp").get_attribute_restriction("")
|
||||
assert attribute_restrictions["eduPersonAffiliation"][0].match("staff")
|
||||
|
||||
def test_idp_2():
|
||||
@@ -235,7 +236,7 @@ def test_idp_2():
|
||||
assert c.endpoint("single_logout_service",
|
||||
BINDING_HTTP_REDIRECT) == ["http://localhost:8088/"]
|
||||
|
||||
attribute_restrictions = c.policy.get_attribute_restriction("")
|
||||
attribute_restrictions = c.getattr("policy","idp").get_attribute_restriction("")
|
||||
assert attribute_restrictions["eduPersonAffiliation"][0].match("staff")
|
||||
|
||||
def test_wayf():
|
||||
@@ -313,15 +314,12 @@ def test_sp():
|
||||
|
||||
def test_dual():
|
||||
cnf = Config().load_file("idp_sp_conf")
|
||||
assert cnf.serves() == ["sp", "idp"]
|
||||
|
||||
spcnf = cnf.copy_into("sp")
|
||||
assert isinstance(spcnf, SPConfig)
|
||||
assert spcnf.context == "sp"
|
||||
|
||||
idpcnf = cnf.copy_into("idp")
|
||||
assert isinstance(idpcnf, IdPConfig)
|
||||
assert idpcnf.context == "idp"
|
||||
spe = cnf.getattr("endpoints", "sp")
|
||||
idpe = cnf.getattr("endpoints", "idp")
|
||||
assert spe
|
||||
assert idpe
|
||||
assert spe != idpe
|
||||
|
||||
def test_ecp():
|
||||
cnf = SPConfig()
|
||||
|
@@ -19,7 +19,11 @@ XML_RESPONSE_FILE2 = "saml2_response.xml"
|
||||
|
||||
def _eq(l1,l2):
|
||||
return set(l1) == set(l2)
|
||||
|
||||
|
||||
IDENTITY = {"eduPersonAffiliation": ["staff", "member"],
|
||||
"surName": ["Jeter"], "givenName": ["Derek"],
|
||||
"mail": ["foo@gmail.com"]}
|
||||
|
||||
class TestResponse:
|
||||
def setup_class(self):
|
||||
server = Server("idp_conf")
|
||||
@@ -27,28 +31,28 @@ class TestResponse:
|
||||
"urn:mace:example.com:saml:roland:sp",
|
||||
"id12")
|
||||
|
||||
self._resp_ = server.do_response(
|
||||
self._resp_ = server.create_response(
|
||||
"id12", # in_response_to
|
||||
"http://lingon.catalogix.se:8087/", # consumer_url
|
||||
"urn:mace:example.com:saml:roland:sp", # sp_entity_id
|
||||
{"eduPersonEntitlement":"Jeter"},
|
||||
IDENTITY,
|
||||
name_id = name_id
|
||||
)
|
||||
|
||||
self._sign_resp_ = server.do_response(
|
||||
self._sign_resp_ = server.create_response(
|
||||
"id12", # in_response_to
|
||||
"http://lingon.catalogix.se:8087/", # consumer_url
|
||||
"urn:mace:example.com:saml:roland:sp", # sp_entity_id
|
||||
{"eduPersonEntitlement":"Jeter"},
|
||||
IDENTITY,
|
||||
name_id = name_id,
|
||||
sign=True
|
||||
sign_assertion=True
|
||||
)
|
||||
|
||||
self._resp_authn = server.do_response(
|
||||
self._resp_authn = server.create_response(
|
||||
"id12", # in_response_to
|
||||
"http://lingon.catalogix.se:8087/", # consumer_url
|
||||
"urn:mace:example.com:saml:roland:sp", # sp_entity_id
|
||||
{"eduPersonEntitlement":"Jeter"},
|
||||
IDENTITY,
|
||||
name_id = name_id,
|
||||
authn=(saml.AUTHN_PASSWORD, "http://www.example.com/login")
|
||||
)
|
||||
|
@@ -1,51 +1,48 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from saml2 import samlp, BINDING_HTTP_POST
|
||||
from saml2 import saml, config, class_name, make_instance
|
||||
from saml2 import saml
|
||||
from saml2.server import Server
|
||||
from saml2.response import authn_response, StatusResponse
|
||||
from saml2.response import authn_response
|
||||
from saml2.config import config_factory
|
||||
|
||||
XML_RESPONSE_FILE = "saml_signed.xml"
|
||||
XML_RESPONSE_FILE2 = "saml2_response.xml"
|
||||
|
||||
import os
|
||||
|
||||
def _eq(l1,l2):
|
||||
return set(l1) == set(l2)
|
||||
|
||||
|
||||
IDENTITY = {"eduPersonAffiliation": ["staff", "member"],
|
||||
"surName": ["Jeter"], "givenName": ["Derek"],
|
||||
"mail": ["foo@gmail.com"]}
|
||||
|
||||
class TestAuthnResponse:
|
||||
def setup_class(self):
|
||||
server = Server("idp_conf")
|
||||
name_id = server.ident.transient_nameid(
|
||||
"urn:mace:example.com:saml:roland:sp","id12")
|
||||
|
||||
self._resp_ = server.do_response(
|
||||
policy = server.conf.getattr("policy", "idp")
|
||||
self._resp_ = server.create_response(
|
||||
"id12", # in_response_to
|
||||
"http://lingon.catalogix.se:8087/", # consumer_url
|
||||
"urn:mace:example.com:saml:roland:sp", # sp_entity_id
|
||||
{"eduPersonEntitlement":"Jeter"},
|
||||
name_id = name_id
|
||||
)
|
||||
IDENTITY, name_id = name_id, policy=policy)
|
||||
|
||||
self._sign_resp_ = server.do_response(
|
||||
self._sign_resp_ = server.create_response(
|
||||
"id12", # in_response_to
|
||||
"http://lingon.catalogix.se:8087/", # consumer_url
|
||||
"urn:mace:example.com:saml:roland:sp", # sp_entity_id
|
||||
{"eduPersonEntitlement":"Jeter"},
|
||||
name_id = name_id,
|
||||
sign=True
|
||||
)
|
||||
IDENTITY,
|
||||
name_id = name_id, sign_assertion=True, policy=policy)
|
||||
|
||||
self._resp_authn = server.do_response(
|
||||
self._resp_authn = server.create_response(
|
||||
"id12", # in_response_to
|
||||
"http://lingon.catalogix.se:8087/", # consumer_url
|
||||
"urn:mace:example.com:saml:roland:sp", # sp_entity_id
|
||||
{"eduPersonEntitlement":"Jeter"},
|
||||
IDENTITY,
|
||||
name_id = name_id,
|
||||
authn=(saml.AUTHN_PASSWORD, "http://www.example.com/login")
|
||||
)
|
||||
authn=(saml.AUTHN_PASSWORD, "http://www.example.com/login"),
|
||||
policy=policy)
|
||||
|
||||
self.conf = config_factory("sp", "server_conf")
|
||||
self.ar = authn_response(self.conf, "http://lingon.catalogix.se:8087/")
|
||||
@@ -60,7 +57,7 @@ class TestAuthnResponse:
|
||||
print self.ar.__dict__
|
||||
assert self.ar.came_from == 'http://localhost:8088/sso'
|
||||
assert self.ar.session_id() == "id12"
|
||||
assert self.ar.ava == {'eduPersonEntitlement': ['Jeter'] }
|
||||
assert self.ar.ava == IDENTITY
|
||||
assert self.ar.name_id
|
||||
assert self.ar.issuer() == 'urn:mace:example.com:saml:roland:idp'
|
||||
|
||||
@@ -76,7 +73,7 @@ class TestAuthnResponse:
|
||||
print self.ar.__dict__
|
||||
assert self.ar.came_from == 'http://localhost:8088/sso'
|
||||
assert self.ar.session_id() == "id12"
|
||||
assert self.ar.ava == {'eduPersonEntitlement': ['Jeter'] }
|
||||
assert self.ar.ava == IDENTITY
|
||||
assert self.ar.issuer() == 'urn:mace:example.com:saml:roland:idp'
|
||||
assert self.ar.name_id
|
||||
|
||||
|
@@ -139,26 +139,17 @@ class TestServer1():
|
||||
assert status.status_code.value == samlp.STATUS_SUCCESS
|
||||
|
||||
def test_parse_faulty_request(self):
|
||||
authn_request = self.client.authn_request(
|
||||
query_id = "id1",
|
||||
destination = "http://www.example.com",
|
||||
service_url = "http://www.example.org",
|
||||
spentityid = "urn:mace:example.com:saml:roland:sp",
|
||||
my_name = "My real name",
|
||||
)
|
||||
authn_request = self.client.create_authn_request(
|
||||
destination = "http://www.example.com",
|
||||
id = "id1")
|
||||
|
||||
intermed = s_utils.deflate_and_base64_encode("%s" % authn_request)
|
||||
# should raise an error because faulty spentityid
|
||||
raises(OtherError, self.server.parse_authn_request, intermed)
|
||||
|
||||
def test_parse_faulty_request_to_err_status(self):
|
||||
authn_request = self.client.authn_request(
|
||||
query_id = "id1",
|
||||
destination = "http://www.example.com",
|
||||
service_url = "http://www.example.org",
|
||||
spentityid = "urn:mace:example.com:saml:roland:sp",
|
||||
my_name = "My real name",
|
||||
)
|
||||
authn_request = self.client.create_authn_request(
|
||||
destination = "http://www.example.com")
|
||||
|
||||
intermed = s_utils.deflate_and_base64_encode("%s" % authn_request)
|
||||
try:
|
||||
@@ -178,20 +169,17 @@ class TestServer1():
|
||||
assert status_code.status_code.value == samlp.STATUS_UNKNOWN_PRINCIPAL
|
||||
|
||||
def test_parse_ok_request(self):
|
||||
authn_request = self.client.authn_request(
|
||||
query_id = "id1",
|
||||
destination = "http://localhost:8088/sso",
|
||||
service_url = "http://localhost:8087/",
|
||||
spentityid = "urn:mace:example.com:saml:roland:sp",
|
||||
my_name = "My real name",
|
||||
)
|
||||
authn_request = self.client.create_authn_request(
|
||||
id = "id1",
|
||||
destination = "http://localhost:8088/sso")
|
||||
|
||||
print authn_request
|
||||
intermed = s_utils.deflate_and_base64_encode("%s" % authn_request)
|
||||
|
||||
response = self.server.parse_authn_request(intermed)
|
||||
# returns a dictionary
|
||||
print response
|
||||
assert response["consumer_url"] == "http://localhost:8087/"
|
||||
assert response["consumer_url"] == "http://lingon.catalogix.se:8087/"
|
||||
assert response["id"] == "id1"
|
||||
name_id_policy = response["request"].name_id_policy
|
||||
assert _eq(name_id_policy.keyswv(), ["format", "allow_create"])
|
||||
@@ -202,12 +190,16 @@ class TestServer1():
|
||||
name_id = self.server.ident.transient_nameid(
|
||||
"urn:mace:example.com:saml:roland:sp",
|
||||
"id12")
|
||||
resp = self.server.do_response(
|
||||
resp = self.server.create_response(
|
||||
"id12", # in_response_to
|
||||
"http://localhost:8087/", # consumer_url
|
||||
"urn:mace:example.com:saml:roland:sp", # sp_entity_id
|
||||
{ "eduPersonEntitlement": "Short stop"}, # identity
|
||||
name_id
|
||||
{"eduPersonEntitlement": "Short stop",
|
||||
"surName": "Jeter",
|
||||
"givenName": "Derek",
|
||||
"mail": "derek.jeter@nyy.mlb.com"},
|
||||
name_id,
|
||||
policy= self.server.conf.getattr("policy")
|
||||
)
|
||||
|
||||
print resp.keyswv()
|
||||
@@ -227,7 +219,7 @@ class TestServer1():
|
||||
assert assertion.attribute_statement
|
||||
attribute_statement = assertion.attribute_statement
|
||||
print attribute_statement
|
||||
assert len(attribute_statement.attribute) == 1
|
||||
assert len(attribute_statement.attribute) == 4
|
||||
attribute = attribute_statement.attribute[0]
|
||||
assert len(attribute.attribute_value) == 1
|
||||
assert attribute.friendly_name == "eduPersonEntitlement"
|
||||
@@ -245,7 +237,7 @@ class TestServer1():
|
||||
assert confirmation.subject_confirmation_data.in_response_to == "id12"
|
||||
|
||||
def test_sso_response_without_identity(self):
|
||||
resp = self.server.do_response(
|
||||
resp = self.server.create_response(
|
||||
"id12", # in_response_to
|
||||
"http://localhost:8087/", # consumer_url
|
||||
"urn:mace:example.com:saml:roland:sp", # sp_entity_id
|
||||
@@ -263,8 +255,9 @@ class TestServer1():
|
||||
|
||||
def test_sso_failure_response(self):
|
||||
exc = s_utils.MissingValue("eduPersonAffiliation missing")
|
||||
resp = self.server.error_response("id12", "http://localhost:8087/",
|
||||
"urn:mace:example.com:saml:roland:sp", exc )
|
||||
resp = self.server.create_error_response("id12",
|
||||
"http://localhost:8087/",
|
||||
exc )
|
||||
|
||||
print resp.keyswv()
|
||||
assert _eq(resp.keyswv(),['status', 'destination', 'in_response_to',
|
||||
@@ -291,14 +284,15 @@ class TestServer1():
|
||||
ava = { "givenName": ["Derek"], "surName": ["Jeter"],
|
||||
"mail": ["derek@nyy.mlb.com"]}
|
||||
|
||||
resp_str = self.server.authn_response(ava,
|
||||
"id1", "http://local:8087/",
|
||||
"urn:mace:example.com:saml:roland:sp",
|
||||
samlp.NameIDPolicy(format=saml.NAMEID_FORMAT_TRANSIENT,
|
||||
allow_create="true"),
|
||||
"foba0001@example.com")
|
||||
npolicy = samlp.NameIDPolicy(format=saml.NAMEID_FORMAT_TRANSIENT,
|
||||
allow_create="true")
|
||||
resp_str = "%s" % self.server.create_authn_response(
|
||||
ava, "id1", "http://local:8087/",
|
||||
"urn:mace:example.com:saml:roland:sp",
|
||||
npolicy,
|
||||
"foba0001@example.com")
|
||||
|
||||
response = samlp.response_from_string("\n".join(resp_str))
|
||||
response = samlp.response_from_string(resp_str)
|
||||
print response.keyswv()
|
||||
assert _eq(response.keyswv(),['status', 'destination', 'assertion',
|
||||
'in_response_to', 'issue_instant', 'version',
|
||||
@@ -318,14 +312,16 @@ class TestServer1():
|
||||
name_id = self.server.ident.transient_nameid(
|
||||
"urn:mace:example.com:saml:roland:sp",
|
||||
"id12")
|
||||
ava = { "givenName": ["Derek"], "surName": ["Jeter"],
|
||||
"mail": ["derek@nyy.mlb.com"]}
|
||||
|
||||
signed_resp = self.server.do_response(
|
||||
signed_resp = self.server.create_response(
|
||||
"id12", # in_response_to
|
||||
"http://lingon.catalogix.se:8087/", # consumer_url
|
||||
"urn:mace:example.com:saml:roland:sp", # sp_entity_id
|
||||
{"eduPersonEntitlement":"Jeter"},
|
||||
ava,
|
||||
name_id = name_id,
|
||||
sign=True
|
||||
sign_assertion=True
|
||||
)
|
||||
|
||||
print "%s" % signed_resp
|
||||
@@ -352,11 +348,11 @@ class TestServer1():
|
||||
}
|
||||
self.client.users.add_information_about_person(sinfo)
|
||||
|
||||
logout_request = self.client.construct_logout_request(
|
||||
subject_id="foba0001",
|
||||
destination = "http://localhost:8088/slop",
|
||||
issuer_entity_id = "urn:mace:example.com:saml:roland:idp",
|
||||
reason = "I'm tired of this")
|
||||
logout_request = self.client.create_logout_request(
|
||||
destination = "http://localhost:8088/slop",
|
||||
subject_id="foba0001",
|
||||
issuer_entity_id = "urn:mace:example.com:saml:roland:idp",
|
||||
reason = "I'm tired of this")
|
||||
|
||||
intermed = s_utils.deflate_and_base64_encode("%s" % (logout_request,))
|
||||
|
||||
@@ -379,10 +375,11 @@ class TestServer1():
|
||||
sp = client.Saml2Client(config_file="server_conf")
|
||||
sp.users.add_information_about_person(sinfo)
|
||||
|
||||
logout_request = sp.construct_logout_request(subject_id = "foba0001",
|
||||
destination = "http://localhost:8088/slo",
|
||||
issuer_entity_id = "urn:mace:example.com:saml:roland:idp",
|
||||
reason = "I'm tired of this")
|
||||
logout_request = sp.create_logout_request(
|
||||
subject_id = "foba0001",
|
||||
destination = "http://localhost:8088/slo",
|
||||
issuer_entity_id = "urn:mace:example.com:saml:roland:idp",
|
||||
reason = "I'm tired of this")
|
||||
|
||||
_ = s_utils.deflate_and_base64_encode("%s" % (logout_request,))
|
||||
|
||||
@@ -402,10 +399,12 @@ class TestServer2():
|
||||
self.server = Server("restrictive_idp_conf")
|
||||
|
||||
def test_do_aa_reponse(self):
|
||||
aa_policy = self.server.conf.policy
|
||||
aa_policy = self.server.conf.getattr("policy", "idp")
|
||||
print aa_policy.__dict__
|
||||
response = self.server.do_aa_response("aaa", "http://example.com/sp/",
|
||||
"urn:mace:example.com:sp:1", IDENTITY.copy())
|
||||
response = self.server.create_aa_response("aaa",
|
||||
"http://example.com/sp/",
|
||||
"urn:mace:example.com:sp:1",
|
||||
IDENTITY.copy())
|
||||
|
||||
assert response is not None
|
||||
assert response.destination == "http://example.com/sp/"
|
||||
@@ -439,7 +438,7 @@ def _logout_request(conf_file):
|
||||
}
|
||||
sp.users.add_information_about_person(sinfo)
|
||||
|
||||
return sp.construct_logout_request(
|
||||
return sp.create_logout_request(
|
||||
subject_id = "foba0001",
|
||||
destination = "http://localhost:8088/slo",
|
||||
issuer_entity_id = "urn:mace:example.com:saml:roland:idp",
|
||||
@@ -452,7 +451,8 @@ class TestServerLogout():
|
||||
request = _logout_request("sp_slo_redirect_conf")
|
||||
print request
|
||||
bindings = [BINDING_HTTP_REDIRECT]
|
||||
(resp, headers, message) = server.logout_response(request, bindings)
|
||||
(resp, headers, message) = server.create_logout_response(request,
|
||||
bindings)
|
||||
assert resp == '302 Found'
|
||||
assert len(headers) == 1
|
||||
assert headers[0][0] == "Location"
|
||||
|
@@ -6,9 +6,12 @@ import urllib
|
||||
from urlparse import urlparse, parse_qs
|
||||
|
||||
from saml2.client import Saml2Client, LogoutError
|
||||
from saml2 import samlp, BINDING_HTTP_POST
|
||||
from saml2 import samlp, BINDING_HTTP_POST, BINDING_HTTP_REDIRECT
|
||||
from saml2 import BINDING_SOAP
|
||||
from saml2 import saml, config, class_name
|
||||
from saml2.discovery import discovery_service_request_url
|
||||
from saml2.discovery import discovery_service_response
|
||||
from saml2.saml import NAMEID_FORMAT_PERSISTENT
|
||||
from saml2.server import Server
|
||||
from saml2.s_utils import decode_base64_and_inflate
|
||||
from saml2.time_util import in_a_while
|
||||
@@ -62,10 +65,11 @@ class TestClient:
|
||||
self.client = Saml2Client(conf)
|
||||
|
||||
def test_create_attribute_query1(self):
|
||||
req = self.client.create_attribute_query("id1",
|
||||
"E8042FB4-4D5B-48C3-8E14-8EDD852790DD",
|
||||
"https://idp.example.com/idp/",
|
||||
nameid_format=saml.NAMEID_FORMAT_PERSISTENT)
|
||||
req = self.client.create_attribute_query(
|
||||
"https://idp.example.com/idp/",
|
||||
"E8042FB4-4D5B-48C3-8E14-8EDD852790DD",
|
||||
nameid_format=saml.NAMEID_FORMAT_PERSISTENT,
|
||||
id="id1")
|
||||
reqstr = "%s" % req.to_string()
|
||||
|
||||
assert req.destination == "https://idp.example.com/idp/"
|
||||
@@ -93,9 +97,9 @@ class TestClient:
|
||||
assert attrq.subject.name_id.text == name_id.text
|
||||
|
||||
def test_create_attribute_query2(self):
|
||||
req = self.client.create_attribute_query("id1",
|
||||
"E8042FB4-4D5B-48C3-8E14-8EDD852790DD",
|
||||
req = self.client.create_attribute_query(
|
||||
"https://idp.example.com/idp/",
|
||||
"E8042FB4-4D5B-48C3-8E14-8EDD852790DD",
|
||||
attribute={
|
||||
("urn:oid:2.5.4.42",
|
||||
"urn:oasis:names:tc:SAML:2.0:attrname-format:uri",
|
||||
@@ -106,7 +110,8 @@ class TestClient:
|
||||
("urn:oid:1.2.840.113549.1.9.1",
|
||||
"urn:oasis:names:tc:SAML:2.0:attrname-format:uri"):None,
|
||||
},
|
||||
nameid_format=saml.NAMEID_FORMAT_PERSISTENT)
|
||||
nameid_format=saml.NAMEID_FORMAT_PERSISTENT,
|
||||
id="id1")
|
||||
|
||||
print req.to_string()
|
||||
assert req.destination == "https://idp.example.com/idp/"
|
||||
@@ -133,13 +138,14 @@ class TestClient:
|
||||
if getattr(attribute,"friendly_name"):
|
||||
assert False
|
||||
seen.append("email")
|
||||
assert set(seen) == set(["givenName", "surname", "email"])
|
||||
assert set(seen) == {"givenName", "surname", "email"}
|
||||
|
||||
def test_create_attribute_query_3(self):
|
||||
req = self.client.create_attribute_query("id1",
|
||||
"_e7b68a04488f715cda642fbdd90099f5",
|
||||
req = self.client.create_attribute_query(
|
||||
"https://aai-demo-idp.switch.ch/idp/shibboleth",
|
||||
nameid_format=saml.NAMEID_FORMAT_TRANSIENT )
|
||||
"_e7b68a04488f715cda642fbdd90099f5",
|
||||
nameid_format=saml.NAMEID_FORMAT_TRANSIENT,
|
||||
id="id1")
|
||||
|
||||
assert isinstance(req, samlp.AttributeQuery)
|
||||
assert req.destination == "https://aai-demo-idp.switch.ch/idp/shibboleth"
|
||||
@@ -152,13 +158,13 @@ class TestClient:
|
||||
assert nameid.text == "_e7b68a04488f715cda642fbdd90099f5"
|
||||
|
||||
def test_attribute_query(self):
|
||||
req = self.client.attribute_query(
|
||||
"_e7b68a04488f715cda642fbdd90099f5",
|
||||
"https://aai-demo-idp.switch.ch/idp/shibboleth",
|
||||
resp = self.client.do_attribute_query(
|
||||
"urn:mace:example.com:saml:roland:idp",
|
||||
"_e7b68a04488f715cda642fbdd90099f5",
|
||||
nameid_format=saml.NAMEID_FORMAT_TRANSIENT)
|
||||
|
||||
# since no one is answering on the other end
|
||||
assert req is None
|
||||
assert resp is None
|
||||
|
||||
# def test_idp_entry(self):
|
||||
# idp_entry = self.client.idp_entry(name="Umeå Universitet",
|
||||
@@ -179,19 +185,17 @@ class TestClient:
|
||||
# assert idp_entry.loc == ['http://localhost:8088/sso']
|
||||
|
||||
def test_create_auth_request_0(self):
|
||||
ar_str = "%s" % self.client.authn_request("id1",
|
||||
ar_str = "%s" % self.client.create_authn_request(
|
||||
"http://www.example.com/sso",
|
||||
"http://www.example.org/service",
|
||||
"urn:mace:example.org:saml:sp",
|
||||
"My Name")
|
||||
id="id1")
|
||||
ar = samlp.authn_request_from_string(ar_str)
|
||||
print ar
|
||||
assert ar.assertion_consumer_service_url == "http://www.example.org/service"
|
||||
assert ar.assertion_consumer_service_url == "http://lingon.catalogix.se:8087/"
|
||||
assert ar.destination == "http://www.example.com/sso"
|
||||
assert ar.protocol_binding == BINDING_HTTP_POST
|
||||
assert ar.version == "2.0"
|
||||
assert ar.provider_name == "My Name"
|
||||
assert ar.issuer.text == "urn:mace:example.org:saml:sp"
|
||||
assert ar.provider_name == "urn:mace:example.com:saml:roland:sp"
|
||||
assert ar.issuer.text == "urn:mace:example.com:saml:roland:sp"
|
||||
nid_policy = ar.name_id_policy
|
||||
assert nid_policy.allow_create == "true"
|
||||
assert nid_policy.format == saml.NAMEID_FORMAT_TRANSIENT
|
||||
@@ -200,36 +204,34 @@ class TestClient:
|
||||
assert self.client.config.virtual_organization.keys() == [
|
||||
"urn:mace:example.com:it:tek"]
|
||||
|
||||
ar_str = "%s" % self.client.authn_request("666",
|
||||
ar_str = "%s" % self.client.create_authn_request(
|
||||
"http://www.example.com/sso",
|
||||
"http://www.example.org/service",
|
||||
"urn:mace:example.org:saml:sp",
|
||||
"My Name",
|
||||
vorg="urn:mace:example.com:it:tek")
|
||||
"urn:mace:example.com:it:tek", # vo
|
||||
nameid_format=NAMEID_FORMAT_PERSISTENT,
|
||||
id="666")
|
||||
|
||||
ar = samlp.authn_request_from_string(ar_str)
|
||||
print ar
|
||||
assert ar.id == "666"
|
||||
assert ar.assertion_consumer_service_url == "http://www.example.org/service"
|
||||
assert ar.assertion_consumer_service_url == "http://lingon.catalogix.se:8087/"
|
||||
assert ar.destination == "http://www.example.com/sso"
|
||||
assert ar.protocol_binding == BINDING_HTTP_POST
|
||||
assert ar.version == "2.0"
|
||||
assert ar.provider_name == "My Name"
|
||||
assert ar.issuer.text == "urn:mace:example.org:saml:sp"
|
||||
assert ar.provider_name == "urn:mace:example.com:saml:roland:sp"
|
||||
assert ar.issuer.text == "urn:mace:example.com:saml:roland:sp"
|
||||
nid_policy = ar.name_id_policy
|
||||
assert nid_policy.allow_create == "true"
|
||||
assert nid_policy.allow_create == "false"
|
||||
assert nid_policy.format == saml.NAMEID_FORMAT_PERSISTENT
|
||||
assert nid_policy.sp_name_qualifier == "urn:mace:example.com:it:tek"
|
||||
|
||||
def test_sign_auth_request_0(self):
|
||||
#print self.client.config
|
||||
|
||||
ar_str = "%s" % self.client.authn_request("id1",
|
||||
ar_str = "%s" % self.client.create_authn_request(
|
||||
"http://www.example.com/sso",
|
||||
"http://www.example.org/service",
|
||||
"urn:mace:example.org:saml:sp",
|
||||
"My Name", sign=True)
|
||||
|
||||
sign=True,
|
||||
id="id1")
|
||||
|
||||
ar = samlp.authn_request_from_string(ar_str)
|
||||
|
||||
assert ar
|
||||
@@ -251,17 +253,20 @@ class TestClient:
|
||||
def test_response(self):
|
||||
IDP = "urn:mace:example.com:saml:roland:idp"
|
||||
|
||||
ava = { "givenName": ["Derek"], "surname": ["Jeter"],
|
||||
ava = { "givenName": ["Derek"], "surName": ["Jeter"],
|
||||
"mail": ["derek@nyy.mlb.com"]}
|
||||
|
||||
resp_str = "\n".join(self.server.authn_response(
|
||||
identity=ava,
|
||||
in_response_to="id1",
|
||||
destination="http://lingon.catalogix.se:8087/",
|
||||
sp_entity_id="urn:mace:example.com:saml:roland:sp",
|
||||
name_id_policy=samlp.NameIDPolicy(
|
||||
format=saml.NAMEID_FORMAT_PERSISTENT),
|
||||
userid="foba0001@example.com"))
|
||||
nameid_policy=samlp.NameIDPolicy(allow_create="false",
|
||||
format=saml.NAMEID_FORMAT_PERSISTENT)
|
||||
|
||||
resp = self.server.create_authn_response(identity=ava,
|
||||
in_response_to="id1",
|
||||
destination="http://lingon.catalogix.se:8087/",
|
||||
sp_entity_id="urn:mace:example.com:saml:roland:sp",
|
||||
name_id_policy=nameid_policy,
|
||||
userid="foba0001@example.com")
|
||||
|
||||
resp_str = "%s" % resp
|
||||
|
||||
resp_str = base64.encodestring(resp_str)
|
||||
|
||||
@@ -274,7 +279,9 @@ class TestClient:
|
||||
session_info = authn_response.session_info()
|
||||
|
||||
print session_info
|
||||
assert session_info["ava"] == {'mail': ['derek@nyy.mlb.com'], 'givenName': ['Derek'], 'sn': ['Jeter']}
|
||||
assert session_info["ava"] == {'mail': ['derek@nyy.mlb.com'],
|
||||
'givenName': ['Derek'],
|
||||
'surName': ['Jeter']}
|
||||
assert session_info["issuer"] == IDP
|
||||
assert session_info["came_from"] == "http://foo.example.com/service"
|
||||
response = samlp.response_from_string(authn_response.xmlstr)
|
||||
@@ -289,17 +296,16 @@ class TestClient:
|
||||
|
||||
# --- authenticate another person
|
||||
|
||||
ava = { "givenName": ["Alfonson"], "surname": ["Soriano"],
|
||||
ava = { "givenName": ["Alfonson"], "surName": ["Soriano"],
|
||||
"mail": ["alfonson@chc.mlb.com"]}
|
||||
|
||||
resp_str = "\n".join(self.server.authn_response(
|
||||
identity=ava,
|
||||
in_response_to="id2",
|
||||
destination="http://lingon.catalogix.se:8087/",
|
||||
sp_entity_id="urn:mace:example.com:saml:roland:sp",
|
||||
name_id_policy=samlp.NameIDPolicy(
|
||||
format=saml.NAMEID_FORMAT_PERSISTENT),
|
||||
userid="also0001@example.com"))
|
||||
resp_str = "%s" % self.server.create_authn_response(
|
||||
identity=ava,
|
||||
in_response_to="id2",
|
||||
destination="http://lingon.catalogix.se:8087/",
|
||||
sp_entity_id="urn:mace:example.com:saml:roland:sp",
|
||||
name_id_policy=nameid_policy,
|
||||
userid="also0001@example.com")
|
||||
|
||||
resp_str = base64.encodestring(resp_str)
|
||||
|
||||
@@ -317,7 +323,6 @@ class TestClient:
|
||||
entityid = self.client.config.entityid
|
||||
print entityid
|
||||
assert entityid == "urn:mace:example.com:saml:roland:sp"
|
||||
print self.client.config.idp
|
||||
print self.client.config.metadata.idps()
|
||||
print self.client.config.idps()
|
||||
location = self.client._sso_location()
|
||||
@@ -332,10 +337,9 @@ class TestClient:
|
||||
|
||||
def test_authenticate(self):
|
||||
print self.client.config.idps()
|
||||
(sid, response) = self.client.authenticate(
|
||||
response = self.client.do_authenticate(
|
||||
"urn:mace:example.com:saml:roland:idp",
|
||||
"http://www.example.com/relay_state")
|
||||
assert sid is not None
|
||||
assert response[0] == "Location"
|
||||
o = urlparse(response[1])
|
||||
qdict = parse_qs(o.query)
|
||||
@@ -343,13 +347,11 @@ class TestClient:
|
||||
saml_request = decode_base64_and_inflate(qdict["SAMLRequest"][0])
|
||||
print saml_request
|
||||
authnreq = samlp.authn_request_from_string(saml_request)
|
||||
assert authnreq.id == sid
|
||||
|
||||
def test_authenticate_no_args(self):
|
||||
(sid, request) = self.client.authenticate(relay_state="http://www.example.com/relay_state")
|
||||
assert sid is not None
|
||||
assert request[0] == "Location"
|
||||
o = urlparse(request[1])
|
||||
response = self.client.do_authenticate(relay_state="http://www.example.com/relay_state")
|
||||
assert response[0] == "Location"
|
||||
o = urlparse(response[1])
|
||||
qdict = parse_qs(o.query)
|
||||
assert _leq(qdict.keys(), ['SAMLRequest', 'RelayState'])
|
||||
saml_request = decode_base64_and_inflate(qdict["SAMLRequest"][0])
|
||||
@@ -357,14 +359,13 @@ class TestClient:
|
||||
print saml_request
|
||||
authnreq = samlp.authn_request_from_string(saml_request)
|
||||
print authnreq.keyswv()
|
||||
assert authnreq.id == sid
|
||||
assert authnreq.destination == "http://localhost:8088/sso"
|
||||
assert authnreq.assertion_consumer_service_url == "http://lingon.catalogix.se:8087/"
|
||||
assert authnreq.provider_name == "urn:mace:example.com:saml:roland:sp"
|
||||
assert authnreq.protocol_binding == BINDING_HTTP_POST
|
||||
assert authnreq.protocol_binding == BINDING_HTTP_REDIRECT
|
||||
name_id_policy = authnreq.name_id_policy
|
||||
assert name_id_policy.allow_create == "true"
|
||||
assert name_id_policy.format == "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
|
||||
assert name_id_policy.allow_create == "false"
|
||||
assert name_id_policy.format == NAMEID_FORMAT_PERSISTENT
|
||||
issuer = authnreq.issuer
|
||||
assert issuer.text == "urn:mace:example.com:saml:roland:sp"
|
||||
|
||||
@@ -386,7 +387,8 @@ class TestClient:
|
||||
self.client.users.add_information_about_person(session_info)
|
||||
entity_ids = self.client.users.issuers_of_info("123456")
|
||||
assert entity_ids == ["urn:mace:example.com:saml:roland:idp"]
|
||||
resp = self.client.global_logout("123456", "Tired", in_a_while(minutes=5))
|
||||
resp = self.client.global_logout("123456", "Tired",
|
||||
in_a_while(minutes=5))
|
||||
print resp
|
||||
assert resp
|
||||
assert resp[0] # a session_id
|
||||
@@ -401,7 +403,7 @@ class TestClient:
|
||||
assert session_info["reason"] == "Tired"
|
||||
assert session_info["operation"] == "SLO"
|
||||
assert session_info["entity_ids"] == entity_ids
|
||||
assert session_info["sign"] == False
|
||||
assert session_info["sign"] == True
|
||||
|
||||
def test_logout_2(self):
|
||||
""" one IdP/AA with BINDING_SOAP, can't actually send something"""
|
||||
@@ -480,7 +482,7 @@ class TestClient:
|
||||
assert state_info["reason"] == "Tired"
|
||||
assert state_info["operation"] == "SLO"
|
||||
assert state_info["entity_ids"] == entity_ids
|
||||
assert state_info["sign"] == False
|
||||
assert state_info["sign"] == True
|
||||
|
||||
def test_authz_decision_query(self):
|
||||
conf = config.SPConfig()
|
||||
@@ -503,7 +505,7 @@ class TestClient:
|
||||
conf.attribute_converters,
|
||||
policy, issuer=client._issuer())
|
||||
|
||||
adq = client.authz_decision_query_using_assertion("entity_id",
|
||||
adq = client.create_authz_decision_query_using_assertion("entity_id",
|
||||
assertion,
|
||||
"read",
|
||||
"http://example.com/text")
|
||||
@@ -517,11 +519,14 @@ class TestClient:
|
||||
|
||||
def test_request_to_discovery_service(self):
|
||||
disc_url = "http://example.com/saml2/idp/disc"
|
||||
url = self.client.discovery_service_request_url(disc_url)
|
||||
url = discovery_service_request_url("urn:mace:example.com:saml:roland:sp",
|
||||
disc_url)
|
||||
print url
|
||||
assert url == "http://example.com/saml2/idp/disc?entityID=urn%3Amace%3Aexample.com%3Asaml%3Aroland%3Asp"
|
||||
|
||||
url = self.client.discovery_service_request_url(disc_url,
|
||||
url = discovery_service_request_url(
|
||||
self.client.config.entityid,
|
||||
disc_url,
|
||||
return_url= "http://example.org/saml2/sp/ds")
|
||||
|
||||
print url
|
||||
@@ -532,15 +537,15 @@ class TestClient:
|
||||
params = urllib.urlencode(pdir)
|
||||
redirect_url = "http://example.com/saml2/sp/disc?%s" % params
|
||||
|
||||
entity_id = self.client.discovery_service_response(url=redirect_url)
|
||||
entity_id = discovery_service_response(url=redirect_url)
|
||||
assert entity_id == "http://example.org/saml2/idp/sso"
|
||||
|
||||
pdir = {"idpID": "http://example.org/saml2/idp/sso"}
|
||||
params = urllib.urlencode(pdir)
|
||||
redirect_url = "http://example.com/saml2/sp/disc?%s" % params
|
||||
|
||||
entity_id = self.client.discovery_service_response(url=redirect_url,
|
||||
returnIDParam="idpID")
|
||||
entity_id = discovery_service_response(url=redirect_url,
|
||||
returnIDParam="idpID")
|
||||
|
||||
assert entity_id == "http://example.org/saml2/idp/sso"
|
||||
|
||||
@@ -559,17 +564,17 @@ class TestClient:
|
||||
|
||||
IDP = "urn:mace:example.com:saml:roland:idp"
|
||||
|
||||
ava = { "givenName": ["Derek"], "surname": ["Jeter"],
|
||||
ava = { "givenName": ["Derek"], "surName": ["Jeter"],
|
||||
"mail": ["derek@nyy.mlb.com"]}
|
||||
|
||||
resp_str = "\n".join(self.server.authn_response(
|
||||
identity=ava,
|
||||
in_response_to="id1",
|
||||
destination="http://lingon.catalogix.se:8087/",
|
||||
sp_entity_id="urn:mace:example.com:saml:roland:sp",
|
||||
name_id_policy=samlp.NameIDPolicy(
|
||||
format=saml.NAMEID_FORMAT_PERSISTENT),
|
||||
userid="foba0001@example.com"))
|
||||
resp_str = "%s" % self.server.create_authn_response(
|
||||
identity=ava,
|
||||
in_response_to="id1",
|
||||
destination="http://lingon.catalogix.se:8087/",
|
||||
sp_entity_id="urn:mace:example.com:saml:roland:sp",
|
||||
name_id_policy=samlp.NameIDPolicy(
|
||||
format=saml.NAMEID_FORMAT_PERSISTENT),
|
||||
userid="foba0001@example.com")
|
||||
|
||||
resp_str = base64.encodestring(resp_str)
|
||||
|
||||
@@ -582,7 +587,9 @@ class TestClient:
|
||||
session_info = authn_response.session_info()
|
||||
|
||||
print session_info
|
||||
assert session_info["ava"] == {'mail': ['derek@nyy.mlb.com'], 'givenName': ['Derek'], 'sn': ['Jeter']}
|
||||
assert session_info["ava"] == {'mail': ['derek@nyy.mlb.com'],
|
||||
'givenName': ['Derek'],
|
||||
'surName': ['Jeter']}
|
||||
assert session_info["issuer"] == IDP
|
||||
assert session_info["came_from"] == ""
|
||||
response = samlp.response_from_string(authn_response.xmlstr)
|
||||
|
@@ -2,6 +2,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import base64
|
||||
from saml2.saml import NAMEID_FORMAT_TRANSIENT
|
||||
from saml2.samlp import NameIDPolicy
|
||||
from s2repoze.plugins.sp import make_plugin
|
||||
from saml2.server import Server
|
||||
from saml2 import make_instance, samlp, saml
|
||||
@@ -30,7 +32,9 @@ ENV1 = {'SERVER_SOFTWARE': 'CherryPy/3.1.2 WSGI Server',
|
||||
'wsgi.multiprocess': False,
|
||||
'HTTP_ACCEPT_LANGUAGE': 'en-us',
|
||||
'HTTP_ACCEPT_ENCODING': 'gzip, deflate'}
|
||||
|
||||
|
||||
trans_name_policy = NameIDPolicy(format=NAMEID_FORMAT_TRANSIENT,
|
||||
allow_create="true")
|
||||
class TestSP():
|
||||
def setup_class(self):
|
||||
self.sp = make_plugin("rem", saml_conf="server_conf")
|
||||
@@ -42,15 +46,14 @@ class TestSP():
|
||||
def test_identify(self):
|
||||
|
||||
# Create a SAMLResponse
|
||||
ava = { "givenName": ["Derek"], "surname": ["Jeter"],
|
||||
ava = { "givenName": ["Derek"], "surName": ["Jeter"],
|
||||
"mail": ["derek@nyy.mlb.com"]}
|
||||
|
||||
resp_str = "\n".join(self.server.authn_response(ava,
|
||||
"id1", "http://lingon.catalogix.se:8087/",
|
||||
"urn:mace:example.com:saml:roland:sp",
|
||||
samlp.NameIDPolicy(format=saml.NAMEID_FORMAT_TRANSIENT,
|
||||
allow_create="true"),
|
||||
"foba0001@example.com"))
|
||||
resp_str = "%s" % self.server.create_authn_response(ava, "id1",
|
||||
"http://lingon.catalogix.se:8087/",
|
||||
"urn:mace:example.com:saml:roland:sp",
|
||||
trans_name_policy,
|
||||
"foba0001@example.com")
|
||||
|
||||
resp_str = base64.encodestring(resp_str)
|
||||
self.sp.outstanding_queries = {"id1":"http://www.example.com/service"}
|
||||
@@ -60,4 +63,4 @@ class TestSP():
|
||||
assert session_info["came_from"] == 'http://www.example.com/service'
|
||||
assert session_info["ava"] == {'givenName': ['Derek'],
|
||||
'mail': ['derek@nyy.mlb.com'],
|
||||
'sn': ['Jeter']}
|
||||
'surName': ['Jeter']}
|
@@ -186,7 +186,7 @@ def test_optional_attributes():
|
||||
|
||||
def test_do_sp_sso_descriptor():
|
||||
conf = SPConfig().load(SP, metadata_construction=True)
|
||||
spsso = metadata.do_sp_sso_descriptor(conf)
|
||||
spsso = metadata.do_spsso_descriptor(conf)
|
||||
|
||||
assert isinstance(spsso, md.SPSSODescriptor)
|
||||
assert _eq(spsso.keyswv(), ['authn_requests_signed',
|
||||
@@ -215,7 +215,7 @@ def test_do_sp_sso_descriptor_2():
|
||||
SP["service"]["sp"]["discovery_response"] = "http://example.com/sp/ds"
|
||||
|
||||
conf = SPConfig().load(SP, metadata_construction=True)
|
||||
spsso = metadata.do_sp_sso_descriptor(conf)
|
||||
spsso = metadata.do_spsso_descriptor(conf)
|
||||
|
||||
assert isinstance(spsso, md.SPSSODescriptor)
|
||||
print spsso.keyswv()
|
||||
@@ -242,7 +242,7 @@ def test_entity_description():
|
||||
#confd = eval(open("../tests/server.config").read())
|
||||
confd = SPConfig().load_file("server_conf")
|
||||
print confd.attribute_converters
|
||||
entd = metadata.entity_descriptor(confd, 1)
|
||||
entd = metadata.entity_descriptor(confd)
|
||||
assert entd is not None
|
||||
print entd.keyswv()
|
||||
assert _eq(entd.keyswv(), ['valid_until', 'entity_id', 'contact_person',
|
||||
@@ -252,7 +252,7 @@ def test_entity_description():
|
||||
|
||||
def test_do_idp_sso_descriptor():
|
||||
conf = IdPConfig().load(IDP, metadata_construction=True)
|
||||
idpsso = metadata.do_idp_sso_descriptor(conf)
|
||||
idpsso = metadata.do_idpsso_descriptor(conf)
|
||||
|
||||
assert isinstance(idpsso, md.IDPSSODescriptor)
|
||||
assert _eq(idpsso.keyswv(), ['protocol_support_enumeration',
|
||||
|
@@ -108,7 +108,7 @@ def main(args):
|
||||
if fil.endswith(".py"):
|
||||
fil = fil[:-3]
|
||||
cnf = Config().load_file(fil, metadata_construction=True)
|
||||
eds.append(entity_descriptor(cnf, valid_for))
|
||||
eds.append(entity_descriptor(cnf))
|
||||
|
||||
secc = SecurityContext(xmlsec, keyfile, cert_file=pubkeyfile)
|
||||
if entitiesid:
|
||||
@@ -118,7 +118,7 @@ def main(args):
|
||||
else:
|
||||
for eid in eds:
|
||||
if sign:
|
||||
desc = sign_entity_descriptor(eid, valid_for, id, secc)
|
||||
desc = sign_entity_descriptor(eid, id, secc)
|
||||
else:
|
||||
desc = eid
|
||||
valid_instance(desc)
|
||||
|
Reference in New Issue
Block a user