Numerous changes more than can be stated here.
Basically all the modules made by hand from the SAML XML schemas has been replaced with modules made by a XML Schema -> Python module script. This change has caused a lot of necessary changes in code that depends on the format/content of the SAML modules. The configuration has changed, partly because the need to support more than one endpoint.
This commit is contained in:
@@ -36,7 +36,7 @@ from repoze.who.plugins.form import FormPluginBase
|
||||
from saml2.client import Saml2Client
|
||||
from saml2.attribute_resolver import AttributeResolver
|
||||
from saml2.config import Config
|
||||
from saml2.cache import Cache
|
||||
from saml2.population import Population
|
||||
|
||||
def construct_came_from(environ):
|
||||
""" The URL that the user used when the process where interupted
|
||||
@@ -55,7 +55,12 @@ def cgi_fieldStorage_to_dict( fieldStorage ):
|
||||
|
||||
params = {}
|
||||
for key in fieldStorage.keys():
|
||||
params[ key ] = fieldStorage[ key ].value
|
||||
try:
|
||||
params[ key ] = fieldStorage[ key ].value
|
||||
except AttributeError:
|
||||
if isinstance(fieldStorage[ key ], basestring):
|
||||
params[key] = fieldStorage[key]
|
||||
|
||||
return params
|
||||
|
||||
class SAML2Plugin(FormPluginBase):
|
||||
@@ -92,18 +97,7 @@ class SAML2Plugin(FormPluginBase):
|
||||
self.outstanding_queries = {}
|
||||
self.iam = os.uname()[1]
|
||||
|
||||
if cache:
|
||||
self.cache = Cache(cache)
|
||||
else:
|
||||
self.cache = Cache()
|
||||
|
||||
def _cache_session(self, session_info):
|
||||
name_id = session_info["name_id"]
|
||||
issuer = session_info["issuer"]
|
||||
del session_info["issuer"]
|
||||
self.cache.set(name_id, issuer, session_info,
|
||||
session_info["not_on_or_after"])
|
||||
return name_id
|
||||
self.users = Population(cache)
|
||||
|
||||
def _pick_idp(self, environ):
|
||||
"""
|
||||
@@ -234,7 +228,7 @@ class SAML2Plugin(FormPluginBase):
|
||||
|
||||
session_info = ar.session_info()
|
||||
# Cache it
|
||||
name_id = self._cache_session(session_info)
|
||||
name_id = self.users.add_information_about_person(session_info)
|
||||
if self.debug:
|
||||
self.log and self.log.info("stored %s with key %s" % (
|
||||
session_info, name_id))
|
||||
@@ -299,56 +293,6 @@ class SAML2Plugin(FormPluginBase):
|
||||
else:
|
||||
return None
|
||||
|
||||
def _vo_members_to_ask(self, subject_id):
|
||||
# Find the member of the Virtual Organization that I haven't
|
||||
# alrady spoken too
|
||||
vo_members = [
|
||||
member for member in self.metadata.vo_members(self.vorg)\
|
||||
if member not in self.srv["idp"].keys()]
|
||||
|
||||
self.log and self.log.info("VO members: %s" % vo_members)
|
||||
|
||||
# Remove the ones I have cached data from about this subject
|
||||
vo_members = [m for m in vo_members \
|
||||
if not self.cache.active(subject_id, m)]
|
||||
self.log and self.log.info(
|
||||
"VO members (not cached): %s" % vo_members)
|
||||
return vo_members
|
||||
|
||||
def _do_vo_aggregation(self, subject_id):
|
||||
|
||||
if self.log:
|
||||
self.log.info("** Do VO aggregation **")
|
||||
self.log.info("SubjectID: %s, VO:%s" % (subject_id, self.vorg))
|
||||
|
||||
vo_members = self._vo_members_to_ask(subject_id)
|
||||
|
||||
if vo_members:
|
||||
# Find the NameIDFormat and the SPNameQualifier
|
||||
if self.vorg_conf and "name_id_format" in self.vorg_conf:
|
||||
name_id_format = self.vorg_conf["name_id_format"]
|
||||
sp_name_qualifier = ""
|
||||
else:
|
||||
sp_name_qualifier = self.vorg
|
||||
name_id_format = ""
|
||||
|
||||
resolver = AttributeResolver(environ, self.metadata, self.conf)
|
||||
# extends returns a list of session_infos
|
||||
for session_info in resolver.extend(subject_id,
|
||||
self.conf["entityid"], vo_members,
|
||||
name_id_format=name_id_format,
|
||||
sp_name_qualifier=sp_name_qualifier,
|
||||
log=self.log):
|
||||
_ignore = self._cache_session(session_info)
|
||||
|
||||
self.log.info(
|
||||
">Issuers: %s" % self.cache.entities(subject_id))
|
||||
self.log.info(
|
||||
"AVA: %s" % (self.cache.get_identity(subject_id),))
|
||||
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
# IMetadataProvider
|
||||
def add_metadata(self, environ, identity):
|
||||
@@ -381,7 +325,7 @@ class SAML2Plugin(FormPluginBase):
|
||||
if "pysaml2_vo_expanded" not in identity:
|
||||
# is this a Virtual Organization situation
|
||||
if self.vorg:
|
||||
if self._do_vo_aggregation(subject_id):
|
||||
if self.vorg.do_vo_aggregation(subject_id):
|
||||
# Get the extended identity
|
||||
identity["user"] = self.cache.get_identity(subject_id)[0]
|
||||
# Only do this once, mark that the identity has been
|
||||
|
||||
@@ -31,6 +31,22 @@
|
||||
provides methods and functions to convert SAML classes to and from strings.
|
||||
"""
|
||||
|
||||
# try:
|
||||
# # lxml: best performance for XML processing
|
||||
# import lxml.etree as ET
|
||||
# except ImportError:
|
||||
# try:
|
||||
# # Python 2.5+: batteries included
|
||||
# import xml.etree.cElementTree as ET
|
||||
# except ImportError:
|
||||
# try:
|
||||
# # Python <2.5: standalone ElementTree install
|
||||
# import elementtree.cElementTree as ET
|
||||
# except ImportError:
|
||||
# raise ImportError, "lxml or ElementTree are not installed, "\
|
||||
# +"see http://codespeak.net/lxml "\
|
||||
# +"or http://effbot.org/zone/element-index.htm"
|
||||
|
||||
try:
|
||||
from xml.etree import cElementTree as ElementTree
|
||||
except ImportError:
|
||||
@@ -45,7 +61,7 @@ NAMESPACE = 'urn:oasis:names:tc:SAML:2.0:assertion'
|
||||
|
||||
NAMEID_FORMAT_EMAILADDRESS = (
|
||||
"urn:oasis:names:tc:SAML:2.0:nameid-format:emailAddress")
|
||||
URN_PASSWORD = "urn:oasis:names:tc:SAML:2.0:ac:classes:Password"
|
||||
|
||||
NAME_FORMAT_UNSPECIFIED = (
|
||||
"urn:oasis:names:tc:SAML:2.0:attrnam-format:unspecified")
|
||||
NAME_FORMAT_URI = "urn:oasis:names:tc:SAML:2.0:attrnam-format:uri"
|
||||
@@ -83,10 +99,10 @@ def create_class_from_xml_string(target_class, xml_string):
|
||||
not match those of the target class.
|
||||
"""
|
||||
tree = ElementTree.fromstring(xml_string)
|
||||
return _create_class_from_element_tree(target_class, tree)
|
||||
return create_class_from_element_tree(target_class, tree)
|
||||
|
||||
|
||||
def _create_class_from_element_tree(target_class, tree, namespace=None,
|
||||
def create_class_from_element_tree(target_class, tree, namespace=None,
|
||||
tag=None):
|
||||
"""Instantiates the class and populates members according to the tree.
|
||||
|
||||
@@ -411,6 +427,9 @@ class SamlBase(ExtensionContainer):
|
||||
|
||||
c_children = {}
|
||||
c_attributes = {}
|
||||
c_attribute_type = {}
|
||||
#c_attribute_use = {}
|
||||
c_attribute_required = {}
|
||||
c_child_order = []
|
||||
|
||||
def _get_all_c_children_with_order(self):
|
||||
@@ -433,11 +452,11 @@ class SamlBase(ExtensionContainer):
|
||||
if getattr(self, member_name) is None:
|
||||
setattr(self, member_name, [])
|
||||
getattr(self, member_name).append(
|
||||
_create_class_from_element_tree(
|
||||
create_class_from_element_tree(
|
||||
member_class[0], child_tree))
|
||||
else:
|
||||
setattr(self, member_name,
|
||||
_create_class_from_element_tree(member_class,
|
||||
create_class_from_element_tree(member_class,
|
||||
child_tree))
|
||||
else:
|
||||
ExtensionContainer._convert_element_tree_to_member(self,
|
||||
@@ -449,7 +468,7 @@ class SamlBase(ExtensionContainer):
|
||||
# Find the member of this class which corresponds to the XML
|
||||
# attribute(lookup in current_class.c_attributes) and set this
|
||||
# member to the desired value (using self.__dict__).
|
||||
setattr(self, self.__class__.c_attributes[attribute], value)
|
||||
setattr(self, self.__class__.c_attributes[attribute][0], value)
|
||||
else:
|
||||
# If it doesn't appear in the attribute list it's an extension
|
||||
ExtensionContainer._convert_element_attribute_to_member(self,
|
||||
@@ -470,8 +489,9 @@ class SamlBase(ExtensionContainer):
|
||||
else:
|
||||
member.become_child_element_of(tree)
|
||||
# Convert the members of this class which are XML attributes.
|
||||
for xml_attribute, member_name in \
|
||||
for xml_attribute, attribute_info in \
|
||||
self.__class__.c_attributes.iteritems():
|
||||
(member_name, member_type, required) = attribute_info
|
||||
member = getattr(self, member_name)
|
||||
if member is not None:
|
||||
tree.attrib[xml_attribute] = member
|
||||
@@ -514,7 +534,8 @@ class SamlBase(ExtensionContainer):
|
||||
def _init_attribute(self, extension_attribute_id,
|
||||
extension_attribute_name, value=None):
|
||||
|
||||
self.c_attributes[extension_attribute_id] = extension_attribute_name
|
||||
self.c_attributes[extension_attribute_id] = (extension_attribute_name,
|
||||
None, False)
|
||||
if value:
|
||||
self.__dict__[extension_attribute_name] = value
|
||||
|
||||
@@ -532,7 +553,7 @@ class SamlBase(ExtensionContainer):
|
||||
:return: list of keys
|
||||
"""
|
||||
keys = ['text']
|
||||
keys.extend(self.c_attributes.values())
|
||||
keys.extend([n for (n, t, r) in self.c_attributes.values()])
|
||||
keys.extend([v[1] for v in self.c_children.values()])
|
||||
return keys
|
||||
|
||||
@@ -592,7 +613,7 @@ class SamlBase(ExtensionContainer):
|
||||
:return: The instance
|
||||
"""
|
||||
|
||||
for prop in self.c_attributes.values():
|
||||
for prop, _typ, _req in self.c_attributes.values():
|
||||
#print "# %s" % (prop)
|
||||
if prop in ava:
|
||||
if isinstance(ava[prop], bool):
|
||||
@@ -641,7 +662,7 @@ def element_to_extension_element(element):
|
||||
exel = ExtensionElement(element.c_tag, element.c_namespace,
|
||||
text=element.text)
|
||||
|
||||
for xml_attribute, member_name in element.c_attributes.iteritems():
|
||||
for xml_attribute, (member_name, typ, req) in element.c_attributes.iteritems():
|
||||
member_value = getattr(element, member_name)
|
||||
if member_value is not None:
|
||||
exel.attributes[xml_attribute] = member_value
|
||||
|
||||
@@ -23,12 +23,14 @@ from saml2 import saml
|
||||
from saml2.time_util import instant, in_a_while
|
||||
from saml2.attribute_converter import from_local
|
||||
|
||||
from saml2.utils import sid, MissingValue
|
||||
from saml2.utils import args2dict
|
||||
from saml2.utils import assertion_factory
|
||||
from saml2.s_utils import sid, MissingValue
|
||||
from saml2.s_utils import factory
|
||||
from saml2.s_utils import assertion_factory
|
||||
from saml2.s_utils import do_attribute_statement
|
||||
|
||||
def _filter_values(vals, required=None, optional=None):
|
||||
""" Removes values from *val* that does not appear in *attributes*.
|
||||
""" Removes values from *vals* that does not appear in *required*
|
||||
or *optional*.
|
||||
|
||||
:param val: The values that are to be filtered
|
||||
:param required: The required values
|
||||
@@ -314,12 +316,13 @@ class Policy(object):
|
||||
return self.filter(ava, sp_entity_id, required, optional)
|
||||
|
||||
def conditions(self, sp_entity_id):
|
||||
return args2dict(
|
||||
return factory( saml.Conditions,
|
||||
not_before=instant(),
|
||||
# How long might depend on who's getting it
|
||||
not_on_or_after=self._not_on_or_after(sp_entity_id),
|
||||
audience_restriction=args2dict(
|
||||
audience=args2dict(sp_entity_id)))
|
||||
audience_restriction=[factory( saml.AudienceRestriction,
|
||||
audience=factory(saml.Audience,
|
||||
text=sp_entity_id))])
|
||||
|
||||
class Assertion(dict):
|
||||
""" Handles assertions about subjects """
|
||||
@@ -327,14 +330,28 @@ class Assertion(dict):
|
||||
def __init__(self, dic=None):
|
||||
dict.__init__(self, dic)
|
||||
|
||||
def _authn_statement(self):
|
||||
return args2dict(authn_instant=instant(), session_index=sid())
|
||||
def _authn_context(self, authn_class):
|
||||
return factory(saml.AuthnContext,
|
||||
authn_context_class_ref=factory(
|
||||
saml.AuthnContextClassRef, text=authn_class))
|
||||
|
||||
def _authn_statement(self, authn_class):
|
||||
if authn_class:
|
||||
return factory(saml.AuthnStatement,
|
||||
authn_instant=instant(),
|
||||
session_index=sid(),
|
||||
authn_context=self._authn_context(authn_class))
|
||||
else:
|
||||
return factory(saml.AuthnStatement,
|
||||
authn_instant=instant(),
|
||||
session_index=sid())
|
||||
|
||||
def construct(self, sp_entity_id, in_response_to, name_id, attrconvs,
|
||||
policy, issuer):
|
||||
policy, issuer, authn_class=None):
|
||||
|
||||
attr_statement = from_local(attrconvs, self,
|
||||
policy.get_name_form(sp_entity_id))
|
||||
attr_statement = saml.AttributeStatement(attribute=from_local(
|
||||
attrconvs, self,
|
||||
policy.get_name_form(sp_entity_id)))
|
||||
|
||||
# start using now and for a hour
|
||||
conds = policy.conditions(sp_entity_id)
|
||||
@@ -342,14 +359,15 @@ class Assertion(dict):
|
||||
return assertion_factory(
|
||||
issuer=issuer,
|
||||
attribute_statement = attr_statement,
|
||||
authn_statement = self._authn_statement(),
|
||||
authn_statement = self._authn_statement(authn_class),
|
||||
conditions = conds,
|
||||
subject=args2dict(
|
||||
subject=factory( saml.Subject,
|
||||
name_id=name_id,
|
||||
method=saml.SUBJECT_CONFIRMATION_METHOD_BEARER,
|
||||
subject_confirmation=args2dict(
|
||||
subject_confirmation_data = \
|
||||
args2dict(in_response_to=in_response_to))),
|
||||
subject_confirmation=factory( saml.SubjectConfirmation,
|
||||
subject_confirmation_data=factory(
|
||||
saml.SubjectConfirmationData,
|
||||
in_response_to=in_response_to))),
|
||||
)
|
||||
|
||||
def apply_policy(self, sp_entity_id, policy, metadata=None):
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
from saml2.utils import args2dict
|
||||
from saml2.s_utils import factory, do_ava
|
||||
from saml2 import saml
|
||||
from saml2.saml import NAME_FORMAT_URI
|
||||
|
||||
class UnknownNameFormat(Exception):
|
||||
@@ -64,7 +65,7 @@ def from_local(acs, ava, name_format):
|
||||
#print ac.format, name_format
|
||||
if aconv.name_format == name_format:
|
||||
#print "Found a name_form converter"
|
||||
return aconv.to(ava)
|
||||
return aconv.to_(ava)
|
||||
|
||||
return None
|
||||
|
||||
@@ -73,7 +74,7 @@ def from_local_name(acs, attr, name_format):
|
||||
:param acs: List of AttributeConverter instances
|
||||
:param attr: attribute name as string
|
||||
:param name_format: Which name-format it should be translated to
|
||||
:return: A dictionary suitable to feed to make_instance
|
||||
:return: An Attribute instance
|
||||
"""
|
||||
for aconv in acs:
|
||||
#print ac.format, name_format
|
||||
@@ -118,7 +119,7 @@ class AttributeConverter(object):
|
||||
self._to = eval(open(filename).read())
|
||||
|
||||
def adjust(self):
|
||||
if self._fro == None and self.to != None:
|
||||
if self._fro == None and self._to != None:
|
||||
self._fro = dict([(value, key) for key, value in self._to.items()])
|
||||
if self._to == None and self.fro != None:
|
||||
self._to = dict([(value, key) for key, value in self._fro.items()])
|
||||
@@ -184,10 +185,12 @@ class AttributeConverter(object):
|
||||
|
||||
def to_format(self, attr):
|
||||
try:
|
||||
return args2dict(name=self._to[attr], name_format=self.name_format,
|
||||
friendly_name=attr)
|
||||
return factory(saml.Attribute,
|
||||
name=self._to[attr],
|
||||
name_format=self.name_format,
|
||||
friendly_name=attr)
|
||||
except KeyError:
|
||||
return args2dict(name=attr)
|
||||
return factory(saml.Attribute, name=attr)
|
||||
|
||||
def from_format(self, attr):
|
||||
"""
|
||||
@@ -201,18 +204,18 @@ class AttributeConverter(object):
|
||||
pass
|
||||
return ""
|
||||
|
||||
def to(self, ava):
|
||||
def to_(self, attrvals):
|
||||
attributes = []
|
||||
for key, value in ava.items():
|
||||
for key, value in attrvals.items():
|
||||
try:
|
||||
attributes.append(args2dict(name=self._to[key],
|
||||
attributes.append(factory(saml.Attribute,
|
||||
name=self._to[key],
|
||||
name_format=self.name_format,
|
||||
friendly_name=key,
|
||||
attribute_value=value))
|
||||
attribute_value=do_ava(value)))
|
||||
except KeyError:
|
||||
# TODO
|
||||
# Should this be made different ???
|
||||
attributes.append(args2dict(name=key,
|
||||
attribute_value=value))
|
||||
attributes.append(factory(saml.Attribute,
|
||||
name=key,
|
||||
attribute_value=do_ava(value)))
|
||||
|
||||
return {"attribute": attributes}
|
||||
return attributes
|
||||
@@ -20,7 +20,6 @@ Contains classes and functions that a SAML2.0 Service Provider (SP) may use
|
||||
to do attribute aggregation.
|
||||
"""
|
||||
import saml2
|
||||
from saml2.client import Saml2Client
|
||||
|
||||
DEFAULT_BINDING = saml2.BINDING_HTTP_REDIRECT
|
||||
|
||||
@@ -32,7 +31,7 @@ class AttributeResolver(object):
|
||||
if saml2client:
|
||||
self.saml2client = saml2client
|
||||
else:
|
||||
self.saml2client = Saml2Client(environ, config)
|
||||
self.saml2client = saml2.client.Saml2Client(environ, config)
|
||||
|
||||
def extend(self, subject_id, issuer, vo_members, name_id_format=None,
|
||||
sp_name_qualifier=None, log=None):
|
||||
|
||||
@@ -31,6 +31,11 @@ from saml2.time_util import daylight_corrected_now
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class IncorrectlySigned(Exception):
|
||||
pass
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _use_on_or_after(condition, slack):
|
||||
now = daylight_corrected_now()
|
||||
#print "NOW: %d" % now
|
||||
@@ -57,23 +62,17 @@ def _use_before(condition, slack):
|
||||
return True
|
||||
|
||||
def for_me(condition, myself ):
|
||||
# Am I among the intended audiences
|
||||
for restriction in condition.audience_restriction:
|
||||
audience = restriction.audience
|
||||
if audience.text.strip() == myself:
|
||||
return True
|
||||
else:
|
||||
#print "Not for me: %s != %s" % (audience.text.strip(), myself)
|
||||
pass
|
||||
for audience in restriction.audience:
|
||||
if audience.text.strip() == myself:
|
||||
return True
|
||||
else:
|
||||
#print "Not for me: %s != %s" % (audience.text.strip(), myself)
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class IncorrectlySigned(Exception):
|
||||
pass
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def authn_response(conf, requestor, outstanding_queries=None, log=None,
|
||||
timeslack=0, debug=0):
|
||||
sec = security_context(conf)
|
||||
@@ -128,7 +127,6 @@ class AuthnResponse(object):
|
||||
self.response = self.sec.correctly_signed_response(decoded_xml)
|
||||
except Exception, excp:
|
||||
self.log and self.log.info("EXCEPTION: %s", excp)
|
||||
raise
|
||||
|
||||
if not self.response:
|
||||
if self.log:
|
||||
@@ -180,7 +178,7 @@ class AuthnResponse(object):
|
||||
self.not_on_or_after = _use_on_or_after(condition, self.timeslack)
|
||||
_use_before(condition, self.timeslack)
|
||||
except Exception, excp:
|
||||
self.log.error("Exception on condition: %s" % (excp,))
|
||||
self.log and self.log.error("Exception on condition: %s" % (excp,))
|
||||
if not lax:
|
||||
raise
|
||||
else:
|
||||
@@ -230,6 +228,7 @@ class AuthnResponse(object):
|
||||
# The subject must contain a name_id
|
||||
assert subject.name_id
|
||||
self.name_id = subject.name_id.text.strip()
|
||||
return self.name_id
|
||||
|
||||
def _assertion(self, assertion):
|
||||
self.assertion = assertion
|
||||
@@ -294,7 +293,7 @@ class AuthnResponse(object):
|
||||
return True
|
||||
|
||||
def verify(self):
|
||||
""" Verify that the assertion is syntaktically correct and
|
||||
""" Verify that the assertion is syntactically correct and
|
||||
the signature is correct if present."""
|
||||
|
||||
self.status_ok()
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import shelve
|
||||
import time
|
||||
from saml2 import time_util
|
||||
|
||||
# The assumption is that any subject may consist of data
|
||||
# gathered from several different sources, all with their own
|
||||
@@ -19,6 +20,9 @@ class Cache(object):
|
||||
self._db = {}
|
||||
self._sync = False
|
||||
|
||||
def delete(self, subject_id):
|
||||
del self._db[subject_id]
|
||||
|
||||
def get_identity(self, subject_id, entities=None):
|
||||
""" Get all the identity information that has been received and
|
||||
are still valid about the subject.
|
||||
@@ -53,24 +57,27 @@ class Cache(object):
|
||||
return (res, oldees)
|
||||
|
||||
def get(self, subject_id, entity_id):
|
||||
""" Get seesion information about a the session when an
|
||||
assertion was received from an IdP or an AA or sent to a SP.
|
||||
""" Get session information about a subject gotten from a
|
||||
specified IdP.
|
||||
|
||||
:param subject_id: The identifier of the subject
|
||||
:param entity_id: The identifier of the entity_id
|
||||
:return: The session information
|
||||
"""
|
||||
(not_on_or_after, info) = self._db[subject_id][entity_id]
|
||||
now = time.gmtime()
|
||||
if isinstance(not_on_or_after, time.struct_time):
|
||||
not_on_or_after = time.mktime(not_on_or_after)
|
||||
now = time_util.daylight_corrected_now()
|
||||
|
||||
if not_on_or_after < now:
|
||||
self.reset(subject_id, entity_id)
|
||||
raise ToOld()
|
||||
#self.reset(subject_id, entity_id)
|
||||
raise ToOld("%s < %s" % (not_on_or_after, now))
|
||||
else:
|
||||
return info
|
||||
|
||||
def set( self, subject_id, entity_id, info, not_on_or_after=0):
|
||||
""" Stores session information in the cache
|
||||
""" Stores session information in the cache. Assumes that the subject_id
|
||||
is unique within the context of the Service Provider.
|
||||
|
||||
:param subject_id: The subjects identifier
|
||||
:param entity_id: The identifier of the entity_id/receiver of an
|
||||
|
||||
@@ -25,14 +25,16 @@ import saml2
|
||||
import base64
|
||||
|
||||
from saml2.time_util import instant
|
||||
from saml2.utils import sid, deflate_and_base64_encode
|
||||
from saml2.utils import do_attributes, args2dict
|
||||
from saml2.s_utils import sid, deflate_and_base64_encode
|
||||
from saml2.s_utils import do_attributes, factory
|
||||
|
||||
from saml2 import samlp, saml
|
||||
from saml2 import VERSION, make_instance
|
||||
from saml2 import samlp, saml, class_name
|
||||
from saml2 import VERSION
|
||||
from saml2.sigver import pre_signature_part
|
||||
from saml2.sigver import security_context, signed_instance_factory
|
||||
from saml2.soap import SOAPClient
|
||||
from saml2.population import Population
|
||||
from saml2.virtual_org import VirtualOrg
|
||||
|
||||
from saml2.authnresponse import authn_response
|
||||
|
||||
@@ -46,19 +48,32 @@ FORM_SPEC = """<form method="post" action="%s">
|
||||
|
||||
LAX = False
|
||||
|
||||
class IdpUnspecified(Exception):
|
||||
pass
|
||||
|
||||
class VerifyError(Exception):
|
||||
pass
|
||||
|
||||
class Saml2Client(object):
|
||||
""" The basic pySAML2 service provider class """
|
||||
|
||||
def __init__(self, environ, config=None, debug=0):
|
||||
def __init__(self, environ, config=None, debug=0, vorg=None,
|
||||
persistent_cache=None):
|
||||
"""
|
||||
:param environ:
|
||||
:param config: A saml2.config.Config instance
|
||||
"""
|
||||
self.environ = environ
|
||||
self.vorg = None
|
||||
self.users = Population(persistent_cache)
|
||||
if config:
|
||||
self.config = config
|
||||
if "metadata" in config:
|
||||
self.metadata = config["metadata"]
|
||||
if vorg:
|
||||
self.vorg = VirtualOrg(self.metadata, vorg,
|
||||
self.users.cache,
|
||||
log=None, vorg_conf=None)
|
||||
self.sec = security_context(config)
|
||||
|
||||
self.debug = debug
|
||||
@@ -71,32 +86,23 @@ class Saml2Client(object):
|
||||
return request
|
||||
|
||||
def idp_entry(self, name=None, location=None, provider_id=None):
|
||||
res = {}
|
||||
res = samlp.IDPEntry()
|
||||
if name:
|
||||
res["name"] = name
|
||||
res.name = name
|
||||
if location:
|
||||
res["loc"] = location
|
||||
res.loc = location
|
||||
if provider_id:
|
||||
res["provider_id"] = provider_id
|
||||
if res:
|
||||
return res
|
||||
else:
|
||||
return None
|
||||
res.provider_id = provider_id
|
||||
|
||||
def scoping(self, idp_ents):
|
||||
return {
|
||||
"idp_list": {
|
||||
"idp_entry": idp_ents
|
||||
}
|
||||
}
|
||||
return res
|
||||
|
||||
def scoping_from_metadata(self, entityid, location):
|
||||
def scoping_from_metadata(self, entityid, location=None):
|
||||
name = self.metadata.name(entityid)
|
||||
return make_instance(samlp.Scoping,
|
||||
self.scoping([self.idp_entry(name, location)]))
|
||||
idp_ent = self.idp_entry(name, location)
|
||||
return samlp.Scoping(idp_list=samlp.IDPList(idp_entry=[idp_ent]))
|
||||
|
||||
def response(self, post, requestor, outstanding, log=None):
|
||||
""" Deal with the AuthnResponse
|
||||
""" Deal with an AuthnResponse
|
||||
|
||||
:param post: The reply as a dictionary
|
||||
:param requestor: The issuer of the AuthN request
|
||||
@@ -104,10 +110,8 @@ class Saml2Client(object):
|
||||
the original web request from the user before redirection
|
||||
as values.
|
||||
:param log: where loggin should go.
|
||||
:return: A 2-tuple of identity information (in the form of a
|
||||
dictionary) and where the user should really be sent. This
|
||||
might differ from what the IdP thinks since I don't want
|
||||
to reveal verything to it and it might not trust me.
|
||||
:return: An authnresponse.AuthnResponse instance which among other
|
||||
things contains a verified saml2.AuthnResponse instance.
|
||||
"""
|
||||
# If the request contains a samlResponse, try to validate it
|
||||
try:
|
||||
@@ -115,15 +119,18 @@ class Saml2Client(object):
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
aresp = None
|
||||
if saml_response:
|
||||
aresp = authn_response(self.config, requestor, outstanding, log,
|
||||
debug=self.debug)
|
||||
aresp.loads(saml_response)
|
||||
if self.debug:
|
||||
log and log.info(aresp)
|
||||
return aresp.verify()
|
||||
aresp = aresp.verify()
|
||||
if aresp:
|
||||
self.users.add_information_about_person(aresp.session_info())
|
||||
|
||||
return None
|
||||
return aresp
|
||||
|
||||
def authn_request(self, query_id, destination, service_url, spentityid,
|
||||
my_name, vorg="", scoping=None, log=None, sign=False):
|
||||
@@ -139,45 +146,78 @@ class Saml2Client(object):
|
||||
:param log: A service to which logs should be written
|
||||
:param sign: Whether the request should be signed or not.
|
||||
"""
|
||||
prel = {
|
||||
"id": query_id,
|
||||
"version": VERSION,
|
||||
"issue_instant": instant(),
|
||||
"destination": destination,
|
||||
"assertion_consumer_service_url": service_url,
|
||||
"protocol_binding": saml2.BINDING_HTTP_POST,
|
||||
"provider_name": my_name,
|
||||
}
|
||||
request = samlp.AuthnRequest(
|
||||
id= query_id,
|
||||
version= VERSION,
|
||||
issue_instant= instant(),
|
||||
destination= destination,
|
||||
assertion_consumer_service_url= service_url,
|
||||
protocol_binding= saml2.BINDING_HTTP_POST,
|
||||
provider_name= my_name
|
||||
)
|
||||
|
||||
if scoping:
|
||||
prel["scoping"] = scoping
|
||||
request.scoping = scoping
|
||||
|
||||
name_id_policy = {
|
||||
"allow_create": "true"
|
||||
}
|
||||
# Profile stuff, should be configurable
|
||||
name_id_policy = samlp.NameIDPolicy(allow_create="true",
|
||||
format=saml.NAMEID_FORMAT_TRANSIENT)
|
||||
|
||||
name_id_policy["format"] = saml.NAMEID_FORMAT_TRANSIENT
|
||||
if vorg:
|
||||
try:
|
||||
name_id_policy["sp_name_qualifier"] = vorg
|
||||
name_id_policy["format"] = saml.NAMEID_FORMAT_PERSISTENT
|
||||
name_id_policy.sp_name_qualifier = vorg
|
||||
name_id_policy.format = saml.NAMEID_FORMAT_PERSISTENT
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
if sign:
|
||||
prel["signature"] = pre_signature_part(prel["id"],
|
||||
self.sec.my_cert, id=1)
|
||||
request.signature = pre_signature_part(request.id,
|
||||
self.sec.my_cert, 1)
|
||||
to_sign = [(class_name(request), request.id)]
|
||||
else:
|
||||
to_sign = []
|
||||
|
||||
prel["name_id_policy"] = name_id_policy
|
||||
prel["issuer"] = { "text": spentityid }
|
||||
request.name_id_policy = name_id_policy
|
||||
request.issuer = factory(saml.Issuer, text=spentityid )
|
||||
|
||||
if log:
|
||||
log.info("DICT VERSION: %s" % prel)
|
||||
log.info("REQUEST: %s" % request)
|
||||
|
||||
return "%s" % signed_instance_factory(samlp.AuthnRequest, prel,
|
||||
self.sec)
|
||||
return "%s" % signed_instance_factory(request, self.sec, to_sign)
|
||||
|
||||
def authenticate(self, spentityid, location="", service_url="",
|
||||
def issuer(self):
|
||||
""" Return an Issuer instance """
|
||||
return saml.Issuer(text=self.config["entityid"],
|
||||
format=saml.NAMEID_FORMAT_ENTITY)
|
||||
|
||||
def _spentityid(self, spentityid=None):
|
||||
if self.config:
|
||||
return self.config["entityid"]
|
||||
else:
|
||||
return spentityid
|
||||
|
||||
def _location(self, location=None):
|
||||
if not location :
|
||||
# get the idp location from the configuration alternative the metadata
|
||||
# If there is more than one IdP in the configuration raise exception
|
||||
urls = self.config.idps()
|
||||
if len(urls) > 1:
|
||||
raise IdpUnspecified("Too many IdPs to choose from: %s" % urls)
|
||||
return urls[0]
|
||||
else:
|
||||
return location
|
||||
|
||||
def _service_url(self, url=None):
|
||||
if not url:
|
||||
return self.config.endpoint("sp", "assertion_consumer_service")[0]
|
||||
|
||||
def _my_name(self, name=None):
|
||||
if not name:
|
||||
return self.config["service"]["sp"]["name"]
|
||||
else:
|
||||
return name
|
||||
|
||||
def authenticate(self, spentityid=None, location="", service_url="",
|
||||
my_name="", relay_state="",
|
||||
binding=saml2.BINDING_HTTP_REDIRECT, log=None,
|
||||
vorg="", scoping=None):
|
||||
@@ -199,11 +239,17 @@ class Saml2Client(object):
|
||||
:return: AuthnRequest response
|
||||
"""
|
||||
|
||||
spentityid = self._spentityid(spentityid)
|
||||
location = self._location(location)
|
||||
service_url = self._service_url(service_url)
|
||||
my_name = self._my_name(my_name)
|
||||
|
||||
if log:
|
||||
log.info("spentityid: %s" % spentityid)
|
||||
log.info("location: %s" % location)
|
||||
log.info("service_url: %s" % service_url)
|
||||
log.info("my_name: %s" % my_name)
|
||||
|
||||
session_id = sid()
|
||||
authen_req = self.authn_request(session_id, location,
|
||||
service_url, spentityid, my_name, vorg,
|
||||
@@ -229,7 +275,8 @@ class Saml2Client(object):
|
||||
lista = ["SAMLRequest=%s" % urllib.quote_plus(
|
||||
deflate_and_base64_encode(
|
||||
authen_req)),
|
||||
"spentityid=%s" % spentityid]
|
||||
#"spentityid=%s" % spentityid
|
||||
]
|
||||
if relay_state:
|
||||
lista.append("RelayState=%s" % relay_state)
|
||||
login_url = "?".join([location, "&".join(lista)])
|
||||
@@ -263,35 +310,35 @@ class Saml2Client(object):
|
||||
"""
|
||||
|
||||
|
||||
subject = args2dict(
|
||||
name_id = args2dict(subject_id, format=nameid_format,
|
||||
subject = saml.Subject(
|
||||
name_id = saml.NameID(
|
||||
text=subject_id,
|
||||
format=nameid_format,
|
||||
sp_name_qualifier=sp_name_qualifier,
|
||||
name_qualifier=name_qualifier),
|
||||
)
|
||||
|
||||
prequery = {
|
||||
"id": session_id,
|
||||
"version": VERSION,
|
||||
"issue_instant": instant(),
|
||||
"destination": destination,
|
||||
"issuer": issuer,
|
||||
"subject":subject,
|
||||
}
|
||||
query = samlp.AttributeQuery(
|
||||
id=session_id,
|
||||
version=VERSION,
|
||||
issue_instant=instant(),
|
||||
destination=destination,
|
||||
issuer=self.issuer(),
|
||||
subject=subject,
|
||||
)
|
||||
|
||||
if sign:
|
||||
prequery["signature"] = pre_signature_part(prequery["id"],
|
||||
self.sec.my_cert, 1)
|
||||
query.signature = pre_signature_part(query.id, self.sec.my_cert, 1)
|
||||
|
||||
if attribute:
|
||||
prequery["attribute"] = do_attributes(attribute)
|
||||
query.attribute = do_attributes(attribute)
|
||||
|
||||
request = make_instance(samlp.AttributeQuery, prequery)
|
||||
if sign:
|
||||
signed_req = self.sec.sign_assertion_using_xmlsec("%s" % request)
|
||||
return samlp.attribute_query_from_string(signed_req)
|
||||
signed_query = self.sec.sign_assertion_using_xmlsec("%s" % query)
|
||||
return samlp.attribute_query_from_string(signed_query)
|
||||
|
||||
else:
|
||||
return request
|
||||
return query
|
||||
|
||||
|
||||
def attribute_query(self, subject_id, issuer, destination,
|
||||
@@ -334,14 +381,17 @@ class Saml2Client(object):
|
||||
aresp = authn_response(self.config, issuer, {session_id:""}, log)
|
||||
session_info = aresp.loads(response).verify().session_info()
|
||||
|
||||
if session_info:
|
||||
self.users.add_information_about_person(session_info)
|
||||
|
||||
log and log.info("session: %s" % session_info)
|
||||
return session_info
|
||||
else:
|
||||
log and log.info("No response")
|
||||
return None
|
||||
|
||||
def make_logout_request(self, session_id, destination, issuer,
|
||||
reason=None, not_on_or_after=None):
|
||||
def make_logout_requests(self, subject_id, reason=None,
|
||||
not_on_or_after=None):
|
||||
""" Constructs a LogoutRequest
|
||||
|
||||
:param subject_id: The identifier of the subject
|
||||
@@ -349,31 +399,67 @@ class Saml2Client(object):
|
||||
form of a URI reference.
|
||||
:param not_on_or_after: The time at which the request expires,
|
||||
after which the recipient may discard the message.
|
||||
:return: An AttributeQuery instance
|
||||
:return: A LogoutRequest instance
|
||||
"""
|
||||
|
||||
prel = {
|
||||
"id": sid(),
|
||||
"version": VERSION,
|
||||
"issue_instant": instant(),
|
||||
"destination": destination,
|
||||
"issuer": issuer,
|
||||
"session_index": session_id,
|
||||
}
|
||||
result = []
|
||||
|
||||
if reason:
|
||||
prel["reason"] = reason
|
||||
for entity_id in self.users.issuers_of_info(subject_id):
|
||||
destination = self.config["service"]["sp"]["idp"][entity_id]
|
||||
|
||||
if not_on_or_after:
|
||||
prel["not_on_or_after"] = not_on_or_after
|
||||
# create NameID from subject_id
|
||||
name_id = NameID(
|
||||
text=self.client.users.get_entityid(subject_id,
|
||||
entity_id)["name_id"])
|
||||
|
||||
return make_instance(samlp.LogoutRequest, prel)
|
||||
request = samlp.LogoutRequest(
|
||||
id=sid(),
|
||||
version=VERSION,
|
||||
issue_instant=instant(),
|
||||
destination=destination,
|
||||
issuer=self.issuer(),
|
||||
session_index=session_id,
|
||||
name_id = name_id
|
||||
)
|
||||
|
||||
def logout(self, session_id, destination,
|
||||
issuer, reason="", not_on_or_after=None):
|
||||
return self.make_logout_request(session_id, destination,
|
||||
issuer, reason, not_on_or_after)
|
||||
|
||||
if reason:
|
||||
request.reason = reason
|
||||
|
||||
if not_on_or_after:
|
||||
request.not_on_or_after = not_on_or_after
|
||||
|
||||
result.append(request)
|
||||
|
||||
return result
|
||||
|
||||
def global_logout(self, subject_id, reason="", not_on_or_after=None):
|
||||
requests = self.make_logout_requests(subject_id, reason,
|
||||
not_on_or_after)
|
||||
return [r.id for r in requests]
|
||||
|
||||
def local_logout(self, subject_id, reason="", not_on_or_after=None):
|
||||
# Remove the user from the cache, equals local logout
|
||||
self.users.remove_person(subject_id)
|
||||
return True
|
||||
|
||||
|
||||
def add_vo_information_about_user(self, subject_id):
|
||||
""" Add information to the knowledge I have about the user """
|
||||
try:
|
||||
(ava, _) = self.users.get_identity(subject_id)
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
# is this a Virtual Organization situation
|
||||
if self.vorg:
|
||||
if self.vorg.do_vo_aggregation(subject_id):
|
||||
# Get the extended identity
|
||||
ava = self.users.get_identity(subject_id)[0]
|
||||
return ava
|
||||
|
||||
def is_session_valid(session_id):
|
||||
return True
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -43,11 +43,11 @@ class Config(dict):
|
||||
assert "idp" in config
|
||||
assert len(config["idp"]) > 0
|
||||
|
||||
assert "url" in config
|
||||
assert "endpoints" in config
|
||||
assert "name" in config
|
||||
|
||||
def _idp_aa_check(self, config):
|
||||
assert "url" in config
|
||||
assert "endpoints" in config
|
||||
if "assertions" in config:
|
||||
config["policy"] = Policy(config["assertions"])
|
||||
del config["assertions"]
|
||||
@@ -95,6 +95,7 @@ class Config(dict):
|
||||
config["metadata"] = self.load_metadata(config["metadata"],
|
||||
config["xmlsec_binary"],
|
||||
config["attrconverters"])
|
||||
self.metadata = config["metadata"]
|
||||
|
||||
if "sp" in config["service"]:
|
||||
#print config["service"]["sp"]
|
||||
@@ -130,11 +131,11 @@ class Config(dict):
|
||||
except KeyError:
|
||||
return Policy()
|
||||
|
||||
def aa_url(self):
|
||||
return self["service"]["aa"]["url"]
|
||||
|
||||
def idp_url(self):
|
||||
return self["service"]["idp"]["url"]
|
||||
def endpoint(self, typ, service):
|
||||
try:
|
||||
return self["service"][typ]["endpoints"][service]
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
def vo_conf(self, name):
|
||||
return self["virtual_organization"][name]
|
||||
@@ -142,3 +143,24 @@ class Config(dict):
|
||||
def attribute_converters(self):
|
||||
return self["attrconverters"]
|
||||
|
||||
def idps(self):
|
||||
""" Returns a list of URLs of the IdP this SP can
|
||||
use according to the configuration"""
|
||||
|
||||
try:
|
||||
return [u for u in self["service"]["sp"]["idp"].values()]
|
||||
except KeyError:
|
||||
return []
|
||||
|
||||
def is_wayf_needed(self):
|
||||
if len(self["service"]["sp"]["idp"]) > 1:
|
||||
return True
|
||||
else: # not really true, what if it's zero (0)
|
||||
return False
|
||||
|
||||
def get_available_idps(self):
|
||||
lista = []
|
||||
for eid, url in self["service"]["sp"]["idp"].items():
|
||||
namn = self.metadata.name(eid)
|
||||
lista.append((eid, namn))
|
||||
return lista
|
||||
2824
src/saml2/md.py
2824
src/saml2/md.py
File diff suppressed because it is too large
Load Diff
@@ -88,7 +88,7 @@ class MetaData(object):
|
||||
:param entity_descriptor: A EntityDescriptor instance
|
||||
"""
|
||||
try:
|
||||
ssd = entity_descriptor.sp_sso_descriptor
|
||||
ssd = entity_descriptor.spsso_descriptor
|
||||
except AttributeError:
|
||||
return
|
||||
|
||||
@@ -141,7 +141,7 @@ class MetaData(object):
|
||||
:param entity_descriptor: A EntityDescriptor instance
|
||||
"""
|
||||
try:
|
||||
isd = entity_descriptor.idp_sso_descriptor
|
||||
isd = entity_descriptor.idpsso_descriptor
|
||||
except AttributeError:
|
||||
return
|
||||
|
||||
@@ -280,7 +280,7 @@ class MetaData(object):
|
||||
except AttributeError:
|
||||
pass
|
||||
try:
|
||||
entity["contact"] = entity_descriptor.contact
|
||||
entity["contact_person"] = entity_descriptor.contact_person
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
@@ -394,22 +394,25 @@ class MetaData(object):
|
||||
:param entityid: The Entity ID
|
||||
:return: A name
|
||||
"""
|
||||
name = ""
|
||||
|
||||
try:
|
||||
org = self.entity[entity_id]["organization"]
|
||||
try:
|
||||
names = org.organization_display_name
|
||||
except KeyError:
|
||||
for org in self.entity[entity_id]["organization"]:
|
||||
try:
|
||||
names = org.organization_name
|
||||
except KeyError:
|
||||
name = org.organization_display_name[0]
|
||||
except IndexError:
|
||||
try:
|
||||
names = org.organization_url
|
||||
except KeyError:
|
||||
names = None
|
||||
if names:
|
||||
name = names[0].text
|
||||
name = org.organization_name[0]
|
||||
except IndexError:
|
||||
try:
|
||||
name = org.organization_url[0]
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
if name:
|
||||
name = name.text
|
||||
except KeyError:
|
||||
name = ""
|
||||
pass
|
||||
|
||||
return name
|
||||
|
||||
|
||||
2107
src/saml2/saml.py
2107
src/saml2/saml.py
File diff suppressed because it is too large
Load Diff
2506
src/saml2/samlp.py
2506
src/saml2/samlp.py
File diff suppressed because it is too large
Load Diff
@@ -24,13 +24,13 @@ import sys
|
||||
|
||||
from saml2 import saml, samlp, VERSION, class_name
|
||||
|
||||
from saml2.utils import sid, decode_base64_and_inflate
|
||||
from saml2.utils import response_factory
|
||||
from saml2.utils import MissingValue, args2dict
|
||||
from saml2.utils import success_status_factory
|
||||
from saml2.utils import OtherError
|
||||
from saml2.utils import VersionMismatch, UnknownPrincipal, UnsupportedBinding
|
||||
from saml2.utils import status_from_exception_factory
|
||||
from saml2.s_utils import sid, decode_base64_and_inflate
|
||||
from saml2.s_utils import response_factory
|
||||
from saml2.s_utils import MissingValue, factory
|
||||
from saml2.s_utils import success_status_factory
|
||||
from saml2.s_utils import OtherError
|
||||
from saml2.s_utils import VersionMismatch, UnknownPrincipal, UnsupportedBinding
|
||||
from saml2.s_utils import status_from_exception_factory
|
||||
|
||||
from saml2.sigver import security_context, signed_instance_factory
|
||||
from saml2.sigver import pre_signature_part
|
||||
@@ -105,8 +105,11 @@ class Identifier(object):
|
||||
except KeyError:
|
||||
nameid_format = saml.NAMEID_FORMAT_PERSISTENT
|
||||
|
||||
return args2dict(subj_id, format=nameid_format,
|
||||
sp_name_qualifier=sp_name_qualifier)
|
||||
return saml.NameID(format=nameid_format,
|
||||
sp_name_qualifier=sp_name_qualifier,
|
||||
text=subj_id)
|
||||
# return args2dict(subj_id, format=nameid_format,
|
||||
# sp_name_qualifier=sp_name_qualifier)
|
||||
|
||||
def persistent_nameid(self, sp_name_qualifier, userid):
|
||||
""" Get or create a persistent identifier for this object to be used
|
||||
@@ -117,8 +120,18 @@ class Identifier(object):
|
||||
:return: A persistent random identifier.
|
||||
"""
|
||||
subj_id = self.persistent(sp_name_qualifier, userid)
|
||||
return args2dict(subj_id, format=saml.NAMEID_FORMAT_PERSISTENT,
|
||||
sp_name_qualifier=sp_name_qualifier)
|
||||
return saml.NameID(format=saml.NAMEID_FORMAT_PERSISTENT,
|
||||
sp_name_qualifier=sp_name_qualifier,
|
||||
text=subj_id)
|
||||
|
||||
# return args2dict(subj_id, format=saml.NAMEID_FORMAT_PERSISTENT,
|
||||
# sp_name_qualifier=sp_name_qualifier)
|
||||
|
||||
def temporary_nameid(self):
|
||||
""" Returns a random one-time identifier """
|
||||
return saml.NameID(format=saml.NAMEID_FORMAT_TRANSIENT,
|
||||
text=sid())
|
||||
#return args2dict(sid(), format=saml.NAMEID_FORMAT_TRANSIENT)
|
||||
|
||||
def construct_nameid(self, local_policy, userid, sp_entity_id,
|
||||
identity=None, name_id_policy=None):
|
||||
@@ -143,9 +156,6 @@ class Identifier(object):
|
||||
elif nameid_format == saml.NAMEID_FORMAT_TRANSIENT:
|
||||
return self.temporary_nameid()
|
||||
|
||||
def temporary_nameid(self):
|
||||
""" Returns a random one-time identifier """
|
||||
return args2dict(sid(), format=saml.NAMEID_FORMAT_TRANSIENT)
|
||||
|
||||
|
||||
class Server(object):
|
||||
@@ -181,10 +191,9 @@ class Server(object):
|
||||
|
||||
def issuer(self):
|
||||
""" Return an Issuer precursor """
|
||||
return args2dict( self.conf["entityid"],
|
||||
return saml.Issuer(text=self.conf["entityid"],
|
||||
format=saml.NAMEID_FORMAT_ENTITY)
|
||||
|
||||
|
||||
def parse_authn_request(self, enc_request):
|
||||
"""Parse a Authentication Request
|
||||
|
||||
@@ -298,6 +307,8 @@ class Server(object):
|
||||
:return: A Response instance
|
||||
"""
|
||||
|
||||
to_sign = []
|
||||
|
||||
if not status:
|
||||
status = success_status_factory()
|
||||
|
||||
@@ -323,19 +334,21 @@ class Server(object):
|
||||
policy, issuer=_issuer)
|
||||
|
||||
if sign:
|
||||
assertion["signature"] = pre_signature_part(assertion["id"],
|
||||
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"],
|
||||
self.cache.set(assertion.subject.name_id.text,
|
||||
sp_entity_id, assertion,
|
||||
assertion["conditions"]["not_on_or_after"])
|
||||
assertion.conditions.not_on_or_after)
|
||||
|
||||
response.update({"assertion":assertion})
|
||||
response.assertion = assertion
|
||||
|
||||
return signed_instance_factory(samlp.Response, response, self.sec)
|
||||
return signed_instance_factory(response, self.sec, to_sign)
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
Based on the use of xmlsec1 binaries and not the python xmlsec module.
|
||||
"""
|
||||
|
||||
from saml2 import samlp, class_name, saml, make_instance
|
||||
from saml2 import samlp, class_name, saml, ExtensionElement
|
||||
from saml2 import create_class_from_xml_string
|
||||
import xmldsig as ds
|
||||
from tempfile import NamedTemporaryFile
|
||||
@@ -27,12 +27,11 @@ from subprocess import Popen, PIPE
|
||||
import base64
|
||||
import random
|
||||
import os
|
||||
import copy
|
||||
|
||||
def get_xmlsec_binary():
|
||||
for path in os.environ["PATH"].split(":"):
|
||||
fil = os.path.join(path, "xmlsec1")
|
||||
if os.access(fil,os.X_OK):
|
||||
if os.access(fil, os.X_OK):
|
||||
return fil
|
||||
|
||||
raise Exception("Can't find xmlsec1")
|
||||
@@ -86,12 +85,12 @@ def _make_vals(val, klass, seccont, klass_inst=None, prop=None, part=False,
|
||||
#print "make_vals(%s, %s)" % (val, klass)
|
||||
|
||||
if isinstance(val, dict):
|
||||
cinst = _instance(klass, val, seccont,base64encode=base64encode,
|
||||
cinst = _instance(klass, val, seccont, base64encode=base64encode,
|
||||
elements_to_sign=elements_to_sign)
|
||||
else:
|
||||
try:
|
||||
cinst = klass().set_text(val)
|
||||
except ValueError, excp:
|
||||
except ValueError:
|
||||
if not part:
|
||||
cis = [_make_vals(sval, klass, seccont, klass_inst, prop,
|
||||
True, base64encode, elements_to_sign) for sval in val]
|
||||
@@ -150,16 +149,12 @@ def _instance(klass, ava, seccont, base64encode=False, elements_to_sign=None):
|
||||
|
||||
return instance
|
||||
|
||||
def signed_instance_factory(klass, ava, seccont, base64encode=False):
|
||||
elements_to_sign = []
|
||||
instance = _instance(klass, ava, seccont, base64encode=False,
|
||||
elements_to_sign=elements_to_sign)
|
||||
|
||||
def signed_instance_factory(instance, seccont, elements_to_sign=None):
|
||||
if elements_to_sign:
|
||||
signed_xml = "%s" % instance
|
||||
for (node_name, nodeid) in elements_to_sign:
|
||||
signed_xml = seccont.sign_statement_using_xmlsec(signed_xml,
|
||||
class_name=node_name, nodeid=nodeid)
|
||||
klass_namn=node_name, nodeid=nodeid)
|
||||
|
||||
#print "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||
#print "%s" % signed_xml
|
||||
@@ -274,7 +269,7 @@ def verify_signature(enctext, xmlsec_binary, cert_file=None, cert_type="pem",
|
||||
com_list.append("--store-signatures")
|
||||
|
||||
if node_id:
|
||||
com_list.extend(["--node-id",node_id])
|
||||
com_list.extend(["--node-id", node_id])
|
||||
|
||||
com_list.append(fil)
|
||||
|
||||
@@ -351,6 +346,7 @@ class SecurityContext(object):
|
||||
|
||||
# Your private key
|
||||
self.key_file = key_file
|
||||
self.key_type = key_type
|
||||
|
||||
# Your certificate
|
||||
self.cert_file = cert_file
|
||||
@@ -379,7 +375,7 @@ class SecurityContext(object):
|
||||
ntf = NamedTemporaryFile()
|
||||
|
||||
com_list = [self.xmlsec, "--decrypt",
|
||||
"--privkey-pem", key_file,
|
||||
"--privkey-pem", self.key_file,
|
||||
"--output", ntf.name,
|
||||
"--id-attr:%s" % ID_ATTR,
|
||||
ENC_NODE_NAME, fil]
|
||||
@@ -503,11 +499,11 @@ class SecurityContext(object):
|
||||
|
||||
return response
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
#--------------------------------------------------------------------------
|
||||
# SIGNATURE PART
|
||||
#----------------------------------------------------------------------------
|
||||
#--------------------------------------------------------------------------
|
||||
|
||||
def sign_statement_using_xmlsec(self, statement, class_name, key=None,
|
||||
def sign_statement_using_xmlsec(self, statement, klass_namn, key=None,
|
||||
key_file=None, nodeid=None):
|
||||
"""Sign a SAML statement using xmlsec.
|
||||
|
||||
@@ -530,11 +526,11 @@ class SecurityContext(object):
|
||||
com_list = [self.xmlsec, "--sign",
|
||||
"--output", ntf.name,
|
||||
"--privkey-pem", key_file,
|
||||
"--id-attr:%s" % ID_ATTR, class_name,
|
||||
"--id-attr:%s" % ID_ATTR, klass_namn,
|
||||
#"--store-signatures"
|
||||
]
|
||||
if nodeid:
|
||||
com_list.extend(["--node-id",nodeid])
|
||||
com_list.extend(["--node-id", nodeid])
|
||||
|
||||
com_list.append(fil)
|
||||
|
||||
@@ -543,9 +539,9 @@ class SecurityContext(object):
|
||||
# this doesn't work if --store-signatures are used
|
||||
if out == "":
|
||||
#print " ".join(com_list)
|
||||
#print ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
|
||||
#print ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
|
||||
#print out
|
||||
#print ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
|
||||
#print ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
|
||||
ntf.seek(0)
|
||||
return ntf.read()
|
||||
else:
|
||||
@@ -568,39 +564,65 @@ class SecurityContext(object):
|
||||
|
||||
# ===========================================================================
|
||||
|
||||
PRE_SIGNATURE = {
|
||||
"signed_info": {
|
||||
"signature_method": {
|
||||
"algorithm": ds.SIG_RSA_SHA1
|
||||
},
|
||||
"canonicalization_method": {
|
||||
"algorithm": ds.ALG_EXC_C14N
|
||||
},
|
||||
"reference": {
|
||||
# must be replace by a uriref based on the assertion ID
|
||||
"uri": None,
|
||||
"transforms": {
|
||||
"transform": [{
|
||||
"algorithm": ds.TRANSFORM_ENVELOPED,
|
||||
},
|
||||
{
|
||||
"algorithm": ds.ALG_EXC_C14N,
|
||||
"inclusive_namespaces": {
|
||||
"prefix_list": "ds saml2 saml2p xenc",
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"digest_method":{
|
||||
"algorithm": ds.DIGEST_SHA1,
|
||||
},
|
||||
"digest_value": "",
|
||||
}
|
||||
},
|
||||
"signature_value": None,
|
||||
}
|
||||
# PRE_SIGNATURE = {
|
||||
# "signed_info": {
|
||||
# "signature_method": {
|
||||
# "algorithm": ds.SIG_RSA_SHA1
|
||||
# },
|
||||
# "canonicalization_method": {
|
||||
# "algorithm": ds.ALG_EXC_C14N
|
||||
# },
|
||||
# "reference": {
|
||||
# # must be replace by a uriref based on the assertion ID
|
||||
# "uri": None,
|
||||
# "transforms": {
|
||||
# "transform": [{
|
||||
# "algorithm": ds.TRANSFORM_ENVELOPED,
|
||||
# },
|
||||
# {
|
||||
# "algorithm": ds.ALG_EXC_C14N,
|
||||
# "inclusive_namespaces": {
|
||||
# "prefix_list": "ds saml2 saml2p xenc",
|
||||
# }
|
||||
# }
|
||||
# ]
|
||||
# },
|
||||
# "digest_method":{
|
||||
# "algorithm": ds.DIGEST_SHA1,
|
||||
# },
|
||||
# "digest_value": "",
|
||||
# }
|
||||
# },
|
||||
# "signature_value": None,
|
||||
# }
|
||||
#
|
||||
# def pre_signature_part(ident, public_key=None, id=None):
|
||||
# """
|
||||
# If an assertion is to be signed the signature part has to be preset
|
||||
# with which algorithms to be used, this function returns such a
|
||||
# preset part.
|
||||
#
|
||||
# :param ident: The identifier of the assertion, so you know which assertion
|
||||
# was signed
|
||||
# :param public_key: The base64 part of a PEM file
|
||||
# :return: A preset signature part
|
||||
# """
|
||||
#
|
||||
# presig = copy.deepcopy(PRE_SIGNATURE)
|
||||
# presig["signed_info"]["reference"]["uri"] = "#%s" % ident
|
||||
# if id:
|
||||
# presig["id"] = "Signature%d" % id
|
||||
# if public_key:
|
||||
# presig["key_info"] = {
|
||||
# "x509_data": {
|
||||
# "x509_certificate": public_key,
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# return presig
|
||||
|
||||
def pre_signature_part(ident, public_key=None, id=None):
|
||||
|
||||
def pre_signature_part(ident, public_key=None, identifier=None):
|
||||
"""
|
||||
If an assertion is to be signed the signature part has to be preset
|
||||
with which algorithms to be used, this function returns such a
|
||||
@@ -612,15 +634,33 @@ def pre_signature_part(ident, public_key=None, id=None):
|
||||
:return: A preset signature part
|
||||
"""
|
||||
|
||||
presig = copy.deepcopy(PRE_SIGNATURE)
|
||||
presig["signed_info"]["reference"]["uri"] = "#%s" % ident
|
||||
if id:
|
||||
presig["id"] = "Signature%d" % id
|
||||
if public_key:
|
||||
presig["key_info"] = {
|
||||
"x509_data": {
|
||||
"x509_certificate": public_key,
|
||||
}
|
||||
}
|
||||
signature_method = ds.SignatureMethod(algorithm = ds.SIG_RSA_SHA1)
|
||||
canonicalization_method = ds.CanonicalizationMethod(
|
||||
algorithm = ds.ALG_EXC_C14N)
|
||||
trans0 = ds.Transform(algorithm = ds.TRANSFORM_ENVELOPED)
|
||||
trans1 = ds.Transform(algorithm = ds.ALG_EXC_C14N)
|
||||
transforms = ds.Transforms(transform = [trans0, trans1])
|
||||
digest_method = ds.DigestMethod(algorithm = ds.DIGEST_SHA1)
|
||||
|
||||
reference = ds.Reference(uri = "#%s" % ident,
|
||||
digest_value = ds.DigestValue(),
|
||||
transforms = transforms,
|
||||
digest_method = digest_method)
|
||||
|
||||
signed_info = ds.SignedInfo(signature_method = signature_method,
|
||||
canonicalization_method = canonicalization_method,
|
||||
reference = reference)
|
||||
|
||||
signature = ds.Signature(signed_info=signed_info,
|
||||
signature_value=ds.SignatureValue())
|
||||
|
||||
if identifier:
|
||||
signature.id = "Signature%d" % identifier
|
||||
|
||||
if public_key:
|
||||
x509_data = ds.X509Data(x509_certificate=[ds.X509Certificate(text=public_key)])
|
||||
key_info = ds.KeyInfo(x509_data=x509_data)
|
||||
signature.key_info = key_info
|
||||
|
||||
return signature
|
||||
|
||||
return presig
|
||||
|
||||
@@ -62,7 +62,7 @@ def parse_soap_enveloped_saml_thingy(text, expected_tag):
|
||||
else:
|
||||
return ""
|
||||
|
||||
def make_soap_enveloped_saml_thingy(thingy):
|
||||
def make_soap_enveloped_saml_thingy(thingy, headers=None):
|
||||
""" Returns a soap envelope containing a SAML request
|
||||
as a text string.
|
||||
|
||||
@@ -72,6 +72,13 @@ def make_soap_enveloped_saml_thingy(thingy):
|
||||
envelope = ElementTree.Element('')
|
||||
envelope.tag = '{%s}Envelope' % NAMESPACE
|
||||
|
||||
if headers:
|
||||
header = ElementTree.Element('')
|
||||
header.tag = '{%s}Header' % NAMESPACE
|
||||
envelope.append(header)
|
||||
for head in headers:
|
||||
head.become_child_element(header)
|
||||
|
||||
body = ElementTree.Element('')
|
||||
body.tag = '{%s}Body' % NAMESPACE
|
||||
envelope.append(body)
|
||||
|
||||
@@ -189,6 +189,17 @@ def response_factory(signature=False, encrypt=False, **kwargs):
|
||||
pass
|
||||
return args2dict(**kwargs)
|
||||
|
||||
def response_factory_x(signature=False, encrypt=False, **kwargs):
|
||||
response = samlp.Response(id=sid(), version=VERSION, issue_instant=instant())
|
||||
|
||||
if signature:
|
||||
kwargs["signature"] = sigver.pre_signature_part(kwargs["id"])
|
||||
|
||||
if encrypt:
|
||||
pass
|
||||
|
||||
return response
|
||||
|
||||
def _attrval(val):
|
||||
if isinstance(val, list) or isinstance(val, set):
|
||||
attrval = [args2dict(v) for v in val]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -54,7 +54,7 @@ TEST_PGP_DATA = """<?xml version="1.0" encoding="utf-8"?>
|
||||
TEST_X509_ISSUER_SERIAL = """<?xml version="1.0" encoding="utf-8"?>
|
||||
<X509IssuerSerial xmlns="http://www.w3.org/2000/09/xmldsig#">
|
||||
<X509IssuerName>issuer name</X509IssuerName>
|
||||
<X509IssuerNumber>1</X509IssuerNumber>
|
||||
<X509SerialNumber>1</X509SerialNumber>
|
||||
</X509IssuerSerial>
|
||||
"""
|
||||
|
||||
|
||||
@@ -3,15 +3,18 @@
|
||||
"service": {
|
||||
"idp": {
|
||||
"name" : "Rolands IdP",
|
||||
"url": "http://localhost:8088/sso",
|
||||
"endpoints" : {
|
||||
"single_sign_on_service" : ["http://localhost:8088/sso"],
|
||||
},
|
||||
"policy": {
|
||||
"default": {
|
||||
"lifetime": {"minutes":15},
|
||||
"attribute_restrictions": None, # means all I have
|
||||
"name_form": "urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
|
||||
"name_form": "urn:oasis:names:tc:SAML:2.0:attrname-format:uri",
|
||||
},
|
||||
"urn:mace:example.com:saml:roland:sp": {
|
||||
"lifetime": {"minutes": 5},
|
||||
"nameid_format": "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent",
|
||||
# "attribute_restrictions":{
|
||||
# "givenName": None,
|
||||
# "surName": None,
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
__author__ = 'tmatsuo@example.com (Takashi MATSUO)'
|
||||
|
||||
TEST_ENDPOINT = """<?xml version="1.0" encoding="utf-8"?>
|
||||
<Endpoint xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
|
||||
<EndpointType xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
|
||||
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
|
||||
Location="http://www.example.com/endpoint"
|
||||
ResponseLocation = "http://www.example.com/response"
|
||||
@@ -72,7 +72,7 @@ TEST_ASSERTION_ID_REQUEST_SERVICE = """<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
|
||||
TEST_INDEXED_ENDPOINT = """<?xml version="1.0" encoding="utf-8"?>
|
||||
<IndexedEndpoint xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
|
||||
<IndexedEndpointType xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
|
||||
index="1"
|
||||
isDefault="false"
|
||||
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
|
||||
@@ -115,22 +115,20 @@ TEST_ATTRIBUTE_PROFILE = """<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
TEST_ORGANIZATION_NAME = """<?xml version="1.0" encoding="utf-8"?>
|
||||
<OrganizationName xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
|
||||
ns1:lang="en" xmlns:ns1="http:#www.w3.org/XML/1998/namespace">
|
||||
SIOS Technology, Inc.
|
||||
xml:lang="se">
|
||||
Catalogix
|
||||
</OrganizationName>
|
||||
"""
|
||||
|
||||
TEST_ORGANIZATION_DISPLAY_NAME = """<?xml version="1.0" encoding="utf-8"?>
|
||||
<OrganizationDisplayName
|
||||
xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
|
||||
ns1:lang="en" xmlns:ns1="http:#www.w3.org/XML/1998/namespace">
|
||||
SIOS
|
||||
</OrganizationDisplayName>
|
||||
TEST_ORGANIZATION_DISPLAY_NAME = """<?xml version='1.0' encoding='UTF-8'?>
|
||||
<ns0:OrganizationDisplayName xml:lang="se" xmlns:ns0="urn:oasis:names:tc:SAML:2.0:metadata">
|
||||
Catalogix
|
||||
</ns0:OrganizationDisplayName>
|
||||
"""
|
||||
|
||||
TEST_ORGANIZATION_URL = """<?xml version="1.0" encoding="utf-8"?>
|
||||
<OrganizationURL xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
|
||||
ns1:lang="ja" xmlns:ns1="http:#www.w3.org/XML/1998/namespace">
|
||||
xml:lang="no">
|
||||
http://www.example.com/
|
||||
</OrganizationURL>
|
||||
"""
|
||||
@@ -141,15 +139,14 @@ TEST_ORGANIZATION = """<?xml version="1.0" encoding="utf-8"?>
|
||||
<hoge xmlns="http://hoge.example.com/">hogehoge</hoge>
|
||||
</Extensions>
|
||||
<OrganizationName
|
||||
ns1:lang="en" xmlns:ns1="http:#www.w3.org/XML/1998/namespace">
|
||||
SIOS Technology, Inc.
|
||||
xml:lang="se">
|
||||
Catalogix AB
|
||||
</OrganizationName>
|
||||
<OrganizationDisplayName ns1:lang="en"
|
||||
xmlns:ns1="http:#www.w3.org/XML/1998/namespace">
|
||||
SIOS
|
||||
<OrganizationDisplayName xml:lang="no">
|
||||
Catalogix AS
|
||||
</OrganizationDisplayName>
|
||||
<OrganizationURL
|
||||
ns1:lang="ja" xmlns:ns1="http:#www.w3.org/XML/1998/namespace">
|
||||
xml:lang="en">
|
||||
http://www.example.com/
|
||||
</OrganizationURL>
|
||||
</Organization>
|
||||
@@ -447,7 +444,7 @@ TEST_ROLE_DESCRIPTOR = """<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
|
||||
TEST_SSO_DESCRIPTOR = """<?xml version="1.0" encoding="utf-8"?>
|
||||
<SSODescriptor
|
||||
<SSODescriptorType
|
||||
xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
|
||||
ID="ID"
|
||||
validUntil="2008-09-14T01:05:02Z"
|
||||
@@ -642,7 +639,7 @@ TEST_SSO_DESCRIPTOR = """<?xml version="1.0" encoding="utf-8"?>
|
||||
<NameIDFormat xmlns="urn:oasis:names:tc:SAML:2.0:metadata">
|
||||
urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
|
||||
</NameIDFormat>
|
||||
</SSODescriptor>
|
||||
</SSODescriptorType>
|
||||
"""
|
||||
|
||||
|
||||
@@ -888,15 +885,15 @@ TEST_REQUESTED_ATTRIBUTE = """<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
TEST_SERVICE_NAME = """<?xml version="1.0" encoding="utf-8"?>
|
||||
<ServiceName xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
|
||||
ns1:lang="en" xmlns:ns1="http:#www.w3.org/XML/1998/namespace">
|
||||
SIOS mail
|
||||
xml:lang="en">
|
||||
Catalogix Whois
|
||||
</ServiceName>
|
||||
"""
|
||||
|
||||
TEST_SERVICE_DESCRIPTION = """<?xml version="1.0" encoding="utf-8"?>
|
||||
<ServiceDescription xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
|
||||
ns1:lang="en" xmlns:ns1="http:#www.w3.org/XML/1998/namespace">
|
||||
SIOS mail service
|
||||
xml:lang="en">
|
||||
Catalogix Whois Service
|
||||
</ServiceDescription>
|
||||
"""
|
||||
|
||||
@@ -1283,7 +1280,7 @@ TEST_ENTITY_DESCRIPTOR = """<?xml version="1.0" encoding="utf-8"?>
|
||||
TEST_ENTITIES_DESCRIPTOR = """<?xml version="1.0" encoding="utf-8"?>
|
||||
<EntitiesDescriptor
|
||||
xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
|
||||
name="name"
|
||||
Name="name"
|
||||
ID="ID"
|
||||
validUntil="2008-09-14T01:05:02Z"
|
||||
cacheDuration="10:00:00:00">
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
"service": {
|
||||
"idp": {
|
||||
"name" : "Rolands restrictied IdP",
|
||||
"url": "http://localhost:8089/sso",
|
||||
"endpoints" : {
|
||||
"single_sign_on_service" : ["http://localhost:8089/sso"],
|
||||
},
|
||||
"assertions": {
|
||||
"default": {
|
||||
"lifetime": {"minutes":15},
|
||||
@@ -22,7 +24,9 @@
|
||||
},
|
||||
"aa": {
|
||||
"name" : "Rolands restrictied AA",
|
||||
"url": "http://localhost:8089/sso",
|
||||
"endpoints" : {
|
||||
"attribute_service" : ["http://localhost:8089/aa"],
|
||||
},
|
||||
"assertions": {
|
||||
"default": {
|
||||
"lifetime": {"minutes":15},
|
||||
|
||||
@@ -96,7 +96,7 @@ TEST_AUTHN_REQUEST = """<?xml version="1.0" encoding="utf-8"?>
|
||||
AssertionConsumerServiceIndex="1"
|
||||
AssertionConsumerServiceURL="http://www.example.com/acs"
|
||||
ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
|
||||
AssertionConsumingServiceIndex="2"
|
||||
AttributeConsumingServiceIndex="2"
|
||||
ProviderName="provider name"
|
||||
xmlns="urn:oasis:names:tc:SAML:2.0:protocol">
|
||||
<Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">
|
||||
|
||||
@@ -3,11 +3,14 @@
|
||||
"service": {
|
||||
"sp":{
|
||||
"name" : "urn:mace:example.com:saml:roland:sp",
|
||||
"url": "http://lingon.catalogix.se:8087/",
|
||||
"description": "My own SP",
|
||||
"endpoints":{
|
||||
"assertion_consumer_service": ["http://lingon.catalogix.se:8087/"],
|
||||
},
|
||||
"required_attributes": ["surName", "givenName", "mail"],
|
||||
"optional_attributes": ["title"],
|
||||
"idp":{
|
||||
"entity_id": ["urn:mace:example.com:saml:roland:idp"],
|
||||
"urn:mace:example.com:saml:roland:idp":None,
|
||||
},
|
||||
}
|
||||
},
|
||||
@@ -27,4 +30,17 @@
|
||||
"subject_data": "subject_data.db",
|
||||
"accept_time_diff": 60,
|
||||
"attribute_map_dir" : "attributemaps",
|
||||
"organization": {
|
||||
"name": ("AB Exempel", "se"),
|
||||
"display_name": ("AB Exempel", "se"),
|
||||
"url": "http://www.example.org",
|
||||
},
|
||||
"contact_person": [{
|
||||
"given_name": "Roland",
|
||||
"sur_name": "Hedberg",
|
||||
"telephone_number": "+46 70 100 0000",
|
||||
"email_address": ["tech@eample.com", "tech@example.org"],
|
||||
"contact_type": "technical"
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -1,541 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from saml2 import create_class_from_xml_string, class_name, make_vals, md
|
||||
from saml2.saml import NameID, Issuer, SubjectLocality, AuthnContextClassRef
|
||||
from saml2.saml import SubjectConfirmationData, SubjectConfirmation
|
||||
from saml2.saml import Attribute
|
||||
import saml2
|
||||
from py.test import raises
|
||||
import saml2_data
|
||||
|
||||
try:
|
||||
from xml.etree import cElementTree as ElementTree
|
||||
except ImportError:
|
||||
try:
|
||||
import cElementTree as ElementTree
|
||||
except ImportError:
|
||||
from elementtree import ElementTree
|
||||
|
||||
ITEMS = {
|
||||
NameID:["""<?xml version="1.0" encoding="utf-8"?>
|
||||
<NameID xmlns="urn:oasis:names:tc:SAML:2.0:assertion"
|
||||
Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
|
||||
SPProvidedID="sp provided id">
|
||||
roland@example.com
|
||||
</NameID>
|
||||
""", """<?xml version="1.0" encoding="utf-8"?>
|
||||
<NameID xmlns="urn:oasis:names:tc:SAML:2.0:assertion"
|
||||
SPNameQualifier="https://foo.example.com/sp"
|
||||
Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient">_1632879f09d08ea5ede2dc667cbed7e429ebc4335c</NameID>
|
||||
""", """<?xml version="1.0" encoding="utf-8"?>
|
||||
<NameID xmlns="urn:oasis:names:tc:SAML:2.0:assertion"
|
||||
Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"
|
||||
NameQualifier="http://authentic.example.com/saml/metadata"
|
||||
SPNameQualifier="http://auth.example.com/saml/metadata">test
|
||||
</NameID>"""],
|
||||
Issuer:"""<?xml version="1.0" encoding="utf-8"?>
|
||||
<Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">
|
||||
http://www.example.com/test
|
||||
</Issuer>
|
||||
""",
|
||||
SubjectLocality: """<?xml version="1.0" encoding="utf-8"?>
|
||||
<SubjectLocality xmlns="urn:oasis:names:tc:SAML:2.0:assertion"
|
||||
Address="127.0.0.1" DNSName="localhost"/>
|
||||
""",
|
||||
SubjectConfirmationData:
|
||||
"""<?xml version="1.0" encoding="utf-8"?>
|
||||
<SubjectConfirmationData xmlns="urn:oasis:names:tc:SAML:2.0:assertion"
|
||||
InResponseTo="_1683146e27983964fbe7bf8f08961108d166a652e5"
|
||||
NotOnOrAfter="2010-02-18T13:52:13.959Z"
|
||||
NotBefore="2010-01-16T12:00:00Z"
|
||||
Recipient="http://192.168.0.10/saml/sp" />""",
|
||||
SubjectConfirmation:
|
||||
"""<?xml version="1.0" encoding="utf-8"?>
|
||||
<SubjectConfirmation xmlns="urn:oasis:names:tc:SAML:2.0:assertion"
|
||||
Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><NameID
|
||||
Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
|
||||
NameQualifier="http://authentic.example.com/saml/metadata">test@example.com
|
||||
</NameID>
|
||||
<SubjectConfirmationData
|
||||
NotOnOrAfter="2010-02-17T17:02:38Z"
|
||||
Recipient="http://auth.example.com/saml/proxySingleSignOnRedirect"
|
||||
InResponseTo="_59B3A01B03334032C31E434C63F89E3E"/></SubjectConfirmation>"""
|
||||
}
|
||||
|
||||
#def pytest_generate_tests(metafunc):
|
||||
# if "target_class" in metafunc.funcargnames:
|
||||
# for tcl,xml in ITEMS.items():
|
||||
# metafunc.addcall(funcargs={"target_class":tcl,"xml_string":xml})
|
||||
|
||||
def _eq(l1,l2):
|
||||
return set(l1) == set(l2)
|
||||
|
||||
def test_create_class_from_xml_string_nameid():
|
||||
kl = create_class_from_xml_string(NameID, ITEMS[NameID][0])
|
||||
assert kl != None
|
||||
assert kl.format == "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
|
||||
assert kl.sp_provided_id == "sp provided id"
|
||||
assert kl.text.strip() == "roland@example.com"
|
||||
assert _eq(kl.keyswv(), ['sp_provided_id', 'format', 'text'])
|
||||
assert class_name(kl) == "urn:oasis:names:tc:SAML:2.0:assertion:NameID"
|
||||
assert _eq(kl.keys(), ['sp_provided_id', 'sp_name_qualifier',
|
||||
'name_qualifier', 'format', 'text'])
|
||||
|
||||
kl = create_class_from_xml_string(NameID, ITEMS[NameID][1])
|
||||
assert kl != None
|
||||
assert kl.format == "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
|
||||
assert kl.sp_name_qualifier == "https://foo.example.com/sp"
|
||||
assert kl.text.strip() == "_1632879f09d08ea5ede2dc667cbed7e429ebc4335c"
|
||||
assert _eq(kl.keyswv(), ['sp_name_qualifier', 'format', 'text'])
|
||||
assert class_name(kl) == "urn:oasis:names:tc:SAML:2.0:assertion:NameID"
|
||||
|
||||
kl = create_class_from_xml_string(NameID, ITEMS[NameID][2])
|
||||
assert kl != None
|
||||
assert kl.format == "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"
|
||||
assert kl.name_qualifier == "http://authentic.example.com/saml/metadata"
|
||||
assert kl.sp_name_qualifier == "http://auth.example.com/saml/metadata"
|
||||
assert kl.text.strip() == "test"
|
||||
assert _eq(kl.keyswv(), ['sp_name_qualifier', 'format', 'name_qualifier',
|
||||
'text'])
|
||||
assert class_name(kl) == "urn:oasis:names:tc:SAML:2.0:assertion:NameID"
|
||||
|
||||
def test_create_class_from_xml_string_issuer():
|
||||
kl = create_class_from_xml_string(Issuer, ITEMS[Issuer])
|
||||
assert kl != None
|
||||
assert kl.text.strip() == "http://www.example.com/test"
|
||||
assert _eq(kl.keyswv(), ['text'])
|
||||
assert class_name(kl) == "urn:oasis:names:tc:SAML:2.0:assertion:Issuer"
|
||||
|
||||
def test_create_class_from_xml_string_subject_locality():
|
||||
kl = create_class_from_xml_string(SubjectLocality, ITEMS[SubjectLocality])
|
||||
assert kl != None
|
||||
assert _eq(kl.keyswv(), ['address', "dns_name"])
|
||||
assert kl.address == "127.0.0.1"
|
||||
assert kl.dns_name == "localhost"
|
||||
assert class_name(kl) == "urn:oasis:names:tc:SAML:2.0:assertion:SubjectLocality"
|
||||
|
||||
def test_create_class_from_xml_string_subject_confirmation_data():
|
||||
kl = create_class_from_xml_string(SubjectConfirmationData,
|
||||
ITEMS[SubjectConfirmationData])
|
||||
assert kl != None
|
||||
assert _eq(kl.keyswv(), ['in_response_to', 'not_on_or_after',
|
||||
'not_before', 'recipient'])
|
||||
assert kl.in_response_to == "_1683146e27983964fbe7bf8f08961108d166a652e5"
|
||||
assert kl.not_on_or_after == "2010-02-18T13:52:13.959Z"
|
||||
assert kl.not_before == "2010-01-16T12:00:00Z"
|
||||
assert kl.recipient == "http://192.168.0.10/saml/sp"
|
||||
assert class_name(kl) == \
|
||||
"urn:oasis:names:tc:SAML:2.0:assertion:SubjectConfirmationData"
|
||||
|
||||
def test_create_class_from_xml_string_subject_confirmation():
|
||||
kl = create_class_from_xml_string(SubjectConfirmation,
|
||||
ITEMS[SubjectConfirmation])
|
||||
assert kl != None
|
||||
assert _eq(kl.keyswv(), ['method', 'name_id',
|
||||
'subject_confirmation_data'])
|
||||
assert kl.method == "urn:oasis:names:tc:SAML:2.0:cm:bearer"
|
||||
name_id = kl.name_id
|
||||
assert _eq(name_id.keyswv(), ['format', 'name_qualifier', 'text'])
|
||||
assert name_id.format == "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
|
||||
assert name_id.name_qualifier == "http://authentic.example.com/saml/metadata"
|
||||
assert name_id.text.strip() == "test@example.com"
|
||||
subject_confirmation_data = kl.subject_confirmation_data
|
||||
assert _eq(subject_confirmation_data.keyswv(), ['not_on_or_after',
|
||||
'recipient', 'in_response_to'])
|
||||
assert subject_confirmation_data.recipient == \
|
||||
"http://auth.example.com/saml/proxySingleSignOnRedirect"
|
||||
assert subject_confirmation_data.not_on_or_after == "2010-02-17T17:02:38Z"
|
||||
assert subject_confirmation_data.in_response_to == \
|
||||
"_59B3A01B03334032C31E434C63F89E3E"
|
||||
assert class_name(kl) == \
|
||||
"urn:oasis:names:tc:SAML:2.0:assertion:SubjectConfirmation"
|
||||
|
||||
def test_create_class_from_xml_string_wrong_class_spec():
|
||||
kl = create_class_from_xml_string(SubjectConfirmationData,
|
||||
ITEMS[SubjectConfirmation])
|
||||
assert kl == None
|
||||
|
||||
def test_ee_1():
|
||||
ee = saml2.extension_element_from_string(
|
||||
"""<?xml version='1.0' encoding='UTF-8'?><foo>bar</foo>""")
|
||||
assert ee != None
|
||||
print ee.__dict__
|
||||
assert ee.attributes == {}
|
||||
assert ee.tag == "foo"
|
||||
assert ee.namespace == None
|
||||
assert ee.children == []
|
||||
assert ee.text == "bar"
|
||||
|
||||
def test_ee_2():
|
||||
ee = saml2.extension_element_from_string(
|
||||
"""<?xml version='1.0' encoding='UTF-8'?><foo id="xyz">bar</foo>""")
|
||||
assert ee != None
|
||||
print ee.__dict__
|
||||
assert ee.attributes == {"id":"xyz"}
|
||||
assert ee.tag == "foo"
|
||||
assert ee.namespace == None
|
||||
assert ee.children == []
|
||||
assert ee.text == "bar"
|
||||
|
||||
def test_ee_3():
|
||||
ee = saml2.extension_element_from_string(
|
||||
"""<?xml version='1.0' encoding='UTF-8'?>
|
||||
<foo xmlns="urn:mace:example.com:saml:ns"
|
||||
id="xyz">bar</foo>""")
|
||||
assert ee != None
|
||||
print ee.__dict__
|
||||
assert ee.attributes == {"id":"xyz"}
|
||||
assert ee.tag == "foo"
|
||||
assert ee.namespace == "urn:mace:example.com:saml:ns"
|
||||
assert ee.children == []
|
||||
assert ee.text == "bar"
|
||||
|
||||
def test_ee_4():
|
||||
ee = saml2.extension_element_from_string(
|
||||
"""<?xml version='1.0' encoding='UTF-8'?>
|
||||
<foo xmlns="urn:mace:example.com:saml:ns">
|
||||
<id>xyz</id><bar>tre</bar></foo>""")
|
||||
assert ee != None
|
||||
print ee.__dict__
|
||||
assert ee.attributes == {}
|
||||
assert ee.tag == "foo"
|
||||
assert ee.namespace == "urn:mace:example.com:saml:ns"
|
||||
assert len(ee.children) == 2
|
||||
assert ee.text.strip() == ""
|
||||
id = ee.find_children("id", "urn:mace:example.com:saml:namespace")
|
||||
assert id == []
|
||||
ids = ee.find_children("id", "urn:mace:example.com:saml:ns")
|
||||
assert ids != []
|
||||
id = ids[0]
|
||||
print id.__dict__
|
||||
assert id.attributes == {}
|
||||
assert id.tag == "id"
|
||||
assert id.namespace == "urn:mace:example.com:saml:ns"
|
||||
assert id.children == []
|
||||
assert id.text.strip() == "xyz"
|
||||
|
||||
def test_ee_5():
|
||||
ee = saml2.extension_element_from_string(
|
||||
"""<?xml version='1.0' encoding='UTF-8'?>
|
||||
<foo xmlns="urn:mace:example.com:saml:ns">bar</foo>""")
|
||||
|
||||
ce = saml2.extension_element_from_string(
|
||||
"""<?xml version='1.0' encoding='UTF-8'?>
|
||||
<educause xmlns="urn:mace:example.com:saml:cu">rev</educause>""")
|
||||
|
||||
ee.children.append(ce)
|
||||
|
||||
assert ee != None
|
||||
print ee.__dict__
|
||||
assert ee.attributes == {}
|
||||
assert ee.tag == "foo"
|
||||
assert ee.namespace == "urn:mace:example.com:saml:ns"
|
||||
assert len(ee.children) == 1
|
||||
assert ee.text.strip() == "bar"
|
||||
|
||||
c = ee.children[0]
|
||||
print c.__dict__
|
||||
|
||||
child = ee.find_children(namespace="urn:mace:example.com:saml:cu")
|
||||
assert len(child) == 1
|
||||
child = ee.find_children(namespace="urn:mace:example.com:saml:ns")
|
||||
assert len(child) == 0
|
||||
child = ee.find_children("educause","urn:mace:example.com:saml:cu")
|
||||
assert len(child) == 1
|
||||
child = ee.find_children("edugain","urn:mace:example.com:saml:cu")
|
||||
assert len(child) == 0
|
||||
print ee.to_string()
|
||||
|
||||
def test_ee_6():
|
||||
ee = saml2.extension_element_from_string(
|
||||
"""<?xml version='1.0' encoding='UTF-8'?>
|
||||
<foo xmlns="urn:mace:example.com:saml:ns">bar</foo>""")
|
||||
|
||||
ce = saml2.extension_element_from_string(
|
||||
"""<?xml version='1.0' encoding='UTF-8'?>
|
||||
<educause xmlns="urn:mace:example.com:saml:cu">rev</educause>""")
|
||||
|
||||
et = ee.transfer_to_element_tree()
|
||||
ce.become_child_element_of(et)
|
||||
|
||||
pee = saml2._extension_element_from_element_tree(et)
|
||||
|
||||
assert pee != None
|
||||
print pee.__dict__
|
||||
assert pee.attributes == {}
|
||||
assert pee.tag == "foo"
|
||||
assert pee.namespace == "urn:mace:example.com:saml:ns"
|
||||
assert len(pee.children) == 1
|
||||
assert pee.text.strip() == "bar"
|
||||
|
||||
c = pee.children[0]
|
||||
print c.__dict__
|
||||
|
||||
child = pee.find_children(namespace="urn:mace:example.com:saml:cu")
|
||||
assert len(child) == 1
|
||||
child = pee.find_children(namespace="urn:mace:example.com:saml:ns")
|
||||
assert len(child) == 0
|
||||
child = pee.find_children("educause","urn:mace:example.com:saml:cu")
|
||||
assert len(child) == 1
|
||||
child = pee.find_children("edugain","urn:mace:example.com:saml:cu")
|
||||
assert len(child) == 0
|
||||
print pee.to_string()
|
||||
|
||||
|
||||
NAMEID_WITH_ATTRIBUTE_EXTENSION = """<?xml version="1.0" encoding="utf-8"?>
|
||||
<NameID xmlns="urn:oasis:names:tc:SAML:2.0:assertion"
|
||||
xmlns:local="urn:mace:example.com:saml:assertion"
|
||||
Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
|
||||
SPProvidedID="sp provided id"
|
||||
local:Foo="BAR">
|
||||
roland@example.com
|
||||
</NameID>
|
||||
"""
|
||||
|
||||
def test_nameid_with_extension():
|
||||
kl = create_class_from_xml_string(NameID, NAMEID_WITH_ATTRIBUTE_EXTENSION)
|
||||
assert kl != None
|
||||
print kl.__dict__
|
||||
assert kl.format == "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
|
||||
assert kl.sp_provided_id == "sp provided id"
|
||||
assert kl.text.strip() == "roland@example.com"
|
||||
assert _eq(kl.keyswv(), ['sp_provided_id', 'format',
|
||||
'extension_attributes', 'text'])
|
||||
assert class_name(kl) == "urn:oasis:names:tc:SAML:2.0:assertion:NameID"
|
||||
assert _eq(kl.keys(), ['sp_provided_id', 'sp_name_qualifier',
|
||||
'name_qualifier', 'format', 'text'])
|
||||
assert kl.extension_attributes == {
|
||||
'{urn:mace:example.com:saml:assertion}Foo': 'BAR'}
|
||||
|
||||
SUBJECT_CONFIRMATION_WITH_MEMBER_EXTENSION = """<?xml version="1.0" encoding="utf-8"?>
|
||||
<SubjectConfirmation xmlns="urn:oasis:names:tc:SAML:2.0:assertion"
|
||||
Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
|
||||
<NameID
|
||||
Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
|
||||
NameQualifier="http://authentic.example.com/saml/metadata">test@example.com
|
||||
</NameID>
|
||||
<SubjectConfirmationData
|
||||
NotOnOrAfter="2010-02-17T17:02:38Z"
|
||||
Recipient="http://auth.example.com/saml/proxySingleSignOnRedirect"
|
||||
InResponseTo="_59B3A01B03334032C31E434C63F89E3E"/>
|
||||
<local:Trustlevel xmlns:local="urn:mace:example.com:saml:assertion">
|
||||
Excellent
|
||||
</local:Trustlevel>
|
||||
</SubjectConfirmation>"""
|
||||
|
||||
def test_subject_confirmation_with_extension():
|
||||
kl = create_class_from_xml_string(SubjectConfirmation,
|
||||
SUBJECT_CONFIRMATION_WITH_MEMBER_EXTENSION)
|
||||
assert kl != None
|
||||
print kl.__dict__
|
||||
assert kl.extension_attributes == {}
|
||||
assert kl.method == "urn:oasis:names:tc:SAML:2.0:cm:bearer"
|
||||
name_id = kl.name_id
|
||||
assert _eq(name_id.keyswv(), ['format', 'name_qualifier', 'text'])
|
||||
assert name_id.format == "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
|
||||
assert name_id.name_qualifier == "http://authentic.example.com/saml/metadata"
|
||||
assert name_id.text.strip() == "test@example.com"
|
||||
subject_confirmation_data = kl.subject_confirmation_data
|
||||
assert _eq(subject_confirmation_data.keyswv(), ['not_on_or_after',
|
||||
'recipient', 'in_response_to'])
|
||||
assert subject_confirmation_data.recipient == \
|
||||
"http://auth.example.com/saml/proxySingleSignOnRedirect"
|
||||
assert subject_confirmation_data.not_on_or_after == "2010-02-17T17:02:38Z"
|
||||
assert subject_confirmation_data.in_response_to == \
|
||||
"_59B3A01B03334032C31E434C63F89E3E"
|
||||
assert len(kl.extension_elements) == 1
|
||||
ee = kl.extension_elements[0]
|
||||
assert ee.tag == "Trustlevel"
|
||||
assert ee.namespace == "urn:mace:example.com:saml:assertion"
|
||||
assert ee.text.strip() == "Excellent"
|
||||
|
||||
def test_to_fro_string_1():
|
||||
kl = create_class_from_xml_string(SubjectConfirmation,
|
||||
SUBJECT_CONFIRMATION_WITH_MEMBER_EXTENSION)
|
||||
str = kl.to_string()
|
||||
cpy = create_class_from_xml_string(SubjectConfirmation, str)
|
||||
|
||||
print kl.__dict__
|
||||
print cpy.__dict__
|
||||
|
||||
assert kl.text.strip() == cpy.text.strip()
|
||||
assert _eq(kl.keyswv(), cpy.keyswv())
|
||||
assert len(kl.extension_elements) == len(cpy.extension_elements)
|
||||
klee = kl.extension_elements[0]
|
||||
cpyee = cpy.extension_elements[0]
|
||||
assert klee.text.strip() == cpyee.text.strip()
|
||||
assert klee.tag == cpyee.tag
|
||||
assert klee.namespace == cpyee.namespace
|
||||
|
||||
|
||||
def test_make_vals_str():
|
||||
kl = make_vals("Jeter",md.GivenName, part=True)
|
||||
assert isinstance(kl, md.GivenName)
|
||||
assert kl.text == "Jeter"
|
||||
|
||||
def test_make_vals_int():
|
||||
kl = make_vals(1024,md.KeySize, part=True)
|
||||
assert isinstance(kl, md.KeySize)
|
||||
assert kl.text == "1024"
|
||||
|
||||
def test_exception_make_vals_int_not_part():
|
||||
raises(TypeError, "make_vals(1024,md.KeySize)")
|
||||
raises(TypeError, "make_vals(1024,md.KeySize,md.EncryptionMethod())")
|
||||
raises(AttributeError, "make_vals(1024,md.KeySize,prop='key_size')")
|
||||
|
||||
def test_make_vals_list_of_ints():
|
||||
em = md.EncryptionMethod()
|
||||
make_vals([1024,2048], md.KeySize, em, "key_size")
|
||||
assert len(em.key_size) == 2
|
||||
|
||||
def test_make_vals_list_of_strs():
|
||||
cp = md.ContactPerson()
|
||||
make_vals(["Derek","Sanderson"], md.GivenName, cp, "given_name")
|
||||
assert len(cp.given_name) == 2
|
||||
assert _eq([i.text for i in cp.given_name],["Sanderson","Derek"])
|
||||
|
||||
def test_exception_make_vals_value_error():
|
||||
raises(ValueError, "make_vals((1024,'xyz'), md.KeySize, part=True)")
|
||||
|
||||
|
||||
def test_attribute_element_to_extension_element():
|
||||
attr = create_class_from_xml_string(Attribute, saml2_data.TEST_ATTRIBUTE)
|
||||
ee = saml2.element_to_extension_element(attr)
|
||||
print ee.__dict__
|
||||
assert ee.tag == "Attribute"
|
||||
assert ee.namespace == 'urn:oasis:names:tc:SAML:2.0:assertion'
|
||||
assert _eq(ee.attributes.keys(),['FriendlyName', 'Name', 'NameFormat'])
|
||||
assert ee.attributes["FriendlyName"] == 'test attribute'
|
||||
assert ee.attributes["Name"] == "testAttribute"
|
||||
assert ee.attributes["NameFormat"] == \
|
||||
'urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified'
|
||||
assert len(ee.children) == 2
|
||||
for child in ee.children:
|
||||
# children are also extension element instances
|
||||
assert child.namespace == 'urn:oasis:names:tc:SAML:2.0:assertion'
|
||||
assert child.tag == "AttributeValue"
|
||||
|
||||
def test_ee_7():
|
||||
ee = saml2.extension_element_from_string(
|
||||
"""<?xml version='1.0' encoding='UTF-8'?>
|
||||
<ExternalEntityAttributeAuthority
|
||||
xmlns="urn:oasis:names:tc:SAML:metadata:dynamicsaml">
|
||||
<AssertingEntity>
|
||||
<NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">
|
||||
http://federationX.org
|
||||
</NameID>
|
||||
</AssertingEntity>
|
||||
<RetrievalEndpoint>
|
||||
https://federationX.org/?ID=a87s76a5765da76576a57as
|
||||
</RetrievalEndpoint>
|
||||
</ExternalEntityAttributeAuthority>
|
||||
""")
|
||||
|
||||
print ee.__dict__
|
||||
assert len(ee.children) == 2
|
||||
for child in ee.children:
|
||||
assert child.namespace == "urn:oasis:names:tc:SAML:metadata:dynamicsaml"
|
||||
assert _eq(["AssertingEntity","RetrievalEndpoint"],
|
||||
[c.tag for c in ee.children])
|
||||
aes = [c for c in ee.children if c.tag == "AssertingEntity"]
|
||||
assert len(aes) == 1
|
||||
assert len(aes[0].children) == 1
|
||||
assert _eq(aes[0].attributes.keys(),[])
|
||||
nid = aes[0].children[0]
|
||||
assert nid.tag == "NameID"
|
||||
assert nid.namespace == "urn:oasis:names:tc:SAML:metadata:dynamicsaml"
|
||||
assert len(nid.children) == 0
|
||||
assert _eq(nid.attributes.keys(),["Format"])
|
||||
assert nid.text.strip() == "http://federationX.org"
|
||||
|
||||
|
||||
def test_extension_element_loadd():
|
||||
ava = {'attributes': {},
|
||||
'tag': 'ExternalEntityAttributeAuthority',
|
||||
'namespace': 'urn:oasis:names:tc:SAML:metadata:dynamicsaml',
|
||||
'children': [{
|
||||
"tag": "AssertingEntity",
|
||||
"namespace": "urn:oasis:names:tc:SAML:metadata:dynamicsaml",
|
||||
"children": [{
|
||||
"tag":"NameID",
|
||||
"namespace": "urn:oasis:names:tc:SAML:metadata:dynamicsaml",
|
||||
"text": "http://federationX.org",
|
||||
"attributes":{
|
||||
"Format":"urn:oasis:names:tc:SAML:2.0:nameid-format:entity"
|
||||
},
|
||||
}]
|
||||
}, {
|
||||
"tag":"RetrievalEndpoint",
|
||||
"namespace": "urn:oasis:names:tc:SAML:metadata:dynamicsaml",
|
||||
"text":"https://federationX.org/?ID=a87s76a5765da76576a57as",
|
||||
}],
|
||||
}
|
||||
|
||||
ee = saml2.ExtensionElement(ava["tag"]).loadd(ava)
|
||||
print ee.__dict__
|
||||
assert len(ee.children) == 2
|
||||
for child in ee.children:
|
||||
assert child.namespace == "urn:oasis:names:tc:SAML:metadata:dynamicsaml"
|
||||
assert _eq(["AssertingEntity","RetrievalEndpoint"],
|
||||
[c.tag for c in ee.children])
|
||||
aes = [c for c in ee.children if c.tag == "AssertingEntity"]
|
||||
assert len(aes) == 1
|
||||
assert len(aes[0].children) == 1
|
||||
assert _eq(aes[0].attributes.keys(),[])
|
||||
nid = aes[0].children[0]
|
||||
assert nid.tag == "NameID"
|
||||
assert nid.namespace == "urn:oasis:names:tc:SAML:metadata:dynamicsaml"
|
||||
assert len(nid.children) == 0
|
||||
assert _eq(nid.attributes.keys(),["Format"])
|
||||
assert nid.text.strip() == "http://federationX.org"
|
||||
|
||||
def test_extensions_loadd():
|
||||
ava = {"extension_elements":[{'attributes': {},
|
||||
'tag': 'ExternalEntityAttributeAuthority',
|
||||
'namespace': 'urn:oasis:names:tc:SAML:metadata:dynamicsaml',
|
||||
'children': [{
|
||||
"tag": "AssertingEntity",
|
||||
"namespace": "urn:oasis:names:tc:SAML:metadata:dynamicsaml",
|
||||
"children": [{
|
||||
"tag":"NameID",
|
||||
"namespace": "urn:oasis:names:tc:SAML:metadata:dynamicsaml",
|
||||
"text": "http://federationX.org",
|
||||
"attributes":{
|
||||
"Format":"urn:oasis:names:tc:SAML:2.0:nameid-format:entity"
|
||||
},
|
||||
}]
|
||||
}, {
|
||||
"tag":"RetrievalEndpoint",
|
||||
"namespace": "urn:oasis:names:tc:SAML:metadata:dynamicsaml",
|
||||
"text":"https://federationX.org/?ID=a87s76a5765da76576a57as",
|
||||
}],
|
||||
}],
|
||||
"extension_attributes": {
|
||||
"foo":"bar",
|
||||
}
|
||||
}
|
||||
|
||||
extension = saml2.SamlBase()
|
||||
extension.loadd(ava)
|
||||
|
||||
print extension.__dict__
|
||||
assert len(extension.extension_elements) == 1
|
||||
ee = extension.extension_elements[0]
|
||||
assert len(ee.children) == 2
|
||||
for child in ee.children:
|
||||
assert child.namespace == "urn:oasis:names:tc:SAML:metadata:dynamicsaml"
|
||||
assert _eq(["AssertingEntity","RetrievalEndpoint"],
|
||||
[c.tag for c in ee.children])
|
||||
aes = [c for c in ee.children if c.tag == "AssertingEntity"]
|
||||
assert len(aes) == 1
|
||||
assert len(aes[0].children) == 1
|
||||
assert _eq(aes[0].attributes.keys(),[])
|
||||
nid = aes[0].children[0]
|
||||
assert nid.tag == "NameID"
|
||||
assert nid.namespace == "urn:oasis:names:tc:SAML:metadata:dynamicsaml"
|
||||
assert len(nid.children) == 0
|
||||
assert _eq(nid.attributes.keys(),["Format"])
|
||||
assert nid.text.strip() == "http://federationX.org"
|
||||
|
||||
assert extension.extension_attributes.keys() == ["foo"]
|
||||
assert extension.extension_attributes["foo"] == "bar"
|
||||
@@ -1,989 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2010 Umeå University.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Tests for saml2.saml"""
|
||||
|
||||
__author__ = 'roland.hedberg@adm.umu.se (Roland Hedberg)'
|
||||
|
||||
try:
|
||||
from xml.etree import ElementTree
|
||||
except ImportError:
|
||||
from elementtree import ElementTree
|
||||
import saml2
|
||||
from saml2 import saml
|
||||
import saml2_data, ds_data
|
||||
import xmldsig as ds
|
||||
|
||||
from py.test import raises
|
||||
|
||||
class TestNameID:
|
||||
|
||||
def setup_class(self):
|
||||
self.name_id = saml.NameID()
|
||||
|
||||
def testEmptyExtensionsList(self):
|
||||
"""Test if NameID has empty extensions list"""
|
||||
assert isinstance(self.name_id.extension_elements, list)
|
||||
assert len(self.name_id.extension_elements) == 0
|
||||
|
||||
def testFormatAttribute(self):
|
||||
"""Test for Format attribute accessors"""
|
||||
self.name_id.format = saml.NAMEID_FORMAT_EMAILADDRESS
|
||||
assert self.name_id.format == saml.NAMEID_FORMAT_EMAILADDRESS
|
||||
assert len(self.name_id.extension_elements) == 0
|
||||
new_name_id = saml.name_id_from_string(self.name_id.to_string())
|
||||
assert len(new_name_id.extension_elements) == 0
|
||||
|
||||
self.name_id.extension_elements.append(saml2.ExtensionElement(
|
||||
'foo', text='bar'))
|
||||
assert len(self.name_id.extension_elements) == 1
|
||||
assert self.name_id.format == saml.NAMEID_FORMAT_EMAILADDRESS
|
||||
|
||||
def testNameIDText(self):
|
||||
"""Test text value of NameID element"""
|
||||
self.name_id.text = "tmatsuo@example.com"
|
||||
assert self.name_id.text == "tmatsuo@example.com"
|
||||
|
||||
def testSPProvidedID(self):
|
||||
"""Test for SPProvidedID attribute accessors"""
|
||||
self.name_id.sp_provided_id = "provided id"
|
||||
assert self.name_id.sp_provided_id == "provided id"
|
||||
|
||||
def testEmptyNameIDToAndFromStringMatch(self):
|
||||
"""Test name_id_from_string() with empty NameID"""
|
||||
string_from_name_id = self.name_id.to_string()
|
||||
new_name_id = saml.name_id_from_string(string_from_name_id)
|
||||
string_from_new_name_id = new_name_id.to_string()
|
||||
assert string_from_name_id == string_from_new_name_id
|
||||
|
||||
def testNameIDToAndFromStringMatch(self):
|
||||
"""Test name_id_from_string() with data"""
|
||||
self.name_id.format = saml.NAMEID_FORMAT_EMAILADDRESS
|
||||
self.name_id.text = "tmatsuo@example.com"
|
||||
self.name_id.name_qualifier = "name_qualifier"
|
||||
self.name_id.sp_name_qualifier = "sp_name_qualifier"
|
||||
string_from_name_id = self.name_id.to_string()
|
||||
new_name_id = saml.name_id_from_string(string_from_name_id)
|
||||
assert new_name_id.name_qualifier == "name_qualifier"
|
||||
assert new_name_id.sp_name_qualifier == "sp_name_qualifier"
|
||||
string_from_new_name_id = new_name_id.to_string()
|
||||
assert string_from_name_id == string_from_new_name_id
|
||||
|
||||
def testExtensionAttributes(self):
|
||||
"""Test extension attributes"""
|
||||
self.name_id.extension_attributes['hoge'] = 'fuga'
|
||||
self.name_id.extension_attributes['moge'] = 'muga'
|
||||
assert self.name_id.extension_attributes['hoge'] == 'fuga'
|
||||
assert self.name_id.extension_attributes['moge'] == 'muga'
|
||||
new_name_id = saml.name_id_from_string(self.name_id.to_string())
|
||||
assert new_name_id.extension_attributes['hoge'] == 'fuga'
|
||||
assert new_name_id.extension_attributes['moge'] == 'muga'
|
||||
|
||||
def testname_id_from_string(self):
|
||||
"""Test name_id_from_string() using test data"""
|
||||
name_id = saml.name_id_from_string(saml2_data.TEST_NAME_ID)
|
||||
assert name_id.format == saml.NAMEID_FORMAT_EMAILADDRESS
|
||||
assert name_id.text.strip() == "tmatsuo@example.com"
|
||||
assert name_id.sp_provided_id == "sp provided id"
|
||||
|
||||
|
||||
class TestIssuer:
|
||||
|
||||
def setup_class(self):
|
||||
self.issuer = saml.Issuer()
|
||||
|
||||
def testIssuerToAndFromString(self):
|
||||
"""Test issuer_from_string()"""
|
||||
self.issuer.text = "http://www.example.com/test"
|
||||
self.issuer.name_qualifier = "name_qualifier"
|
||||
self.issuer.sp_name_qualifier = "sp_name_qualifier"
|
||||
new_issuer = saml.issuer_from_string(self.issuer.to_string())
|
||||
assert self.issuer.text == new_issuer.text
|
||||
assert self.issuer.name_qualifier == new_issuer.name_qualifier
|
||||
assert self.issuer.sp_name_qualifier == new_issuer.sp_name_qualifier
|
||||
assert self.issuer.extension_elements == new_issuer.extension_elements
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test issuer_from_string() using test data"""
|
||||
issuer = saml.issuer_from_string(saml2_data.TEST_ISSUER)
|
||||
assert issuer.text.strip() == "http://www.example.com/test"
|
||||
new_issuer = saml.issuer_from_string(issuer.to_string())
|
||||
assert issuer.text == new_issuer.text
|
||||
assert issuer.extension_elements == new_issuer.extension_elements
|
||||
|
||||
|
||||
class TestSubjectLocality:
|
||||
|
||||
def setup_class(self):
|
||||
self.subject_locality = saml.SubjectLocality()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for SubjectLocality accessors"""
|
||||
self.subject_locality.address = "127.0.0.1"
|
||||
self.subject_locality.dns_name = "localhost"
|
||||
assert self.subject_locality.address == "127.0.0.1"
|
||||
assert self.subject_locality.dns_name == "localhost"
|
||||
new_subject_locality = saml.subject_locality_from_string(
|
||||
self.subject_locality.to_string())
|
||||
assert new_subject_locality.address == "127.0.0.1"
|
||||
assert new_subject_locality.dns_name == "localhost"
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test SubjectLocalityFromString() using test data"""
|
||||
|
||||
subject_locality = saml.subject_locality_from_string(
|
||||
saml2_data.TEST_SUBJECT_LOCALITY)
|
||||
assert subject_locality.address == "127.0.0.1"
|
||||
assert subject_locality.dns_name == "localhost"
|
||||
|
||||
new_subject_locality = saml.subject_locality_from_string(
|
||||
subject_locality.to_string())
|
||||
assert new_subject_locality.address == "127.0.0.1"
|
||||
assert new_subject_locality.dns_name == "localhost"
|
||||
assert subject_locality.to_string() == new_subject_locality.to_string()
|
||||
|
||||
|
||||
class TestAuthnContextClassRef:
|
||||
|
||||
def setup_class(self):
|
||||
self.authn_context_class_ref = saml.AuthnContextClassRef()
|
||||
self.text = "http://www.example.com/authnContextClassRef"
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for AuthnContextClassRef accessors"""
|
||||
self.authn_context_class_ref.text = self.text
|
||||
assert self.authn_context_class_ref.text == self.text
|
||||
new_authn_context_class_ref = saml.authn_context_class_ref_from_string(
|
||||
self.authn_context_class_ref.to_string())
|
||||
assert new_authn_context_class_ref.text == self.text
|
||||
assert self.authn_context_class_ref.to_string() == \
|
||||
new_authn_context_class_ref.to_string()
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test authn_context_class_ref_from_string() using test data"""
|
||||
authn_context_class_ref = saml.authn_context_class_ref_from_string(
|
||||
saml2_data.TEST_AUTHN_CONTEXT_CLASS_REF)
|
||||
assert authn_context_class_ref.text.strip() == self.text
|
||||
|
||||
|
||||
class TestAuthnContextDeclRef:
|
||||
|
||||
def setup_class(self):
|
||||
self.authn_context_decl_ref = saml.AuthnContextDeclRef()
|
||||
self.ref = "http://www.example.com/authnContextDeclRef"
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for AuthnContextDeclRef accessors"""
|
||||
self.authn_context_decl_ref.text = self.ref
|
||||
assert self.authn_context_decl_ref.text == self.ref
|
||||
new_authn_context_decl_ref = saml.authn_context_decl_ref_from_string(
|
||||
self.authn_context_decl_ref.to_string())
|
||||
assert new_authn_context_decl_ref.text == self.ref
|
||||
assert self.authn_context_decl_ref.to_string() == \
|
||||
new_authn_context_decl_ref.to_string()
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test authn_context_decl_ref_from_string() using test data"""
|
||||
authn_context_decl_ref = saml.authn_context_decl_ref_from_string(
|
||||
saml2_data.TEST_AUTHN_CONTEXT_DECL_REF)
|
||||
assert authn_context_decl_ref.text.strip() == self.ref
|
||||
|
||||
|
||||
class TestAuthnContextDecl:
|
||||
|
||||
def setup_class(self):
|
||||
self.authn_context_decl = saml.AuthnContextDecl()
|
||||
self.text = "http://www.example.com/authnContextDecl"
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for AuthnContextDecl accessors"""
|
||||
self.authn_context_decl.text = self.text
|
||||
assert self.authn_context_decl.text == self.text
|
||||
new_authn_context_decl = saml.authn_context_decl_from_string(
|
||||
self.authn_context_decl.to_string())
|
||||
assert new_authn_context_decl.text == self.text
|
||||
assert self.authn_context_decl.to_string() == \
|
||||
new_authn_context_decl.to_string()
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test authn_context_decl_from_string() using test data"""
|
||||
authn_context_decl = saml.authn_context_decl_from_string(
|
||||
saml2_data.TEST_AUTHN_CONTEXT_DECL)
|
||||
assert authn_context_decl.text.strip() == self.text
|
||||
|
||||
|
||||
class TestAuthenticatingAuthority:
|
||||
|
||||
def setup_class(self):
|
||||
self.authenticating_authority = saml.AuthenticatingAuthority()
|
||||
self.text = "http://www.example.com/authenticatingAuthority"
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for AuthenticatingAuthority accessors"""
|
||||
self.authenticating_authority.text = self.text
|
||||
assert self.authenticating_authority.text == self.text
|
||||
new_authenticating_authority = saml.authenticating_authority_from_string(
|
||||
self.authenticating_authority.to_string())
|
||||
assert new_authenticating_authority.text == self.text
|
||||
assert self.authenticating_authority.to_string() == \
|
||||
new_authenticating_authority.to_string()
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test authenticating_authority_from_string() using test data"""
|
||||
authenticating_authority = saml.authenticating_authority_from_string(
|
||||
saml2_data.TEST_AUTHENTICATING_AUTHORITY)
|
||||
assert authenticating_authority.text.strip() == self.text
|
||||
|
||||
class TestAuthnContext:
|
||||
|
||||
def setup_class(self):
|
||||
self.authn_context = saml.AuthnContext()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for AuthnContext accessors"""
|
||||
self.authn_context.authn_context_class_ref = \
|
||||
saml.authn_context_class_ref_from_string(
|
||||
saml2_data.TEST_AUTHN_CONTEXT_CLASS_REF)
|
||||
self.authn_context.authn_context_decl_ref = \
|
||||
saml.authn_context_decl_ref_from_string(
|
||||
saml2_data.TEST_AUTHN_CONTEXT_DECL_REF)
|
||||
self.authn_context.authn_context_decl = \
|
||||
saml.authn_context_decl_from_string(
|
||||
saml2_data.TEST_AUTHN_CONTEXT_DECL)
|
||||
self.authn_context.authenticating_authority.append(
|
||||
saml.authenticating_authority_from_string(
|
||||
saml2_data.TEST_AUTHENTICATING_AUTHORITY))
|
||||
assert self.authn_context.authn_context_class_ref.text.strip() == \
|
||||
"http://www.example.com/authnContextClassRef"
|
||||
assert self.authn_context.authn_context_decl_ref.text.strip() == \
|
||||
"http://www.example.com/authnContextDeclRef"
|
||||
assert self.authn_context.authn_context_decl.text.strip() == \
|
||||
"http://www.example.com/authnContextDecl"
|
||||
assert self.authn_context.authenticating_authority[0].text.strip() == \
|
||||
"http://www.example.com/authenticatingAuthority"
|
||||
new_authn_context = saml.authn_context_from_string(
|
||||
self.authn_context.to_string())
|
||||
assert self.authn_context.to_string() == new_authn_context.to_string()
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test authn_context_from_string() using test data"""
|
||||
authn_context = saml.authn_context_from_string(saml2_data.TEST_AUTHN_CONTEXT)
|
||||
assert authn_context.authn_context_class_ref.text.strip() == \
|
||||
saml.URN_PASSWORD
|
||||
|
||||
|
||||
class TestAuthnStatement:
|
||||
|
||||
def setup_class(self):
|
||||
self.authn_statem = saml.AuthnStatement()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for AuthnStatement accessors"""
|
||||
self.authn_statem.authn_instant = "2007-08-31T01:05:02Z"
|
||||
self.authn_statem.session_not_on_or_after = "2007-09-14T01:05:02Z"
|
||||
self.authn_statem.session_index = "sessionindex"
|
||||
self.authn_statem.authn_context = saml.AuthnContext()
|
||||
self.authn_statem.authn_context.authn_context_class_ref = \
|
||||
saml.authn_context_class_ref_from_string(
|
||||
saml2_data.TEST_AUTHN_CONTEXT_CLASS_REF)
|
||||
self.authn_statem.authn_context.authn_context_decl_ref = \
|
||||
saml.authn_context_decl_ref_from_string(
|
||||
saml2_data.TEST_AUTHN_CONTEXT_DECL_REF)
|
||||
self.authn_statem.authn_context.authn_context_decl = \
|
||||
saml.authn_context_decl_from_string(
|
||||
saml2_data.TEST_AUTHN_CONTEXT_DECL)
|
||||
self.authn_statem.authn_context.authenticating_authority.append(
|
||||
saml.authenticating_authority_from_string(
|
||||
saml2_data.TEST_AUTHENTICATING_AUTHORITY))
|
||||
|
||||
new_as = saml.authn_statement_from_string(self.authn_statem.to_string())
|
||||
assert new_as.authn_instant == "2007-08-31T01:05:02Z"
|
||||
assert new_as.session_index == "sessionindex"
|
||||
assert new_as.session_not_on_or_after == "2007-09-14T01:05:02Z"
|
||||
assert new_as.authn_context.authn_context_class_ref.text.strip() == \
|
||||
"http://www.example.com/authnContextClassRef"
|
||||
assert new_as.authn_context.authn_context_decl_ref.text.strip() == \
|
||||
"http://www.example.com/authnContextDeclRef"
|
||||
assert new_as.authn_context.authn_context_decl.text.strip() == \
|
||||
"http://www.example.com/authnContextDecl"
|
||||
assert new_as.authn_context.authenticating_authority[0].text.strip() \
|
||||
== "http://www.example.com/authenticatingAuthority"
|
||||
assert self.authn_statem.to_string() == new_as.to_string()
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test authn_statement_from_string() using test data"""
|
||||
authn_statem = saml.authn_statement_from_string(saml2_data.TEST_AUTHN_STATEMENT)
|
||||
assert authn_statem.authn_instant == "2007-08-31T01:05:02Z"
|
||||
assert authn_statem.session_not_on_or_after == "2007-09-14T01:05:02Z"
|
||||
assert authn_statem.authn_context.authn_context_class_ref.text.strip() == \
|
||||
saml.URN_PASSWORD
|
||||
|
||||
|
||||
class TestAttributeValue:
|
||||
|
||||
def setup_class(self):
|
||||
self.attribute_value = saml.AttributeValue()
|
||||
self.text = "value for test attribute"
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for AttributeValue accessors"""
|
||||
|
||||
self.attribute_value.text = self.text
|
||||
new_attribute_value = saml.attribute_value_from_string(
|
||||
self.attribute_value.to_string())
|
||||
assert new_attribute_value.text.strip() == self.text
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test attribute_value_from_string() using test data"""
|
||||
|
||||
attribute_value = saml.attribute_value_from_string(
|
||||
saml2_data.TEST_ATTRIBUTE_VALUE)
|
||||
assert attribute_value.text.strip() == self.text
|
||||
|
||||
BASIC_STR_AV = """<?xml version="1.0" encoding="utf-8"?>
|
||||
<Attribute xmlns="urn:oasis:names:tc:SAML:2.0:assertion"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"
|
||||
Name="FirstName">
|
||||
<AttributeValue xsi:type="xs:string">By-Tor</AttributeValue>
|
||||
</Attribute>"""
|
||||
|
||||
BASIC_INT_AV = """<?xml version="1.0" encoding="utf-8"?>
|
||||
<Attribute xmlns="urn:oasis:names:tc:SAML:2.0:assertion"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"
|
||||
Name="age">
|
||||
<AttributeValue xsi:type="xs:int">23</AttributeValue>
|
||||
</Attribute>"""
|
||||
|
||||
BASIC_NOT_INT_AV = """<?xml version="1.0" encoding="utf-8"?>
|
||||
<Attribute xmlns="urn:oasis:names:tc:SAML:2.0:assertion"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"
|
||||
Name="age">
|
||||
<AttributeValue xsi:type="xs:int">foo</AttributeValue>
|
||||
</Attribute>"""
|
||||
|
||||
BASIC_BOOLEAN_TRUE_AV = """<?xml version="1.0" encoding="utf-8"?>
|
||||
<Attribute xmlns="urn:oasis:names:tc:SAML:2.0:assertion"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"
|
||||
Name="on-off">
|
||||
<AttributeValue xsi:type="xs:boolean">true</AttributeValue>
|
||||
</Attribute>"""
|
||||
|
||||
BASIC_BOOLEAN_FALSE_AV = """<?xml version="1.0" encoding="utf-8"?>
|
||||
<Attribute xmlns="urn:oasis:names:tc:SAML:2.0:assertion"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"
|
||||
Name="on-off">
|
||||
<AttributeValue xsi:type="xs:boolean">false</AttributeValue>
|
||||
</Attribute>"""
|
||||
|
||||
BASIC_BASE64_AV = """<?xml version="1.0" encoding="utf-8"?>
|
||||
<Attribute xmlns="urn:oasis:names:tc:SAML:2.0:assertion"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"
|
||||
Name="FirstName">
|
||||
<AttributeValue
|
||||
xsi:type="xs:base64Binary">VU5JTkVUVA==</AttributeValue>
|
||||
</Attribute>"""
|
||||
|
||||
X500_AV = """<?xml version="1.0" encoding="utf-8"?>
|
||||
<Attribute xmlns="urn:oasis:names:tc:SAML:2.0:assertion"
|
||||
xmlns:x500="urn:oasis:names:tc:SAML:2.0:profiles:attribute:X500"
|
||||
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
|
||||
Name="urn:oid:2.5.4.42" FriendlyName="givenName">
|
||||
<AttributeValue xsi:type="xs:string" x500:Encoding="LDAP">Steven
|
||||
</AttributeValue>
|
||||
</Attribute>"""
|
||||
|
||||
UUID_AV = """<?xml version="1.0" encoding="utf-8"?>
|
||||
<Attribute xmlns="urn:oasis:names:tc:SAML:2.0:assertion"
|
||||
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
|
||||
Name="urn:uuid:6c9d0ec8-dd2d-11cc-abdd-080009353559"
|
||||
FriendlyName="pre_auth_req">
|
||||
<AttributeValue xsi:type="xs:integer">1</AttributeValue>
|
||||
</Attribute>"""
|
||||
|
||||
class TestAttribute:
|
||||
|
||||
def setup_class(self):
|
||||
self.attribute = saml.Attribute()
|
||||
self.text = ["value of test attribute",
|
||||
"value1 of test attribute",
|
||||
"value2 of test attribute"]
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for Attribute accessors"""
|
||||
self.attribute.name = "testAttribute"
|
||||
self.attribute.name_format = saml.NAME_FORMAT_URI
|
||||
self.attribute.friendly_name = "test attribute"
|
||||
self.attribute.attribute_value.append(saml.AttributeValue())
|
||||
self.attribute.attribute_value[0].text = self.text[0]
|
||||
|
||||
new_attribute = saml.attribute_from_string(self.attribute.to_string())
|
||||
assert new_attribute.name == "testAttribute"
|
||||
assert new_attribute.name_format == saml.NAME_FORMAT_URI
|
||||
assert new_attribute.friendly_name == "test attribute"
|
||||
assert new_attribute.attribute_value[0].text.strip() == self.text[0]
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test attribute_from_string() using test data"""
|
||||
attribute = saml.attribute_from_string(saml2_data.TEST_ATTRIBUTE)
|
||||
assert attribute.name == "testAttribute"
|
||||
assert attribute.name_format == saml.NAME_FORMAT_UNSPECIFIED
|
||||
assert attribute.friendly_name == "test attribute"
|
||||
assert attribute.attribute_value[0].text.strip() == self.text[1]
|
||||
assert attribute.attribute_value[1].text.strip() == self.text[2]
|
||||
# test again
|
||||
attribute = saml.attribute_from_string(attribute.to_string())
|
||||
assert attribute.name == "testAttribute"
|
||||
assert attribute.name_format == saml.NAME_FORMAT_UNSPECIFIED
|
||||
assert attribute.friendly_name == "test attribute"
|
||||
assert attribute.attribute_value[0].text.strip() == self.text[1]
|
||||
assert attribute.attribute_value[1].text.strip() == self.text[2]
|
||||
|
||||
def test_basic_str(self):
|
||||
attribute = saml.attribute_from_string(BASIC_STR_AV)
|
||||
print attribute
|
||||
assert attribute.attribute_value[0].text.strip() == "By-Tor"
|
||||
|
||||
def test_basic_int(self):
|
||||
attribute = saml.attribute_from_string(BASIC_INT_AV)
|
||||
print attribute
|
||||
assert attribute.attribute_value[0].text == "23"
|
||||
|
||||
def test_basic_not_int(self):
|
||||
raises(ValueError, "saml.attribute_from_string(BASIC_NOT_INT_AV)")
|
||||
|
||||
def test_basic_base64(self):
|
||||
attribute = saml.attribute_from_string(BASIC_BASE64_AV)
|
||||
print attribute
|
||||
assert attribute.attribute_value[0].text == "VU5JTkVUVA=="
|
||||
assert attribute.attribute_value[0].type == "xs:base64Binary"
|
||||
|
||||
def test_basic_boolean_true(self):
|
||||
attribute = saml.attribute_from_string(BASIC_BOOLEAN_TRUE_AV)
|
||||
print attribute
|
||||
assert attribute.attribute_value[0].text.lower() == "true"
|
||||
|
||||
def test_basic_boolean_false(self):
|
||||
attribute = saml.attribute_from_string(BASIC_BOOLEAN_FALSE_AV)
|
||||
print attribute
|
||||
assert attribute.attribute_value[0].text.lower() == "false"
|
||||
|
||||
class TestAttributeStatement:
|
||||
|
||||
def setup_class(self):
|
||||
self.attr_statem = saml.AttributeStatement()
|
||||
self.text = ["value of test attribute",
|
||||
"value1 of test attribute",
|
||||
"value2 of test attribute",
|
||||
"value1 of test attribute2",
|
||||
"value2 of test attribute2",]
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for Attribute accessors"""
|
||||
self.attr_statem.attribute.append(saml.Attribute())
|
||||
self.attr_statem.attribute.append(saml.Attribute())
|
||||
self.attr_statem.attribute[0].name = "testAttribute"
|
||||
self.attr_statem.attribute[0].name_format = saml.NAME_FORMAT_URI
|
||||
self.attr_statem.attribute[0].friendly_name = "test attribute"
|
||||
self.attr_statem.attribute[0].attribute_value.append(saml.AttributeValue())
|
||||
self.attr_statem.attribute[0].attribute_value[0].text = self.text[0]
|
||||
|
||||
self.attr_statem.attribute[1].name = "testAttribute2"
|
||||
self.attr_statem.attribute[1].name_format = saml.NAME_FORMAT_UNSPECIFIED
|
||||
self.attr_statem.attribute[1].friendly_name = self.text[2]
|
||||
self.attr_statem.attribute[1].attribute_value.append(saml.AttributeValue())
|
||||
self.attr_statem.attribute[1].attribute_value[0].text = self.text[2]
|
||||
|
||||
new_as = saml.attribute_statement_from_string(self.attr_statem.to_string())
|
||||
assert new_as.attribute[0].name == "testAttribute"
|
||||
assert new_as.attribute[0].name_format == saml.NAME_FORMAT_URI
|
||||
assert new_as.attribute[0].friendly_name == "test attribute"
|
||||
assert new_as.attribute[0].attribute_value[0].text.strip() == self.text[0]
|
||||
assert new_as.attribute[1].name == "testAttribute2"
|
||||
assert new_as.attribute[1].name_format == saml.NAME_FORMAT_UNSPECIFIED
|
||||
assert new_as.attribute[1].friendly_name == "value2 of test attribute"
|
||||
assert new_as.attribute[1].attribute_value[0].text.strip() == self.text[2]
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test attribute_statement_from_string() using test data"""
|
||||
attr_statem = saml.attribute_statement_from_string( \
|
||||
saml2_data.TEST_ATTRIBUTE_STATEMENT)
|
||||
assert attr_statem.attribute[0].name == "testAttribute"
|
||||
assert attr_statem.attribute[0].name_format == saml.NAME_FORMAT_UNSPECIFIED
|
||||
assert attr_statem.attribute[0].friendly_name == "test attribute"
|
||||
assert attr_statem.attribute[0].attribute_value[0].text.strip() == self.text[1]
|
||||
assert attr_statem.attribute[0].attribute_value[1].text.strip() == self.text[2]
|
||||
assert attr_statem.attribute[1].name == "http://www.example.com/testAttribute2"
|
||||
assert attr_statem.attribute[1].name_format == saml.NAME_FORMAT_URI
|
||||
assert attr_statem.attribute[1].friendly_name == "test attribute2"
|
||||
assert attr_statem.attribute[1].attribute_value[0].text.strip() == self.text[3]
|
||||
assert attr_statem.attribute[1].attribute_value[1].text.strip() == self.text[4]
|
||||
|
||||
# test again
|
||||
attr_statem2 = saml.attribute_statement_from_string(attr_statem.to_string())
|
||||
assert attr_statem2.attribute[0].name == "testAttribute"
|
||||
assert attr_statem2.attribute[0].name_format == saml.NAME_FORMAT_UNSPECIFIED
|
||||
assert attr_statem2.attribute[0].friendly_name == "test attribute"
|
||||
assert attr_statem2.attribute[0].attribute_value[0].text.strip() == self.text[1]
|
||||
assert attr_statem2.attribute[0].attribute_value[1].text.strip() == self.text[2]
|
||||
assert attr_statem2.attribute[1].name == "http://www.example.com/testAttribute2"
|
||||
assert attr_statem2.attribute[1].name_format == saml.NAME_FORMAT_URI
|
||||
assert attr_statem2.attribute[1].friendly_name == "test attribute2"
|
||||
assert attr_statem2.attribute[1].attribute_value[0].text.strip() == self.text[3]
|
||||
assert attr_statem2.attribute[1].attribute_value[1].text.strip() == self.text[4]
|
||||
|
||||
|
||||
class TestSubjectConfirmationData:
|
||||
|
||||
def setup_class(self):
|
||||
self.scd = saml.SubjectConfirmationData()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for SubjectConfirmationData accessors"""
|
||||
|
||||
self.scd.not_before = "2007-08-31T01:05:02Z"
|
||||
self.scd.not_on_or_after = "2007-09-14T01:05:02Z"
|
||||
self.scd.recipient = "recipient"
|
||||
self.scd.in_response_to = "responseID"
|
||||
self.scd.address = "127.0.0.1"
|
||||
new_scd = saml.subject_confirmation_data_from_string(self.scd.to_string())
|
||||
assert new_scd.not_before == "2007-08-31T01:05:02Z"
|
||||
assert new_scd.not_on_or_after == "2007-09-14T01:05:02Z"
|
||||
assert new_scd.recipient == "recipient"
|
||||
assert new_scd.in_response_to == "responseID"
|
||||
assert new_scd.address == "127.0.0.1"
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test subject_confirmation_data_from_string() using test data"""
|
||||
|
||||
scd = saml.subject_confirmation_data_from_string(
|
||||
saml2_data.TEST_SUBJECT_CONFIRMATION_DATA)
|
||||
assert scd.not_before == "2007-08-31T01:05:02Z"
|
||||
assert scd.not_on_or_after == "2007-09-14T01:05:02Z"
|
||||
assert scd.recipient == "recipient"
|
||||
assert scd.in_response_to == "responseID"
|
||||
assert scd.address == "127.0.0.1"
|
||||
|
||||
|
||||
class TestSubjectConfirmation:
|
||||
|
||||
def setup_class(self):
|
||||
self.sc = saml.SubjectConfirmation()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for SubjectConfirmation accessors"""
|
||||
self.sc.name_id = saml.name_id_from_string(saml2_data.TEST_NAME_ID)
|
||||
self.sc.method = saml.SUBJECT_CONFIRMATION_METHOD_BEARER
|
||||
self.sc.subject_confirmation_data = saml.subject_confirmation_data_from_string(
|
||||
saml2_data.TEST_SUBJECT_CONFIRMATION_DATA)
|
||||
new_sc = saml.subject_confirmation_from_string(self.sc.to_string())
|
||||
assert new_sc.name_id.sp_provided_id == "sp provided id"
|
||||
assert new_sc.method == saml.SUBJECT_CONFIRMATION_METHOD_BEARER
|
||||
assert new_sc.subject_confirmation_data.not_before == \
|
||||
"2007-08-31T01:05:02Z"
|
||||
assert new_sc.subject_confirmation_data.not_on_or_after == \
|
||||
"2007-09-14T01:05:02Z"
|
||||
assert new_sc.subject_confirmation_data.recipient == "recipient"
|
||||
assert new_sc.subject_confirmation_data.in_response_to == "responseID"
|
||||
assert new_sc.subject_confirmation_data.address == "127.0.0.1"
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test subject_confirmation_from_string() using test data"""
|
||||
|
||||
sc = saml.subject_confirmation_from_string(
|
||||
saml2_data.TEST_SUBJECT_CONFIRMATION)
|
||||
assert sc.name_id.sp_provided_id == "sp provided id"
|
||||
assert sc.method == saml.SUBJECT_CONFIRMATION_METHOD_BEARER
|
||||
assert sc.subject_confirmation_data.not_before == "2007-08-31T01:05:02Z"
|
||||
assert sc.subject_confirmation_data.not_on_or_after == "2007-09-14T01:05:02Z"
|
||||
assert sc.subject_confirmation_data.recipient == "recipient"
|
||||
assert sc.subject_confirmation_data.in_response_to == "responseID"
|
||||
assert sc.subject_confirmation_data.address == "127.0.0.1"
|
||||
|
||||
|
||||
class TestSubject:
|
||||
|
||||
def setup_class(self):
|
||||
self.subject = saml.Subject()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for Subject accessors"""
|
||||
self.subject.name_id = saml.name_id_from_string(saml2_data.TEST_NAME_ID)
|
||||
self.subject.subject_confirmation.append(
|
||||
saml.subject_confirmation_from_string(
|
||||
saml2_data.TEST_SUBJECT_CONFIRMATION))
|
||||
new_subject = saml.subject_from_string(self.subject.to_string())
|
||||
assert new_subject.name_id.sp_provided_id == "sp provided id"
|
||||
assert new_subject.name_id.text.strip() == "tmatsuo@example.com"
|
||||
assert new_subject.name_id.format == saml.NAMEID_FORMAT_EMAILADDRESS
|
||||
assert isinstance(new_subject.subject_confirmation[0],
|
||||
saml.SubjectConfirmation)
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for subject_from_string() using test data."""
|
||||
|
||||
subject = saml.subject_from_string(saml2_data.TEST_SUBJECT)
|
||||
assert subject.name_id.sp_provided_id == "sp provided id"
|
||||
assert subject.name_id.text.strip() == "tmatsuo@example.com"
|
||||
assert subject.name_id.format == saml.NAMEID_FORMAT_EMAILADDRESS
|
||||
assert isinstance(subject.subject_confirmation[0],
|
||||
saml.SubjectConfirmation)
|
||||
|
||||
|
||||
class TestCondition:
|
||||
|
||||
def setup_class(self):
|
||||
self.condition = saml.Condition()
|
||||
self.name = "{%s}type" % saml.XSI_NAMESPACE
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for Condition accessors."""
|
||||
self.condition.extension_attributes[self.name] = "test"
|
||||
self.condition.extension_attributes['ExtendedAttribute'] = "value"
|
||||
new_condition = saml.condition_from_string(self.condition.to_string())
|
||||
assert new_condition.extension_attributes[self.name] == "test"
|
||||
assert new_condition.extension_attributes["ExtendedAttribute"] == "value"
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for condition_from_string() using test data."""
|
||||
condition = saml.condition_from_string(saml2_data.TEST_CONDITION)
|
||||
assert condition.extension_attributes[self.name] == "test"
|
||||
assert condition.extension_attributes["ExtendedAttribute"] == "value"
|
||||
|
||||
|
||||
class TestAudience:
|
||||
|
||||
def setup_class(self):
|
||||
self.audience = saml.Audience()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for Audience accessors"""
|
||||
|
||||
self.audience.text = "http://www.example.com/Audience"
|
||||
new_audience = saml.audience_from_string(self.audience.to_string())
|
||||
assert new_audience.text.strip() == "http://www.example.com/Audience"
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test audience_from_string using test data"""
|
||||
|
||||
audience = saml.audience_from_string(saml2_data.TEST_AUDIENCE)
|
||||
assert audience.text.strip() == "http://www.example.com/Audience"
|
||||
|
||||
|
||||
class TestAudienceRestriction:
|
||||
def setup_class(self):
|
||||
self.audience_restriction = saml.AudienceRestriction()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for AudienceRestriction accessors"""
|
||||
|
||||
self.audience_restriction.audience = \
|
||||
saml.audience_from_string(saml2_data.TEST_AUDIENCE)
|
||||
new_audience = saml.audience_restriction_from_string(
|
||||
self.audience_restriction.to_string())
|
||||
assert self.audience_restriction.audience.text.strip() == \
|
||||
"http://www.example.com/Audience"
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test audience_restriction_from_string using test data"""
|
||||
|
||||
audience_restriction = saml.audience_restriction_from_string(
|
||||
saml2_data.TEST_AUDIENCE_RESTRICTION)
|
||||
assert audience_restriction.audience.text.strip() == \
|
||||
"http://www.example.com/Audience"
|
||||
|
||||
|
||||
class TestOneTimeUse:
|
||||
|
||||
def setup_class(self):
|
||||
self.one_time_use = saml.OneTimeUse()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for OneTimeUse accessors"""
|
||||
assert isinstance(self.one_time_use, saml.OneTimeUse)
|
||||
assert isinstance(self.one_time_use, saml.Condition)
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test one_time_use_from_string() using test data"""
|
||||
one_time_use = saml.one_time_use_from_string(saml2_data.TEST_ONE_TIME_USE)
|
||||
assert isinstance(one_time_use, saml.OneTimeUse)
|
||||
assert isinstance(one_time_use, saml.Condition)
|
||||
|
||||
|
||||
class TestProxyRestriction:
|
||||
|
||||
def setup_class(self):
|
||||
self.proxy_restriction = saml.ProxyRestriction()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for ProxyRestriction accessors"""
|
||||
|
||||
assert isinstance(self.proxy_restriction, saml.Condition)
|
||||
self.proxy_restriction.count = "2"
|
||||
self.proxy_restriction.audience.append(saml.audience_from_string(
|
||||
saml2_data.TEST_AUDIENCE))
|
||||
new_proxy_restriction = saml.proxy_restriction_from_string(
|
||||
self.proxy_restriction.to_string())
|
||||
assert new_proxy_restriction.count == "2"
|
||||
assert new_proxy_restriction.audience[0].text.strip() == \
|
||||
"http://www.example.com/Audience"
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test proxy_restriction_from_string() using test data"""
|
||||
|
||||
proxy_restriction = saml.proxy_restriction_from_string(
|
||||
saml2_data.TEST_PROXY_RESTRICTION)
|
||||
assert proxy_restriction.count == "2"
|
||||
assert proxy_restriction.audience[0].text.strip() == \
|
||||
"http://www.example.com/Audience"
|
||||
|
||||
class TestConditions:
|
||||
|
||||
def setup_class(self):
|
||||
self.conditions = saml.Conditions()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for Conditions accessors"""
|
||||
self.conditions.not_before = "2007-08-31T01:05:02Z"
|
||||
self.conditions.not_on_or_after = "2007-09-14T01:05:02Z"
|
||||
self.conditions.condition.append(saml.Condition())
|
||||
self.conditions.audience_restriction.append(saml.AudienceRestriction())
|
||||
self.conditions.one_time_use.append(saml.OneTimeUse())
|
||||
self.conditions.proxy_restriction.append(saml.ProxyRestriction())
|
||||
new_conditions = saml.conditions_from_string(self.conditions.to_string())
|
||||
assert new_conditions.not_before == "2007-08-31T01:05:02Z"
|
||||
assert new_conditions.not_on_or_after == "2007-09-14T01:05:02Z"
|
||||
assert isinstance(new_conditions.condition[0], saml.Condition)
|
||||
assert isinstance(new_conditions.audience_restriction[0],
|
||||
saml.AudienceRestriction)
|
||||
assert isinstance(new_conditions.one_time_use[0],
|
||||
saml.OneTimeUse)
|
||||
assert isinstance(new_conditions.proxy_restriction[0],
|
||||
saml.ProxyRestriction)
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test conditions_from_string() using test data"""
|
||||
new_conditions = saml.conditions_from_string(saml2_data.TEST_CONDITIONS)
|
||||
assert new_conditions.not_before == "2007-08-31T01:05:02Z"
|
||||
assert new_conditions.not_on_or_after == "2007-09-14T01:05:02Z"
|
||||
assert isinstance(new_conditions.condition[0], saml.Condition)
|
||||
assert isinstance(new_conditions.audience_restriction[0],
|
||||
saml.AudienceRestriction)
|
||||
assert isinstance(new_conditions.one_time_use[0],
|
||||
saml.OneTimeUse)
|
||||
assert isinstance(new_conditions.proxy_restriction[0],
|
||||
saml.ProxyRestriction)
|
||||
|
||||
class TestAssertionIDRef:
|
||||
|
||||
def setup_class(self):
|
||||
self.assertion_id_ref = saml.AssertionIDRef()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for AssertionIDRef accessors"""
|
||||
self.assertion_id_ref.text = "zzlieajngjbkjggjldmgindkckkolcblndbghlhm"
|
||||
new_assertion_id_ref = saml.assertion_id_ref_from_string(
|
||||
self.assertion_id_ref.to_string())
|
||||
assert new_assertion_id_ref.text == \
|
||||
"zzlieajngjbkjggjldmgindkckkolcblndbghlhm"
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test assertion_id_ref_from_string() using test data"""
|
||||
new_assertion_id_ref = saml.assertion_id_ref_from_string(
|
||||
saml2_data.TEST_ASSERTION_ID_REF)
|
||||
assert new_assertion_id_ref.text.strip() == \
|
||||
"zzlieajngjbkjggjldmgindkckkolcblndbghlhm"
|
||||
|
||||
|
||||
class TestAssertionURIRef:
|
||||
|
||||
def setup_class(self):
|
||||
self.assertion_uri_ref = saml.AssertionURIRef()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for AssertionURIRef accessors"""
|
||||
self.assertion_uri_ref.text = "http://www.example.com/AssertionURIRef"
|
||||
new_assertion_uri_ref = saml.assertion_uri_ref_from_string(
|
||||
self.assertion_uri_ref.to_string())
|
||||
assert new_assertion_uri_ref.text == \
|
||||
"http://www.example.com/AssertionURIRef"
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test assertion_uri_ref_from_string() using test data"""
|
||||
new_assertion_uri_ref = saml.assertion_uri_ref_from_string(
|
||||
saml2_data.TEST_ASSERTION_URI_REF)
|
||||
assert new_assertion_uri_ref.text.strip() == \
|
||||
"http://www.example.com/AssertionURIRef"
|
||||
|
||||
|
||||
class TestAction:
|
||||
|
||||
def setup_class(self):
|
||||
self.action = saml.Action()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for Action accessors"""
|
||||
self.action.namespace = "http://www.example.com/Namespace"
|
||||
new_action = saml.action_from_string(self.action.to_string())
|
||||
assert new_action.namespace == "http://www.example.com/Namespace"
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test action_from_string() using test data"""
|
||||
new_action = saml.action_from_string(saml2_data.TEST_ACTION)
|
||||
assert new_action.namespace == "http://www.example.com/Namespace"
|
||||
|
||||
|
||||
class TestEvidence:
|
||||
|
||||
def setup_class(self):
|
||||
self.evidence = saml.Evidence()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for Evidence accessors"""
|
||||
self.evidence.assertion_id_ref.append(saml.AssertionIDRef())
|
||||
self.evidence.assertion_uri_ref.append(saml.AssertionURIRef())
|
||||
self.evidence.assertion.append(saml.Assertion())
|
||||
self.evidence.encrypted_assertion.append(saml.EncryptedAssertion())
|
||||
new_evidence = saml.evidence_from_string(self.evidence.to_string())
|
||||
print new_evidence
|
||||
assert self.evidence.to_string() == new_evidence.to_string()
|
||||
assert isinstance(new_evidence.assertion_id_ref[0],
|
||||
saml.AssertionIDRef)
|
||||
assert isinstance(new_evidence.assertion_uri_ref[0],
|
||||
saml.AssertionURIRef)
|
||||
assert len(new_evidence.assertion) == 1
|
||||
assert isinstance(new_evidence.assertion[0], saml.Assertion)
|
||||
assert len(new_evidence.encrypted_assertion) == 1
|
||||
assert isinstance(new_evidence.encrypted_assertion[0],
|
||||
saml.EncryptedAssertion)
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test evidence_from_string() using test data"""
|
||||
# TODO:
|
||||
pass
|
||||
|
||||
|
||||
class TestAuthzDecisionStatement:
|
||||
|
||||
def setup_class(self):
|
||||
self.authz_decision_statement = saml.AuthzDecisionStatement()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for AuthzDecisionStatement accessors"""
|
||||
self.authz_decision_statement.resource = "http://www.example.com/Resource"
|
||||
self.authz_decision_statement.decision = saml.DECISION_TYPE_PERMIT
|
||||
self.authz_decision_statement.action.append(saml.Action())
|
||||
self.authz_decision_statement.evidence.append(saml.Evidence())
|
||||
new_authz_decision_statement = saml.authz_decision_statement_from_string(
|
||||
self.authz_decision_statement.to_string())
|
||||
assert self.authz_decision_statement.to_string() == \
|
||||
new_authz_decision_statement.to_string()
|
||||
assert new_authz_decision_statement.resource == \
|
||||
"http://www.example.com/Resource"
|
||||
assert new_authz_decision_statement.decision == \
|
||||
saml.DECISION_TYPE_PERMIT
|
||||
assert isinstance(new_authz_decision_statement.action[0],
|
||||
saml.Action)
|
||||
assert isinstance(new_authz_decision_statement.evidence[0],
|
||||
saml.Evidence)
|
||||
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test authz_decision_statement_from_string() using test data"""
|
||||
# TODO:
|
||||
pass
|
||||
|
||||
class TestAdvice:
|
||||
|
||||
def setup_class(self):
|
||||
self.advice = saml.Advice()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for Advice accessors"""
|
||||
self.advice.assertion_id_ref.append(saml.AssertionIDRef())
|
||||
self.advice.assertion_uri_ref.append(saml.AssertionURIRef())
|
||||
self.advice.assertion.append(saml.Assertion())
|
||||
self.advice.encrypted_assertion.append(saml.EncryptedAssertion())
|
||||
new_advice = saml.advice_from_string(self.advice.to_string())
|
||||
assert self.advice.to_string() == new_advice.to_string()
|
||||
assert isinstance(new_advice.assertion_id_ref[0],
|
||||
saml.AssertionIDRef)
|
||||
assert isinstance(new_advice.assertion_uri_ref[0],
|
||||
saml.AssertionURIRef)
|
||||
assert isinstance(new_advice.assertion[0], saml.Assertion)
|
||||
assert isinstance(new_advice.encrypted_assertion[0],
|
||||
saml.EncryptedAssertion)
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test advice_from_string() using test data"""
|
||||
# TODO:
|
||||
pass
|
||||
|
||||
|
||||
class TestAssertion:
|
||||
|
||||
def setup_class(self):
|
||||
self.assertion = saml.Assertion()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for Assertion accessors"""
|
||||
self.assertion.id = "assertion id"
|
||||
self.assertion.version = saml2.VERSION
|
||||
self.assertion.issue_instant = "2007-08-31T01:05:02Z"
|
||||
self.assertion.issuer = saml.issuer_from_string(saml2_data.TEST_ISSUER)
|
||||
self.assertion.signature = ds.signature_from_string(
|
||||
ds_data.TEST_SIGNATURE)
|
||||
self.assertion.subject = saml.subject_from_string(saml2_data.TEST_SUBJECT)
|
||||
self.assertion.conditions = saml.conditions_from_string(
|
||||
saml2_data.TEST_CONDITIONS)
|
||||
self.assertion.advice = saml.Advice()
|
||||
self.assertion.statement.append(saml.Statement())
|
||||
self.assertion.authn_statement.append(saml.authn_statement_from_string(
|
||||
saml2_data.TEST_AUTHN_STATEMENT))
|
||||
self.assertion.authz_decision_statement.append(
|
||||
saml.AuthzDecisionStatement())
|
||||
self.assertion.attribute_statement.append(
|
||||
saml.attribute_statement_from_string(
|
||||
saml2_data.TEST_ATTRIBUTE_STATEMENT))
|
||||
|
||||
new_assertion = saml.assertion_from_string(self.assertion.to_string())
|
||||
assert new_assertion.id == "assertion id"
|
||||
assert new_assertion.version == saml2.VERSION
|
||||
assert new_assertion.issue_instant == "2007-08-31T01:05:02Z"
|
||||
assert isinstance(new_assertion.issuer, saml.Issuer)
|
||||
assert isinstance(new_assertion.signature, ds.Signature)
|
||||
assert isinstance(new_assertion.subject, saml.Subject)
|
||||
assert isinstance(new_assertion.conditions, saml.Conditions)
|
||||
assert isinstance(new_assertion.advice, saml.Advice)
|
||||
assert isinstance(new_assertion.statement[0], saml.Statement)
|
||||
assert isinstance(new_assertion.authn_statement[0],
|
||||
saml.AuthnStatement)
|
||||
assert isinstance(new_assertion.authz_decision_statement[0],
|
||||
saml.AuthzDecisionStatement)
|
||||
assert isinstance(new_assertion.attribute_statement[0],
|
||||
saml.AttributeStatement)
|
||||
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test assertion_from_string() using test data"""
|
||||
# TODO
|
||||
pass
|
||||
1166
tests/test_02_md.py
1166
tests/test_02_md.py
File diff suppressed because it is too large
Load Diff
@@ -1,535 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2009 Umeå University.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Tests for saml2.samlp"""
|
||||
|
||||
__author__ = 'roland.hedberg@adm.umu.se (Roland Hedberg)'
|
||||
|
||||
import unittest
|
||||
try:
|
||||
from xml.etree import ElementTree
|
||||
except ImportError:
|
||||
from elementtree import ElementTree
|
||||
import saml2
|
||||
from saml2 import saml, samlp
|
||||
import saml2_data, ds_data, samlp_data
|
||||
import xmldsig as ds
|
||||
|
||||
|
||||
class TestAbstractRequest:
|
||||
|
||||
def setup_class(self):
|
||||
self.ar = samlp.AbstractRequest()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for AbstractRequest accessors"""
|
||||
self.ar.id = "request id"
|
||||
self.ar.version = saml2.VERSION
|
||||
self.ar.issue_instant = "2007-09-14T01:05:02Z"
|
||||
self.ar.destination = "http://www.example.com/Destination"
|
||||
self.ar.consent = saml.CONSENT_UNSPECIFIED
|
||||
self.ar.issuer = saml.Issuer()
|
||||
self.ar.signature = ds.get_empty_signature()
|
||||
self.ar.extensions = samlp.Extensions()
|
||||
|
||||
new_ar = samlp.abstract_request_from_string(self.ar.to_string())
|
||||
assert new_ar.id == "request id"
|
||||
assert new_ar.version == saml2.VERSION
|
||||
assert new_ar.issue_instant == "2007-09-14T01:05:02Z"
|
||||
assert new_ar.destination == "http://www.example.com/Destination"
|
||||
assert new_ar.consent == saml.CONSENT_UNSPECIFIED
|
||||
assert isinstance(new_ar.issuer, saml.Issuer)
|
||||
assert isinstance(new_ar.signature, ds.Signature)
|
||||
assert isinstance(new_ar.extensions, samlp.Extensions)
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for abstract_request_from_string() using test data"""
|
||||
# TODO:
|
||||
pass
|
||||
|
||||
class TestStatusDetail:
|
||||
|
||||
def setup_class(self):
|
||||
self.status_detail = samlp.StatusDetail()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for StatusDetail accessors"""
|
||||
# TODO:
|
||||
pass
|
||||
|
||||
|
||||
class TestStatusMessage:
|
||||
|
||||
def setup_class(self):
|
||||
self.status_message = samlp.StatusMessage()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for StatusMessage accessors"""
|
||||
# TODO:
|
||||
pass
|
||||
|
||||
|
||||
class TestStatusCode:
|
||||
|
||||
def setup_class(self):
|
||||
self.status_code = samlp.StatusCode()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for StatusCode accessors"""
|
||||
self.status_code.value = samlp.STATUS_RESPONDER
|
||||
self.status_code.status_code = samlp.StatusCode(
|
||||
value=samlp.STATUS_REQUEST_DENIED)
|
||||
print self.status_code.__dict__
|
||||
new_status_code = samlp.status_code_from_string(self.status_code.to_string())
|
||||
assert new_status_code.value == samlp.STATUS_RESPONDER
|
||||
assert new_status_code.status_code.value == \
|
||||
samlp.STATUS_REQUEST_DENIED
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for status_code_from_string() using test data"""
|
||||
new_status_code = samlp.status_code_from_string(
|
||||
samlp_data.TEST_STATUS_CODE)
|
||||
assert new_status_code.value == samlp.STATUS_RESPONDER
|
||||
assert new_status_code.status_code.value == \
|
||||
samlp.STATUS_REQUEST_DENIED
|
||||
|
||||
|
||||
class TestStatus:
|
||||
|
||||
def setup_class(self):
|
||||
self.status = samlp.Status()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for Status accessors"""
|
||||
self.status.status_code = samlp.StatusCode()
|
||||
self.status.status_message = samlp.StatusMessage()
|
||||
self.status.status_detail = samlp.StatusDetail()
|
||||
new_status = samlp.status_from_string(self.status.to_string())
|
||||
assert isinstance(new_status.status_code, samlp.StatusCode)
|
||||
assert isinstance(new_status.status_message, samlp.StatusMessage)
|
||||
assert isinstance(new_status.status_detail, samlp.StatusDetail)
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for status_from_string using test data"""
|
||||
new_status = samlp.status_from_string(samlp_data.TEST_STATUS)
|
||||
assert isinstance(new_status.status_code, samlp.StatusCode)
|
||||
assert isinstance(new_status.status_code.status_code,
|
||||
samlp.StatusCode)
|
||||
assert isinstance(new_status.status_message, samlp.StatusMessage)
|
||||
assert isinstance(new_status.status_detail, samlp.StatusDetail)
|
||||
|
||||
class TestStatusResponse:
|
||||
|
||||
def setup_class(self):
|
||||
self.sr = samlp.StatusResponse()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for StatusResponse accessors"""
|
||||
self.sr.id = "response id"
|
||||
self.sr.in_response_to = "request id"
|
||||
self.sr.version = saml2.VERSION
|
||||
self.sr.issue_instant = "2007-09-14T01:05:02Z"
|
||||
self.sr.destination = "http://www.example.com/Destination"
|
||||
self.sr.consent = saml.CONSENT_UNSPECIFIED
|
||||
self.sr.issuer = saml.Issuer()
|
||||
self.sr.signature = ds.get_empty_signature()
|
||||
self.sr.extensions = samlp.Extensions()
|
||||
self.sr.status = samlp.Status()
|
||||
|
||||
new_sr = samlp.status_response_from_string(self.sr.to_string())
|
||||
assert new_sr.id == "response id"
|
||||
assert new_sr.in_response_to == "request id"
|
||||
assert new_sr.version == saml2.VERSION
|
||||
assert new_sr.issue_instant == "2007-09-14T01:05:02Z"
|
||||
assert new_sr.destination == "http://www.example.com/Destination"
|
||||
assert new_sr.consent == saml.CONSENT_UNSPECIFIED
|
||||
assert isinstance(new_sr.issuer, saml.Issuer)
|
||||
assert isinstance(new_sr.signature, ds.Signature)
|
||||
assert isinstance(new_sr.extensions, samlp.Extensions)
|
||||
assert isinstance(new_sr.status, samlp.Status)
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for status_response_from_string() using test data"""
|
||||
# TODO:
|
||||
pass
|
||||
|
||||
|
||||
class TestResponse:
|
||||
|
||||
def setup_class(self):
|
||||
self.response = samlp.Response()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for Response accessors"""
|
||||
self.response.id = "response id"
|
||||
self.response.in_response_to = "request id"
|
||||
self.response.version = saml2.VERSION
|
||||
self.response.issue_instant = "2007-09-14T01:05:02Z"
|
||||
self.response.destination = "http://www.example.com/Destination"
|
||||
self.response.consent = saml.CONSENT_UNSPECIFIED
|
||||
self.response.issuer = saml.Issuer()
|
||||
self.response.signature = ds.get_empty_signature()
|
||||
self.response.extensions = samlp.Extensions()
|
||||
self.response.status = samlp.Status()
|
||||
self.response.assertion.append(saml.Assertion())
|
||||
self.response.encrypted_assertion.append(saml.EncryptedAssertion())
|
||||
|
||||
new_response = samlp.response_from_string(self.response.to_string())
|
||||
assert new_response.id == "response id"
|
||||
assert new_response.in_response_to == "request id"
|
||||
assert new_response.version == saml2.VERSION
|
||||
assert new_response.issue_instant == "2007-09-14T01:05:02Z"
|
||||
assert new_response.destination == "http://www.example.com/Destination"
|
||||
assert new_response.consent == saml.CONSENT_UNSPECIFIED
|
||||
assert isinstance(new_response.issuer, saml.Issuer)
|
||||
assert isinstance(new_response.signature, ds.Signature)
|
||||
assert isinstance(new_response.extensions, samlp.Extensions)
|
||||
assert isinstance(new_response.status, samlp.Status)
|
||||
|
||||
assert isinstance(new_response.assertion[0], saml.Assertion)
|
||||
assert isinstance(new_response.encrypted_assertion[0],
|
||||
saml.EncryptedAssertion)
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for response_from_string() using test data"""
|
||||
# TODO:
|
||||
pass
|
||||
|
||||
class TestNameIDPolicy:
|
||||
|
||||
def setup_class(self):
|
||||
self.name_id_policy = samlp.NameIDPolicy()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for NameIDPolicy accessors"""
|
||||
self.name_id_policy.format = saml.NAMEID_FORMAT_EMAILADDRESS
|
||||
self.name_id_policy.sp_name_qualifier = saml.NAMEID_FORMAT_PERSISTENT
|
||||
self.name_id_policy.allow_create = 'false'
|
||||
|
||||
new_name_id_policy = samlp.name_id_policy_from_string(
|
||||
self.name_id_policy.to_string())
|
||||
|
||||
assert new_name_id_policy.format == saml.NAMEID_FORMAT_EMAILADDRESS
|
||||
assert new_name_id_policy.sp_name_qualifier == \
|
||||
saml.NAMEID_FORMAT_PERSISTENT
|
||||
assert new_name_id_policy.allow_create == 'false'
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for name_id_policy_from_string() using test data"""
|
||||
new_name_id_policy = samlp.name_id_policy_from_string(
|
||||
samlp_data.TEST_NAME_ID_POLICY)
|
||||
|
||||
assert new_name_id_policy.format == saml.NAMEID_FORMAT_EMAILADDRESS
|
||||
assert new_name_id_policy.sp_name_qualifier == \
|
||||
saml.NAMEID_FORMAT_PERSISTENT
|
||||
assert new_name_id_policy.allow_create == 'false'
|
||||
|
||||
|
||||
class TestIDPEntry:
|
||||
|
||||
def setup_class(self):
|
||||
self.idp_entry = samlp.IDPEntry()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for IDPEntry accessors"""
|
||||
self.idp_entry.provider_id = "http://www.example.com/provider"
|
||||
self.idp_entry.name = "the provider"
|
||||
self.idp_entry.loc = "http://www.example.com/Loc"
|
||||
|
||||
new_idp_entry = samlp.idp_entry_from_string(self.idp_entry.to_string())
|
||||
assert new_idp_entry.provider_id == "http://www.example.com/provider"
|
||||
assert new_idp_entry.name == "the provider"
|
||||
assert new_idp_entry.loc == "http://www.example.com/Loc"
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for idp_entry_from_string() using test data"""
|
||||
new_idp_entry = samlp.idp_entry_from_string(samlp_data.TEST_IDP_ENTRY)
|
||||
assert new_idp_entry.provider_id == "http://www.example.com/provider"
|
||||
assert new_idp_entry.name == "the provider"
|
||||
assert new_idp_entry.loc == "http://www.example.com/Loc"
|
||||
|
||||
|
||||
class TestIDPList:
|
||||
|
||||
def setup_class(self):
|
||||
self.idp_list = samlp.IDPList()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for IDPList accessors"""
|
||||
self.idp_list.idp_entry.append(samlp.idp_entry_from_string(
|
||||
samlp_data.TEST_IDP_ENTRY))
|
||||
self.idp_list.get_complete = samlp.GetComplete(
|
||||
text="http://www.example.com/GetComplete")
|
||||
new_idp_list = samlp.idp_list_from_string(self.idp_list.to_string())
|
||||
assert isinstance(new_idp_list.idp_entry[0], samlp.IDPEntry)
|
||||
assert new_idp_list.get_complete.text.strip() == \
|
||||
"http://www.example.com/GetComplete"
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for idp_list_from_string() using test data"""
|
||||
new_idp_list = samlp.idp_list_from_string(samlp_data.TEST_IDP_LIST)
|
||||
assert isinstance(new_idp_list.idp_entry[0], samlp.IDPEntry)
|
||||
assert new_idp_list.get_complete.text.strip() == \
|
||||
"http://www.example.com/GetComplete"
|
||||
|
||||
|
||||
class TestScoping:
|
||||
|
||||
def setup_class(self):
|
||||
self.scoping = samlp.Scoping()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for Scoping accessors"""
|
||||
|
||||
self.scoping.proxy_count = "1"
|
||||
self.scoping.idp_list = samlp.IDPList()
|
||||
self.scoping.requester_id.append(samlp.RequesterID())
|
||||
|
||||
new_scoping = samlp.scoping_from_string(self.scoping.to_string())
|
||||
|
||||
assert new_scoping.proxy_count == "1"
|
||||
assert isinstance(new_scoping.idp_list, samlp.IDPList)
|
||||
assert isinstance(new_scoping.requester_id[0], samlp.RequesterID)
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for scoping_from_string() using test data"""
|
||||
new_scoping = samlp.scoping_from_string(samlp_data.TEST_SCOPING)
|
||||
|
||||
assert new_scoping.proxy_count == "1"
|
||||
assert isinstance(new_scoping.idp_list, samlp.IDPList)
|
||||
assert isinstance(new_scoping.requester_id[0], samlp.RequesterID)
|
||||
|
||||
|
||||
class TestRequestedAuthnContext:
|
||||
|
||||
def setup_class(self):
|
||||
self.context = samlp.RequestedAuthnContext()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for RequestedAuthnContext accessors"""
|
||||
|
||||
self.context.authn_context_class_ref.append(saml.AuthnContextClassRef())
|
||||
self.context.authn_context_decl_ref.append(saml.AuthnContextDeclRef())
|
||||
self.context.comparison = "exact"
|
||||
|
||||
new_context = samlp.requested_authn_context_from_string(
|
||||
self.context.to_string())
|
||||
|
||||
assert isinstance(new_context.authn_context_class_ref[0],
|
||||
saml.AuthnContextClassRef)
|
||||
assert isinstance(new_context.authn_context_decl_ref[0],
|
||||
saml.AuthnContextDeclRef)
|
||||
assert new_context.comparison == "exact"
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for requested_authn_context_from_string() using test data"""
|
||||
new_context = samlp.requested_authn_context_from_string(
|
||||
samlp_data.TEST_REQUESTED_AUTHN_CONTEXT)
|
||||
|
||||
assert isinstance(new_context.authn_context_class_ref[0],
|
||||
saml.AuthnContextClassRef)
|
||||
assert isinstance(new_context.authn_context_decl_ref[0],
|
||||
saml.AuthnContextDeclRef)
|
||||
assert new_context.comparison == "exact"
|
||||
|
||||
|
||||
class TestAuthnRequest:
|
||||
|
||||
def setup_class(self):
|
||||
self.ar = samlp.AuthnRequest()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for AuthnRequest accessors"""
|
||||
self.ar.id = "request id"
|
||||
self.ar.version = saml2.VERSION
|
||||
self.ar.issue_instant = "2007-09-14T01:05:02Z"
|
||||
self.ar.destination = "http://www.example.com/Destination"
|
||||
self.ar.consent = saml.CONSENT_UNSPECIFIED
|
||||
self.ar.issuer = saml.Issuer()
|
||||
self.ar.signature = ds.get_empty_signature()
|
||||
self.ar.extensions = samlp.Extensions()
|
||||
|
||||
self.ar.subject = saml.Subject()
|
||||
self.ar.name_id_policy = samlp.NameIDPolicy()
|
||||
self.ar.conditions = saml.Conditions()
|
||||
self.ar.requested_authn_context = samlp.RequestedAuthnContext()
|
||||
self.ar.scoping = samlp.Scoping()
|
||||
self.ar.force_authn = 'true'
|
||||
self.ar.is_passive = 'true'
|
||||
self.ar.assertion_consumer_service_index = "1"
|
||||
self.ar.assertion_consumer_service_url = "http://www.example.com/acs"
|
||||
self.ar.protocol_binding = saml2.BINDING_HTTP_POST
|
||||
self.ar.assertion_consuming_service_index = "2"
|
||||
self.ar.provider_name = "provider name"
|
||||
|
||||
new_ar = samlp.authn_request_from_string(self.ar.to_string())
|
||||
assert new_ar.id == "request id"
|
||||
assert new_ar.version == saml2.VERSION
|
||||
assert new_ar.issue_instant == "2007-09-14T01:05:02Z"
|
||||
assert new_ar.destination == "http://www.example.com/Destination"
|
||||
assert new_ar.consent == saml.CONSENT_UNSPECIFIED
|
||||
assert isinstance(new_ar.issuer, saml.Issuer)
|
||||
assert isinstance(new_ar.signature, ds.Signature)
|
||||
assert isinstance(new_ar.extensions, samlp.Extensions)
|
||||
|
||||
assert isinstance(new_ar.subject, saml.Subject)
|
||||
assert isinstance(new_ar.name_id_policy, samlp.NameIDPolicy)
|
||||
assert isinstance(new_ar.conditions, saml.Conditions)
|
||||
assert isinstance(new_ar.requested_authn_context,
|
||||
samlp.RequestedAuthnContext)
|
||||
assert isinstance(new_ar.scoping, samlp.Scoping)
|
||||
assert new_ar.force_authn == 'true'
|
||||
assert new_ar.is_passive == 'true'
|
||||
assert new_ar.assertion_consumer_service_index == '1'
|
||||
assert new_ar.assertion_consumer_service_url == \
|
||||
'http://www.example.com/acs'
|
||||
assert new_ar.protocol_binding == saml2.BINDING_HTTP_POST
|
||||
assert new_ar.assertion_consuming_service_index == '2'
|
||||
assert new_ar.provider_name == "provider name"
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for authn_request_from_string() using test data"""
|
||||
new_ar = samlp.authn_request_from_string(samlp_data.TEST_AUTHN_REQUEST)
|
||||
assert new_ar.id == "request id"
|
||||
assert new_ar.version == saml2.VERSION
|
||||
assert new_ar.issue_instant == "2007-09-14T01:05:02Z"
|
||||
assert new_ar.destination == "http://www.example.com/Destination"
|
||||
assert new_ar.consent == saml.CONSENT_UNSPECIFIED
|
||||
assert isinstance(new_ar.issuer, saml.Issuer)
|
||||
assert isinstance(new_ar.signature, ds.Signature)
|
||||
assert isinstance(new_ar.extensions, samlp.Extensions)
|
||||
|
||||
assert isinstance(new_ar.subject, saml.Subject)
|
||||
assert isinstance(new_ar.name_id_policy, samlp.NameIDPolicy)
|
||||
assert isinstance(new_ar.conditions, saml.Conditions)
|
||||
assert isinstance(new_ar.requested_authn_context,
|
||||
samlp.RequestedAuthnContext)
|
||||
assert isinstance(new_ar.scoping, samlp.Scoping)
|
||||
assert new_ar.force_authn == 'true'
|
||||
assert new_ar.is_passive == 'true'
|
||||
assert new_ar.assertion_consumer_service_index == '1'
|
||||
assert new_ar.assertion_consumer_service_url == \
|
||||
'http://www.example.com/acs'
|
||||
assert new_ar.protocol_binding == saml2.BINDING_HTTP_POST
|
||||
assert new_ar.assertion_consuming_service_index == '2'
|
||||
assert new_ar.provider_name == "provider name"
|
||||
|
||||
|
||||
class TestLogoutRequest:
|
||||
|
||||
def setup_class(self):
|
||||
self.lr = samlp.LogoutRequest()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for LogoutRequest accessors"""
|
||||
self.lr.id = "request id"
|
||||
self.lr.version = saml2.VERSION
|
||||
self.lr.issue_instant = "2007-09-14T01:05:02Z"
|
||||
self.lr.destination = "http://www.example.com/Destination"
|
||||
self.lr.consent = saml.CONSENT_UNSPECIFIED
|
||||
self.lr.issuer = saml.Issuer()
|
||||
self.lr.signature = ds.get_empty_signature()
|
||||
self.lr.extensions = samlp.Extensions()
|
||||
|
||||
self.lr.not_on_or_after = "2007-10-14T01:05:02Z"
|
||||
self.lr.reason = "http://www.example.com/Reason"
|
||||
self.lr.base_id = saml.BaseID()
|
||||
self.lr.name_id = saml.NameID()
|
||||
self.lr.encrypted_id = saml.EncryptedID()
|
||||
self.lr.session_index = samlp.SessionIndex()
|
||||
|
||||
new_lr = samlp.logout_request_from_string(self.lr.to_string())
|
||||
assert new_lr.id == "request id"
|
||||
assert new_lr.version == saml2.VERSION
|
||||
assert new_lr.issue_instant == "2007-09-14T01:05:02Z"
|
||||
assert new_lr.destination == "http://www.example.com/Destination"
|
||||
assert new_lr.consent == saml.CONSENT_UNSPECIFIED
|
||||
assert isinstance(new_lr.issuer, saml.Issuer)
|
||||
assert isinstance(new_lr.signature, ds.Signature)
|
||||
assert isinstance(new_lr.extensions, samlp.Extensions)
|
||||
assert new_lr.not_on_or_after == "2007-10-14T01:05:02Z"
|
||||
assert new_lr.reason == "http://www.example.com/Reason"
|
||||
assert isinstance(new_lr.base_id, saml.BaseID)
|
||||
assert isinstance(new_lr.name_id, saml.NameID)
|
||||
assert isinstance(new_lr.encrypted_id, saml.EncryptedID)
|
||||
assert isinstance(new_lr.session_index, samlp.SessionIndex)
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for logout_request_from_string() using test data"""
|
||||
new_lr = samlp.logout_request_from_string(samlp_data.TEST_LOGOUT_REQUEST)
|
||||
assert new_lr.id == "request id"
|
||||
assert new_lr.version == saml2.VERSION
|
||||
assert new_lr.issue_instant == "2007-09-14T01:05:02Z"
|
||||
assert new_lr.destination == "http://www.example.com/Destination"
|
||||
assert new_lr.consent == saml.CONSENT_UNSPECIFIED
|
||||
assert isinstance(new_lr.issuer, saml.Issuer)
|
||||
assert isinstance(new_lr.signature, ds.Signature)
|
||||
assert isinstance(new_lr.extensions, samlp.Extensions)
|
||||
assert new_lr.not_on_or_after == "2007-10-14T01:05:02Z"
|
||||
assert new_lr.reason == "http://www.example.com/Reason"
|
||||
assert isinstance(new_lr.base_id, saml.BaseID)
|
||||
assert isinstance(new_lr.name_id, saml.NameID)
|
||||
assert isinstance(new_lr.encrypted_id, saml.EncryptedID)
|
||||
assert isinstance(new_lr.session_index, samlp.SessionIndex)
|
||||
assert new_lr.session_index.text.strip() == "session index"
|
||||
|
||||
|
||||
class TestLogoutResponse:
|
||||
|
||||
def setup_class(self):
|
||||
self.lr = samlp.LogoutResponse()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for LogoutResponse accessors"""
|
||||
self.lr.id = "response id"
|
||||
self.lr.in_response_to = "request id"
|
||||
self.lr.version = saml2.VERSION
|
||||
self.lr.issue_instant = "2007-09-14T01:05:02Z"
|
||||
self.lr.destination = "http://www.example.com/Destination"
|
||||
self.lr.consent = saml.CONSENT_UNSPECIFIED
|
||||
self.lr.issuer = saml.Issuer()
|
||||
self.lr.signature = ds.get_empty_signature()
|
||||
self.lr.extensions = samlp.Extensions()
|
||||
self.lr.status = samlp.Status()
|
||||
|
||||
new_lr = samlp.logout_response_from_string(self.lr.to_string())
|
||||
assert new_lr.id == "response id"
|
||||
assert new_lr.in_response_to == "request id"
|
||||
assert new_lr.version == saml2.VERSION
|
||||
assert new_lr.issue_instant == "2007-09-14T01:05:02Z"
|
||||
assert new_lr.destination == "http://www.example.com/Destination"
|
||||
assert new_lr.consent == saml.CONSENT_UNSPECIFIED
|
||||
assert isinstance(new_lr.issuer, saml.Issuer)
|
||||
assert isinstance(new_lr.signature, ds.Signature)
|
||||
assert isinstance(new_lr.extensions, samlp.Extensions)
|
||||
assert isinstance(new_lr.status, samlp.Status)
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for logout_response_from_string() using test data"""
|
||||
new_lr = samlp.logout_response_from_string(
|
||||
samlp_data.TEST_LOGOUT_RESPONSE)
|
||||
assert new_lr.id == "response id"
|
||||
assert new_lr.in_response_to == "request id"
|
||||
assert new_lr.version == saml2.VERSION
|
||||
assert new_lr.issue_instant == "2007-09-14T01:05:02Z"
|
||||
assert new_lr.destination == "http://www.example.com/Destination"
|
||||
assert new_lr.consent == saml.CONSENT_UNSPECIFIED
|
||||
assert isinstance(new_lr.issuer, saml.Issuer)
|
||||
assert isinstance(new_lr.signature, ds.Signature)
|
||||
assert isinstance(new_lr.extensions, samlp.Extensions)
|
||||
assert isinstance(new_lr.status, samlp.Status)
|
||||
|
||||
@@ -1,443 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import zlib
|
||||
import base64
|
||||
import gzip
|
||||
|
||||
from saml2 import utils, saml, samlp, md, make_instance
|
||||
from saml2.utils import do_attribute_statement
|
||||
from saml2.sigver import make_temp
|
||||
from saml2.saml import Attribute, NAME_FORMAT_URI, AttributeValue
|
||||
from py.test import raises
|
||||
|
||||
SUCCESS_STATUS = """<?xml version=\'1.0\' encoding=\'UTF-8\'?>
|
||||
<ns0:Status xmlns:ns0="urn:oasis:names:tc:SAML:2.0:protocol"><ns0:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" /></ns0:Status>"""
|
||||
|
||||
ERROR_STATUS = """<?xml version='1.0' encoding='UTF-8'?>
|
||||
<ns0:Status xmlns:ns0="urn:oasis:names:tc:SAML:2.0:protocol"><ns0:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Responder"><ns0:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:UnknownPrincipal" /></ns0:StatusCode><ns0:StatusMessage>Error resolving principal</ns0:StatusMessage></ns0:Status>"""
|
||||
|
||||
|
||||
def _eq(l1,l2):
|
||||
return set(l1) == set(l2)
|
||||
|
||||
def _oeq(l1,l2):
|
||||
if len(l1) != len(l2):
|
||||
print "Different number of items"
|
||||
return False
|
||||
for item in l1:
|
||||
if item not in l2:
|
||||
print "%s not in l2" % (item,)
|
||||
for ite in l2:
|
||||
print "\t%s" % (ite,)
|
||||
return False
|
||||
return True
|
||||
|
||||
def test_inflate_then_deflate():
|
||||
str = """Selma Lagerlöf (1858-1940) was born in Östra Emterwik, Värmland,
|
||||
Sweden. She was brought up on Mårbacka, the family estate, which she did
|
||||
not leave until 1881, when she went to a teachers' college at Stockholm"""
|
||||
|
||||
interm = utils.deflate_and_base64_encode(str)
|
||||
bis = utils.decode_base64_and_inflate(interm)
|
||||
assert bis == str
|
||||
|
||||
def test_status_success():
|
||||
stat = utils.args2dict(
|
||||
status_code=utils.args2dict(value=samlp.STATUS_SUCCESS))
|
||||
status = make_instance( samlp.Status, stat)
|
||||
status_text = "%s" % status
|
||||
assert status_text == SUCCESS_STATUS
|
||||
assert status.status_code.value == samlp.STATUS_SUCCESS
|
||||
|
||||
def test_success_status():
|
||||
stat = utils.success_status_factory()
|
||||
status = make_instance(samlp.Status, stat)
|
||||
status_text = "%s" % status
|
||||
assert status_text == SUCCESS_STATUS
|
||||
assert status.status_code.value == samlp.STATUS_SUCCESS
|
||||
|
||||
def test_error_status():
|
||||
stat = utils.args2dict(
|
||||
status_message=utils.args2dict("Error resolving principal"),
|
||||
status_code=utils.args2dict(
|
||||
value=samlp.STATUS_RESPONDER,
|
||||
status_code=utils.args2dict(
|
||||
value=samlp.STATUS_UNKNOWN_PRINCIPAL)))
|
||||
|
||||
status_text = "%s" % make_instance( samlp.Status, stat )
|
||||
print status_text
|
||||
assert status_text == ERROR_STATUS
|
||||
|
||||
def test_status_from_exception():
|
||||
e = utils.UnknownPrincipal("Error resolving principal")
|
||||
stat = utils.status_from_exception_factory(e)
|
||||
print stat
|
||||
status_text = "%s" % make_instance( samlp.Status, stat )
|
||||
print status_text
|
||||
assert status_text == ERROR_STATUS
|
||||
|
||||
def test_attribute_sn():
|
||||
attr = utils.do_attributes({"surName":"Jeter"})
|
||||
|
||||
assert len(attr) == 1
|
||||
print attr
|
||||
inst = make_instance(saml.Attribute, attr[0])
|
||||
print inst
|
||||
assert inst.name == "surName"
|
||||
assert len(inst.attribute_value) == 1
|
||||
av = inst.attribute_value[0]
|
||||
assert av.text == "Jeter"
|
||||
assert av.type == "xs:string"
|
||||
|
||||
def test_attribute_age():
|
||||
attr = utils.do_attributes({"age":37})
|
||||
|
||||
assert len(attr) == 1
|
||||
inst = make_instance(saml.Attribute, attr[0])
|
||||
print inst
|
||||
assert inst.name == "age"
|
||||
assert len(inst.attribute_value) == 1
|
||||
av = inst.attribute_value[0]
|
||||
assert av.text == "37"
|
||||
assert av.type == "xs:integer"
|
||||
|
||||
def test_attribute_onoff():
|
||||
attr = utils.do_attributes({"onoff":False})
|
||||
|
||||
assert len(attr) == 1
|
||||
inst = make_instance(saml.Attribute, attr[0])
|
||||
print inst
|
||||
assert inst.name == "onoff"
|
||||
assert len(inst.attribute_value) == 1
|
||||
av = inst.attribute_value[0]
|
||||
assert av.text == "false"
|
||||
assert av.type == "xs:boolean"
|
||||
|
||||
def test_attribute_base64():
|
||||
attr = utils.do_attributes({"name":"Selma Lagerlöf"})
|
||||
|
||||
assert len(attr) == 1
|
||||
inst = make_instance(saml.Attribute, attr[0], True)
|
||||
print inst
|
||||
assert inst.name == "name"
|
||||
assert len(inst.attribute_value) == 1
|
||||
av = inst.attribute_value[0]
|
||||
assert av.type == "xs:base64Binary"
|
||||
assert av.text.strip() == "U2VsbWEgTGFnZXJsw7Zm"
|
||||
|
||||
def test_attribute_statement():
|
||||
astat = do_attribute_statement({"surName":"Jeter",
|
||||
"givenName":"Derek"})
|
||||
print astat
|
||||
statement = make_instance(saml.AttributeStatement,astat)
|
||||
print statement
|
||||
assert statement.keyswv() == ["attribute"]
|
||||
assert len(statement.attribute) == 2
|
||||
attr0 = statement.attribute[0]
|
||||
assert _eq(attr0.keyswv(), ["name","attribute_value"])
|
||||
assert len(attr0.attribute_value) == 1
|
||||
attr1 = statement.attribute[1]
|
||||
assert _eq(attr1.keyswv(), ["name","attribute_value"])
|
||||
assert len(attr1.attribute_value) == 1
|
||||
if attr0.name == "givenName":
|
||||
assert attr0.attribute_value[0].text == "Derek"
|
||||
assert attr1.name == "surName"
|
||||
assert attr1.attribute_value[0].text == "Jeter"
|
||||
else:
|
||||
assert attr0.name == "surName"
|
||||
assert attr0.attribute_value[0].text == "Jeter"
|
||||
assert attr1.name == "givenName"
|
||||
assert attr1.attribute_value[0].text == "Derek"
|
||||
|
||||
def test_audience():
|
||||
aud_restr = make_instance( saml.AudienceRestriction,
|
||||
utils.args2dict(
|
||||
audience=utils.args2dict("urn:foo:bar")))
|
||||
|
||||
assert aud_restr.keyswv() == ["audience"]
|
||||
assert aud_restr.audience.text == "urn:foo:bar"
|
||||
|
||||
def test_conditions():
|
||||
conds_dict = utils.args2dict(
|
||||
not_before="2009-10-30T07:58:10.852Z",
|
||||
not_on_or_after="2009-10-30T08:03:10.852Z",
|
||||
audience_restriction=utils.args2dict(
|
||||
audience=utils.args2dict("urn:foo:bar")))
|
||||
|
||||
conditions = make_instance(saml.Conditions, conds_dict)
|
||||
assert _eq(conditions.keyswv(), ["not_before", "not_on_or_after",
|
||||
"audience_restriction"])
|
||||
assert conditions.not_before == "2009-10-30T07:58:10.852Z"
|
||||
assert conditions.not_on_or_after == "2009-10-30T08:03:10.852Z"
|
||||
assert conditions.audience_restriction[0].audience.text == "urn:foo:bar"
|
||||
|
||||
def test_value_1():
|
||||
#FriendlyName="givenName" Name="urn:oid:2.5.4.42"
|
||||
# NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
|
||||
adict = utils.args2dict(name="urn:oid:2.5.4.42",
|
||||
name_format=NAME_FORMAT_URI)
|
||||
attribute = make_instance(saml.Attribute, adict)
|
||||
assert _eq(attribute.keyswv(),["name","name_format"])
|
||||
assert attribute.name == "urn:oid:2.5.4.42"
|
||||
assert attribute.name_format == saml.NAME_FORMAT_URI
|
||||
|
||||
def test_value_2():
|
||||
adict = utils.args2dict(name="urn:oid:2.5.4.42",
|
||||
name_format=NAME_FORMAT_URI,
|
||||
friendly_name="givenName")
|
||||
attribute = make_instance(saml.Attribute, adict)
|
||||
assert _eq(attribute.keyswv(),["name","name_format","friendly_name"])
|
||||
assert attribute.name == "urn:oid:2.5.4.42"
|
||||
assert attribute.name_format == NAME_FORMAT_URI
|
||||
assert attribute.friendly_name == "givenName"
|
||||
|
||||
def test_value_3():
|
||||
adict = utils.args2dict(attribute_value="Derek",
|
||||
name="urn:oid:2.5.4.42",
|
||||
name_format=NAME_FORMAT_URI,
|
||||
friendly_name="givenName")
|
||||
attribute = make_instance(saml.Attribute, adict)
|
||||
assert _eq(attribute.keyswv(),["name", "name_format",
|
||||
"friendly_name", "attribute_value"])
|
||||
assert attribute.name == "urn:oid:2.5.4.42"
|
||||
assert attribute.name_format == NAME_FORMAT_URI
|
||||
assert attribute.friendly_name == "givenName"
|
||||
assert len(attribute.attribute_value) == 1
|
||||
assert attribute.attribute_value[0].text == "Derek"
|
||||
|
||||
def test_value_4():
|
||||
adict = utils.args2dict(attribute_value="Derek",
|
||||
friendly_name="givenName")
|
||||
attribute = make_instance(saml.Attribute, adict)
|
||||
assert _eq(attribute.keyswv(),["friendly_name", "attribute_value"])
|
||||
assert attribute.friendly_name == "givenName"
|
||||
assert len(attribute.attribute_value) == 1
|
||||
assert attribute.attribute_value[0].text == "Derek"
|
||||
|
||||
def test_do_attribute_statement_0():
|
||||
astat = do_attribute_statement({"vo_attr":"foobar"})
|
||||
statement = make_instance(saml.AttributeStatement,astat)
|
||||
assert statement.keyswv() == ["attribute"]
|
||||
assert len(statement.attribute) == 1
|
||||
attr0 = statement.attribute[0]
|
||||
assert _eq(attr0.keyswv(), ["name","attribute_value"])
|
||||
assert attr0.name == "vo_attr"
|
||||
assert len(attr0.attribute_value) == 1
|
||||
assert attr0.attribute_value[0].text == "foobar"
|
||||
|
||||
def test_do_attribute_statement():
|
||||
astat = do_attribute_statement({"surName":"Jeter",
|
||||
"givenName":["Derek","Sanderson"]})
|
||||
statement = make_instance(saml.AttributeStatement, astat)
|
||||
assert statement.keyswv() == ["attribute"]
|
||||
assert len(statement.attribute) == 2
|
||||
attr0 = statement.attribute[0]
|
||||
assert _eq(attr0.keyswv(), ["name","attribute_value"])
|
||||
attr1 = statement.attribute[1]
|
||||
assert _eq(attr1.keyswv(), ["name","attribute_value"])
|
||||
if attr0.name == "givenName":
|
||||
assert len(attr0.attribute_value) == 2
|
||||
assert _eq([av.text for av in attr0.attribute_value],
|
||||
["Derek","Sanderson"])
|
||||
assert attr1.name == "surName"
|
||||
assert attr1.attribute_value[0].text == "Jeter"
|
||||
assert len(attr1.attribute_value) == 1
|
||||
else:
|
||||
assert attr0.name == "surName"
|
||||
assert attr0.attribute_value[0].text == "Jeter"
|
||||
assert len(attr0.attribute_value) == 1
|
||||
assert attr1.name == "givenName"
|
||||
assert len(attr1.attribute_value) == 2
|
||||
assert _eq([av.text for av in attr1.attribute_value],
|
||||
["Derek","Sanderson"])
|
||||
|
||||
def test_do_attribute_statement_multi():
|
||||
astat = do_attribute_statement(
|
||||
{( "urn:oid:1.3.6.1.4.1.5923.1.1.1.7",
|
||||
"urn:oasis:names:tc:SAML:2.0:attrname-format:uri",
|
||||
"eduPersonEntitlement"):"Jeter"})
|
||||
statement = make_instance(saml.AttributeStatement,astat)
|
||||
assert statement.keyswv() == ["attribute"]
|
||||
assert len(statement.attribute)
|
||||
assert _eq(statement.attribute[0].keyswv(),
|
||||
["name","name_format","friendly_name","attribute_value"])
|
||||
attribute = statement.attribute[0]
|
||||
assert attribute.name == "urn:oid:1.3.6.1.4.1.5923.1.1.1.7"
|
||||
assert attribute.name_format == (
|
||||
"urn:oasis:names:tc:SAML:2.0:attrname-format:uri")
|
||||
assert attribute.friendly_name == "eduPersonEntitlement"
|
||||
|
||||
def test_subject():
|
||||
adict = utils.args2dict("_aaa", name_id=saml.NAMEID_FORMAT_TRANSIENT)
|
||||
subject = make_instance(saml.Subject, adict)
|
||||
assert _eq(subject.keyswv(),["text", "name_id"])
|
||||
assert subject.text == "_aaa"
|
||||
assert subject.name_id.text == saml.NAMEID_FORMAT_TRANSIENT
|
||||
|
||||
|
||||
|
||||
|
||||
def test_parse_attribute_map():
|
||||
(forward, backward) = utils.parse_attribute_map(["attribute.map"])
|
||||
|
||||
assert _eq(forward.keys(), backward.values())
|
||||
assert _eq(forward.values(), backward.keys())
|
||||
print forward.keys()
|
||||
assert _oeq(forward.keys(), [
|
||||
('urn:oid:1.3.6.1.4.1.5923.1.1.1.7', NAME_FORMAT_URI),
|
||||
('urn:oid:0.9.2342.19200300.100.1.1', NAME_FORMAT_URI),
|
||||
('urn:oid:1.3.6.1.4.1.5923.1.1.1.1', NAME_FORMAT_URI),
|
||||
('urn:oid:2.5.4.42', NAME_FORMAT_URI),
|
||||
('urn:oid:2.5.4.4', NAME_FORMAT_URI),
|
||||
('urn:oid:0.9.2342.19200300.100.1.3', NAME_FORMAT_URI),
|
||||
('urn:oid:2.5.4.12', NAME_FORMAT_URI)])
|
||||
assert _eq(forward.keys(), [
|
||||
('urn:oid:1.3.6.1.4.1.5923.1.1.1.7', NAME_FORMAT_URI),
|
||||
('urn:oid:0.9.2342.19200300.100.1.1', NAME_FORMAT_URI),
|
||||
('urn:oid:1.3.6.1.4.1.5923.1.1.1.1', NAME_FORMAT_URI),
|
||||
('urn:oid:2.5.4.42', NAME_FORMAT_URI),
|
||||
('urn:oid:2.5.4.4', NAME_FORMAT_URI),
|
||||
('urn:oid:0.9.2342.19200300.100.1.3', NAME_FORMAT_URI),
|
||||
('urn:oid:2.5.4.12', NAME_FORMAT_URI)])
|
||||
assert _eq(backward.keys(),["surName","givenName","title","uid","mail",
|
||||
"eduPersonAffiliation",
|
||||
"eduPersonEntitlement"])
|
||||
|
||||
|
||||
def test_identity_attribute_0():
|
||||
(forward, backward) = utils.parse_attribute_map(["attribute.map"])
|
||||
a = Attribute(name="urn:oid:2.5.4.4", name_format=NAME_FORMAT_URI,
|
||||
friendly_name="surName")
|
||||
|
||||
assert utils.identity_attribute("name",a,forward) == "urn:oid:2.5.4.4"
|
||||
assert utils.identity_attribute("friendly",a,forward) == "surName"
|
||||
|
||||
def test_identity_attribute_1():
|
||||
(forward, backward) = utils.parse_attribute_map(["attribute.map"])
|
||||
a = Attribute(name="urn:oid:2.5.4.4", name_format=NAME_FORMAT_URI)
|
||||
|
||||
assert utils.identity_attribute("name",a,forward) == "urn:oid:2.5.4.4"
|
||||
assert utils.identity_attribute("friendly",a,forward) == "surName"
|
||||
|
||||
def test_identity_attribute_2():
|
||||
(forward, backward) = utils.parse_attribute_map(["attribute.map"])
|
||||
a = Attribute(name="urn:oid:2.5.4.5", name_format=NAME_FORMAT_URI)
|
||||
|
||||
assert utils.identity_attribute("name",a,forward) == "urn:oid:2.5.4.5"
|
||||
# if there would be a map it would be serialNumber
|
||||
assert utils.identity_attribute("friendly",a,forward) == "urn:oid:2.5.4.5"
|
||||
|
||||
def test_identity_attribute_3():
|
||||
a = Attribute(name="urn:oid:2.5.4.5", name_format=NAME_FORMAT_URI)
|
||||
|
||||
assert utils.identity_attribute("name",a) == "urn:oid:2.5.4.5"
|
||||
# if there would be a map it would be serialNumber
|
||||
assert utils.identity_attribute("friendly",a) == "urn:oid:2.5.4.5"
|
||||
|
||||
def test_identity_attribute_4():
|
||||
a = Attribute(name="urn:oid:2.5.4.5", name_format=NAME_FORMAT_URI,
|
||||
friendly_name="serialNumber")
|
||||
|
||||
assert utils.identity_attribute("name",a) == "urn:oid:2.5.4.5"
|
||||
# if there would be a map it would be serialNumber
|
||||
assert utils.identity_attribute("friendly",a) == "serialNumber"
|
||||
|
||||
def _givenName(a):
|
||||
assert a["name"] == "urn:oid:2.5.4.42"
|
||||
assert a["friendly_name"] == "givenName"
|
||||
assert len(a["attribute_value"]) == 1
|
||||
assert a["attribute_value"] == [{"text":"Derek"}]
|
||||
|
||||
def _surName(a):
|
||||
assert a["name"] == "urn:oid:2.5.4.4"
|
||||
assert a["friendly_name"] == "surName"
|
||||
assert len(a["attribute_value"]) == 1
|
||||
assert a["attribute_value"] == [{"text":"Jeter"}]
|
||||
|
||||
def test_nameformat_email():
|
||||
assert utils.valid_email("foo@example.com")
|
||||
assert utils.valid_email("a@b.com")
|
||||
assert utils.valid_email("a@b.se")
|
||||
assert utils.valid_email("john@doe@johndoe.com") == False
|
||||
|
||||
def test_args2dict():
|
||||
n = utils.args2dict("foo", name_qualifier="urn:mace:example.com:nq")
|
||||
assert _eq(n.keys(), ["text","name_qualifier"])
|
||||
assert n["text"] == "foo"
|
||||
assert n["name_qualifier"] == "urn:mace:example.com:nq"
|
||||
|
||||
def test_attribute():
|
||||
a = utils.args2dict(friendly_name="eduPersonScopedAffiliation",
|
||||
name="urn:oid:1.3.6.1.4.1.5923.1.1.1.9",
|
||||
name_format="urn:oasis:names:tc:SAML:2.0:attrname-format:uri")
|
||||
|
||||
assert _eq(a.keys(), ["friendly_name","name", "name_format"])
|
||||
|
||||
a = utils.args2dict(friendly_name="eduPersonScopedAffiliation",
|
||||
name="urn:oid:1.3.6.1.4.1.5923.1.1.1.9",
|
||||
name_format="urn:oasis:names:tc:SAML:2.0:attrname-format:uri",
|
||||
attribute_value=utils.args2dict("member@example.com"))
|
||||
|
||||
assert _eq(a.keys(), ["friendly_name","name", "name_format",
|
||||
"attribute_value"])
|
||||
|
||||
def test_attribute_statement():
|
||||
a = utils.args2dict(
|
||||
attribute=[
|
||||
utils.args2dict(attribute_value="Derek",
|
||||
friendly_name="givenName"),
|
||||
utils.args2dict(attribute_value="Jeter",
|
||||
friendly_name="surName"),
|
||||
])
|
||||
assert a.keys() == ["attribute"]
|
||||
assert len(a["attribute"]) == 2
|
||||
|
||||
def test_subject_confirmation_data():
|
||||
s = utils.args2dict(
|
||||
in_response_to="_12345678",
|
||||
not_before="2010-02-11T07:30:00Z",
|
||||
not_on_or_after="2010-02-11T07:35:00Z",
|
||||
recipient="http://example.com/sp/",
|
||||
address="192.168.0.10")
|
||||
|
||||
assert _eq(s.keys(),["in_response_to","not_before","not_on_or_after",
|
||||
"recipient", "address"])
|
||||
|
||||
def test_subject_confirmation():
|
||||
s = utils.args2dict(
|
||||
method="urn:oasis:names:tc:SAML:2.0:profiles:SSO:browser",
|
||||
base_id="1234",
|
||||
name_id="abcd",
|
||||
subject_confirmation_data=utils.args2dict(
|
||||
in_response_to="_1234567890",
|
||||
recipient="http://example.com/sp/"))
|
||||
|
||||
assert _eq(s.keys(),
|
||||
["method","base_id","name_id","subject_confirmation_data"])
|
||||
assert s["method"] == "urn:oasis:names:tc:SAML:2.0:profiles:SSO:browser"
|
||||
|
||||
|
||||
def test_authn_context_class_ref():
|
||||
a = utils.args2dict(
|
||||
"urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified")
|
||||
assert a.keys() == ["text"]
|
||||
assert a["text"] == "urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified"
|
||||
|
||||
def test_authn_context():
|
||||
accr = utils.args2dict(
|
||||
"urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified")
|
||||
a = utils.args2dict(authn_context_class_ref=accr)
|
||||
|
||||
assert a.keys() == ["authn_context_class_ref"]
|
||||
|
||||
def test_authn_statement():
|
||||
accr = utils.args2dict(
|
||||
"urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified")
|
||||
ac = utils.args2dict(authn_context_class_ref=accr)
|
||||
a = utils.args2dict(
|
||||
authn_instant="2010-03-10T12:33:00Z",
|
||||
session_index="_12345",
|
||||
session_not_on_or_after="2010-03-11T12:00:00Z",
|
||||
authn_context=ac
|
||||
)
|
||||
@@ -2,7 +2,7 @@ from saml2 import md, assertion
|
||||
from saml2.saml import Attribute, NAME_FORMAT_URI, AttributeValue
|
||||
from saml2.assertion import Policy, Assertion, filter_on_attributes
|
||||
from saml2.assertion import filter_attribute_value_assertions
|
||||
from saml2.utils import MissingValue
|
||||
from saml2.s_utils import MissingValue
|
||||
from saml2 import attribute_converter
|
||||
|
||||
from py.test import raises
|
||||
@@ -152,21 +152,24 @@ def test_ava_filter_2():
|
||||
}
|
||||
}}
|
||||
|
||||
r = Policy(conf)
|
||||
policy = Policy(conf)
|
||||
|
||||
ava = {"givenName":"Derek",
|
||||
"surName": "Jeter",
|
||||
"mail":"derek@example.com"}
|
||||
|
||||
# I'm filtering away something the SP deems necessary
|
||||
raises(MissingValue, r.filter, ava, 'urn:mace:umu.se:saml:roland:sp',
|
||||
|
||||
#policy.filter(ava, 'urn:mace:umu.se:saml:roland:sp', [mail], [gn, sn])
|
||||
|
||||
raises(MissingValue, policy.filter, ava, 'urn:mace:umu.se:saml:roland:sp',
|
||||
[mail], [gn, sn])
|
||||
|
||||
ava = {"givenName":"Derek",
|
||||
"surName": "Jeter"}
|
||||
|
||||
# it wasn't there to begin with
|
||||
raises(MissingValue, r.filter, ava, 'urn:mace:umu.se:saml:roland:sp',
|
||||
raises(MissingValue, policy.filter, ava, 'urn:mace:umu.se:saml:roland:sp',
|
||||
[gn,sn,mail])
|
||||
|
||||
def test_filter_attribute_value_assertions_0(AVA):
|
||||
|
||||
@@ -57,21 +57,21 @@ class TestAC():
|
||||
statement = attribute_converter.from_local(self.acs, ava, BASIC_NF)
|
||||
|
||||
assert statement != None
|
||||
assert len(statement["attribute"]) == 2
|
||||
a0 = statement["attribute"][0]
|
||||
a1 = statement["attribute"][1]
|
||||
if a0["friendly_name"] == 'sn':
|
||||
assert a0["name"] == 'urn:mace:dir:attribute-def:sn'
|
||||
assert a0["name_format"] == BASIC_NF
|
||||
assert a1["friendly_name"] == "givenName"
|
||||
assert a1["name"] == 'urn:mace:dir:attribute-def:givenName'
|
||||
assert a1["name_format"] == BASIC_NF
|
||||
elif a0["friendly_name"] == 'givenName':
|
||||
assert a0["name"] == 'urn:mace:dir:attribute-def:givenName'
|
||||
assert a0["name_format"] == BASIC_NF
|
||||
assert a1["friendly_name"] == "sn"
|
||||
assert a1["name"] == 'urn:mace:dir:attribute-def:sn'
|
||||
assert a1["name_format"] == BASIC_NF
|
||||
assert len(statement) == 2
|
||||
a0 = statement[0]
|
||||
a1 = statement[1]
|
||||
if a0.friendly_name == 'sn':
|
||||
assert a0.name == 'urn:mace:dir:attribute-def:sn'
|
||||
assert a0.name_format == BASIC_NF
|
||||
assert a1.friendly_name == "givenName"
|
||||
assert a1.name == 'urn:mace:dir:attribute-def:givenName'
|
||||
assert a1.name_format == BASIC_NF
|
||||
elif a0.friendly_name == 'givenName':
|
||||
assert a0.name == 'urn:mace:dir:attribute-def:givenName'
|
||||
assert a0.name_format == BASIC_NF
|
||||
assert a1.friendly_name == "sn"
|
||||
assert a1.name == 'urn:mace:dir:attribute-def:sn'
|
||||
assert a1.name_format == BASIC_NF
|
||||
else:
|
||||
assert False
|
||||
|
||||
@@ -80,21 +80,21 @@ class TestAC():
|
||||
|
||||
statement = attribute_converter.from_local(self.acs, ava, URI_NF)
|
||||
|
||||
assert len(statement["attribute"]) == 2
|
||||
a0 = statement["attribute"][0]
|
||||
a1 = statement["attribute"][1]
|
||||
if a0["friendly_name"] == 'surname':
|
||||
assert a0["name"] == 'urn:oid:2.5.4.4'
|
||||
assert a0["name_format"] == URI_NF
|
||||
assert a1["friendly_name"] == "givenName"
|
||||
assert a1["name"] == 'urn:oid:2.5.4.42'
|
||||
assert a1["name_format"] == URI_NF
|
||||
elif a0["friendly_name"] == 'givenName':
|
||||
assert a0["name"] == 'urn:oid:2.5.4.42'
|
||||
assert a0["name_format"] == URI_NF
|
||||
assert a1["friendly_name"] == "surname"
|
||||
assert a1["name"] == 'urn:oid:2.5.4.4'
|
||||
assert a1["name_format"] == URI_NF
|
||||
assert len(statement) == 2
|
||||
a0 = statement[0]
|
||||
a1 = statement[1]
|
||||
if a0.friendly_name == 'surname':
|
||||
assert a0.name == 'urn:oid:2.5.4.4'
|
||||
assert a0.name_format == URI_NF
|
||||
assert a1.friendly_name == "givenName"
|
||||
assert a1.name == 'urn:oid:2.5.4.42'
|
||||
assert a1.name_format == URI_NF
|
||||
elif a0.friendly_name == 'givenName':
|
||||
assert a0.name == 'urn:oid:2.5.4.42'
|
||||
assert a0.name_format == URI_NF
|
||||
assert a1.friendly_name == "surname"
|
||||
assert a1.name == 'urn:oid:2.5.4.4'
|
||||
assert a1.name_format == URI_NF
|
||||
else:
|
||||
assert False
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ from saml2.attribute_converter import ac_factory
|
||||
|
||||
from py.test import raises
|
||||
|
||||
SWAMI_METADATA = "swamid-kalmar-1.0.xml"
|
||||
SWAMI_METADATA = "swamid-1.0.xml"
|
||||
INCOMMON_METADATA = "InCommon-metadata.xml"
|
||||
EXAMPLE_METADATA = "metadata_example.xml"
|
||||
SWITCH_METADATA = "metadata.aaitest.xml"
|
||||
@@ -53,16 +53,19 @@ def test_swami_1():
|
||||
ssocerts = md.certs('https://idp.umu.se/saml2/idp/SSOService.php')
|
||||
print ssocerts
|
||||
assert len(ssocerts) == 1
|
||||
print md.wants
|
||||
print md._wants.keys()
|
||||
assert _eq(md._wants.keys(),['https://connect.sunet.se/shibboleth',
|
||||
'https://sp.swamid.se/shibboleth'])
|
||||
assert _eq(md.wants('https://sp.swamid.se/shibboleth')[1].keys(),
|
||||
["eduPersonPrincipalName"])
|
||||
assert md.wants('https://sp.swamid.se/shibboleth')[0] == {}
|
||||
'https://www.diva-portal.org/shibboleth'])
|
||||
|
||||
print md.wants('https://www.diva-portal.org/shibboleth')
|
||||
assert _eq(md.wants('https://www.diva-portal.org/shibboleth')[1].keys(),
|
||||
['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'])
|
||||
assert md.wants('https://connect.sunet.se/shibboleth')[0] == {}
|
||||
|
||||
def test_incommon_1():
|
||||
md = metadata.MetaData(attrconv=ATTRCONV)
|
||||
@@ -130,8 +133,9 @@ def test_sp_metadata():
|
||||
print md.entity
|
||||
assert len(md.entity) == 1
|
||||
assert md.entity.keys() == ['urn:mace:umu.se:saml:roland:sp']
|
||||
assert md.entity['urn:mace:umu.se:saml:roland:sp'].keys() == [
|
||||
'valid_until',"organization","sp_sso"]
|
||||
assert _eq(md.entity['urn:mace:umu.se:saml:roland:sp'].keys(), [
|
||||
'valid_until',"organization","sp_sso",
|
||||
'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 req
|
||||
@@ -174,7 +178,7 @@ def test_construct_organisation_name():
|
||||
md.OrganizationName, o, "organization_name")
|
||||
print o
|
||||
assert str(o) == """<?xml version='1.0' encoding='UTF-8'?>
|
||||
<ns0:Organization xmlns:ns0="urn:oasis:names:tc:SAML:2.0:metadata"><ns0:OrganizationName ns1:lang="se" xmlns:ns1="http:#www.w3.org/XML/1998/namespace">Exempel AB</ns0:OrganizationName></ns0:Organization>"""
|
||||
<ns0:Organization xmlns:ns0="urn:oasis:names:tc:SAML:2.0:metadata"><ns0:OrganizationName xml:lang="se">Exempel AB</ns0:OrganizationName></ns0:Organization>"""
|
||||
|
||||
def test_make_int_value():
|
||||
val = make_vals( 1, saml.AttributeValue, part=True)
|
||||
@@ -260,10 +264,11 @@ def test_construct_entity_descr_1():
|
||||
|
||||
assert ed.entity_id == "urn:mace:catalogix.se:sp1"
|
||||
org = ed.organization
|
||||
assert _eq(org.keyswv(), ["organization_name","organization_url"])
|
||||
assert len(org.organization_name) == 1
|
||||
assert org.organization_name[0].text == "Catalogix"
|
||||
assert org.organization_url[0].text == "http://www.catalogix.se/"
|
||||
assert len(org) == 1
|
||||
assert _eq(org[0].keyswv(), ["organization_name","organization_url"])
|
||||
assert len(org[0].organization_name) == 1
|
||||
assert org[0].organization_name[0].text == "Catalogix"
|
||||
assert org[0].organization_url[0].text == "http://www.catalogix.se/"
|
||||
|
||||
def test_construct_entity_descr_2():
|
||||
ed = make_instance(md.EntityDescriptor,
|
||||
@@ -281,10 +286,11 @@ def test_construct_entity_descr_2():
|
||||
assert _eq(ed.keyswv(), ["entity_id", "contact_person", "organization"])
|
||||
assert ed.entity_id == "urn:mace:catalogix.se:sp1"
|
||||
org = ed.organization
|
||||
assert _eq(org.keyswv(), ["organization_name", "organization_url"])
|
||||
assert len(org.organization_name) == 1
|
||||
assert org.organization_name[0].text == "Catalogix"
|
||||
assert org.organization_url[0].text == "http://www.catalogix.se/"
|
||||
assert len(org) == 1
|
||||
assert _eq(org[0].keyswv(), ["organization_name", "organization_url"])
|
||||
assert len(org[0].organization_name) == 1
|
||||
assert org[0].organization_name[0].text == "Catalogix"
|
||||
assert org[0].organization_url[0].text == "http://www.catalogix.se/"
|
||||
assert len(ed.contact_person) == 1
|
||||
c = ed.contact_person[0]
|
||||
assert c.given_name.text == "Roland"
|
||||
|
||||
@@ -9,7 +9,9 @@ sp1 = {
|
||||
"entityid" : "urn:mace:umu.se:saml:roland:sp",
|
||||
"service": {
|
||||
"sp": {
|
||||
"url" : "http://lingon.catalogix.se:8087/",
|
||||
"endpoints" : {
|
||||
"assertion_consumer_service" : ["http://lingon.catalogix.se:8087/"],
|
||||
},
|
||||
"name": "test",
|
||||
"idp" : {
|
||||
"urn:mace:example.com:saml:roland:idp":None,
|
||||
@@ -36,7 +38,9 @@ sp2 = {
|
||||
"service": {
|
||||
"sp":{
|
||||
"name" : "Rolands SP",
|
||||
"url" : "http://localhost:8087/",
|
||||
"endpoints" : {
|
||||
"assertion_consumer_service" : ["http://lingon.catalogix.se:8087/"],
|
||||
},
|
||||
"required_attributes": ["surName", "givenName", "mail"],
|
||||
"optional_attributes": ["title"],
|
||||
"idp": {
|
||||
@@ -52,7 +56,9 @@ IDP1 = {
|
||||
"service": {
|
||||
"idp":{
|
||||
"name" : "Rolands IdP",
|
||||
"url" : "http://localhost:8088/",
|
||||
"endpoints": {
|
||||
"single_sign_on_service" : ["http://localhost:8088/"],
|
||||
},
|
||||
"assertions":{
|
||||
"default": {
|
||||
"attribute_restrictions": {
|
||||
@@ -79,7 +85,7 @@ def test_1():
|
||||
service = c["service"]
|
||||
assert service.keys() == ["sp"]
|
||||
sp = service["sp"]
|
||||
assert _eq(sp.keys(),["url","name","idp"])
|
||||
assert _eq(sp.keys(),["endpoints","name","idp"])
|
||||
md = c["metadata"]
|
||||
assert isinstance(md, MetaData)
|
||||
|
||||
@@ -94,7 +100,7 @@ def test_2():
|
||||
service = c["service"]
|
||||
assert service.keys() == ["sp"]
|
||||
sp = service["sp"]
|
||||
assert _eq(sp.keys(),['url', 'idp', 'optional_attributes', 'name',
|
||||
assert _eq(sp.keys(),['endpoints', 'idp', 'optional_attributes', 'name',
|
||||
'required_attributes'])
|
||||
|
||||
assert len(sp["idp"]) == 1
|
||||
@@ -111,7 +117,9 @@ def test_missing_must():
|
||||
no_entity_id = {
|
||||
"service": {
|
||||
"sp": {
|
||||
"url" : "http://lingon.catalogix.se:8087/",
|
||||
"endpoints" : {
|
||||
"assertion_consumer_service" : ["http://lingon.catalogix.se:8087/"],
|
||||
},
|
||||
"name" : "test"
|
||||
}
|
||||
},
|
||||
@@ -122,7 +130,9 @@ def test_missing_must():
|
||||
"entityid" : "urn:mace:umu.se:saml:roland:sp",
|
||||
"service": {
|
||||
"sp": {
|
||||
"url" : "http://lingon.catalogix.se:8087/",
|
||||
"endpoints" : {
|
||||
"assertion_consumer_service" : ["http://lingon.catalogix.se:8087/"],
|
||||
},
|
||||
"name" : "test"
|
||||
}
|
||||
},
|
||||
@@ -138,7 +148,9 @@ def test_minimum():
|
||||
"entityid" : "urn:mace:example.com:saml:roland:sp",
|
||||
"service": {
|
||||
"sp": {
|
||||
"url" : "http://sp.example.org/",
|
||||
"endpoints" : {
|
||||
"assertion_consumer_service" : ["http://sp.example.org/"],
|
||||
},
|
||||
"name" : "test",
|
||||
"idp": {
|
||||
"" : "https://example.com/idp/SSOService.php",
|
||||
@@ -157,8 +169,15 @@ def test_idp():
|
||||
|
||||
print c
|
||||
assert c.services() == ["idp"]
|
||||
assert c.idp_url() == "http://localhost:8088/"
|
||||
assert c.endpoint("idp", "single_sign_on_service") == ['http://localhost:8088/']
|
||||
|
||||
attribute_restrictions = c.idp_policy().get_attribute_restriction("")
|
||||
assert attribute_restrictions["eduPersonAffiliation"][0].match("staff")
|
||||
|
||||
def test_wayf():
|
||||
c = Config().load_file("server.config")
|
||||
|
||||
idps = c.get_available_idps()
|
||||
assert idps == [('urn:mace:example.com:saml:roland:idp', 'Example Co')]
|
||||
|
||||
|
||||
@@ -14,7 +14,9 @@ CONFIG = Config().load({
|
||||
"entityid" : "urn:mace:example.com:idp:2",
|
||||
"service": {
|
||||
"idp": {
|
||||
"url" : "http://idp.example.org/",
|
||||
"endpoints" : {
|
||||
"single_sign_on_service" : ["http://idp.example.org/"],
|
||||
},
|
||||
"name" : "test",
|
||||
"assertions": {
|
||||
"default": {
|
||||
@@ -69,15 +71,17 @@ class TestIdentifier():
|
||||
nameid = self.id.construct_nameid(policy, "foobar",
|
||||
"urn:mace:example.com:sp:1")
|
||||
|
||||
assert _eq(nameid.keys(), ['text', 'sp_name_qualifier', 'format'])
|
||||
assert nameid["sp_name_qualifier"] == CONFIG["entityid"]
|
||||
assert nameid["format"] == NAMEID_FORMAT_PERSISTENT
|
||||
assert _eq(nameid.keys(), ['text', 'sp_provided_id',
|
||||
'sp_name_qualifier', 'name_qualifier', 'format'])
|
||||
assert _eq(nameid.keyswv(), ['format', 'text', 'sp_name_qualifier'])
|
||||
assert nameid.sp_name_qualifier == CONFIG["entityid"]
|
||||
assert nameid.format == NAMEID_FORMAT_PERSISTENT
|
||||
|
||||
nameid_2 = self.id.construct_nameid(policy, "foobar",
|
||||
"urn:mace:example.com:sp:1")
|
||||
|
||||
assert nameid == nameid_2
|
||||
assert nameid["text"] == nameid_2["text"]
|
||||
assert nameid != nameid_2
|
||||
assert nameid.text == nameid_2.text
|
||||
|
||||
def test_transient_1(self):
|
||||
policy = Policy({
|
||||
@@ -92,8 +96,8 @@ class TestIdentifier():
|
||||
nameid = self.id.construct_nameid(policy, "foobar",
|
||||
"urn:mace:example.com:sp:1")
|
||||
|
||||
assert _eq(nameid.keys(), ['text', 'format'])
|
||||
assert nameid["format"] == NAMEID_FORMAT_TRANSIENT
|
||||
assert _eq(nameid.keyswv(), ['text', 'format'])
|
||||
assert nameid.format == NAMEID_FORMAT_TRANSIENT
|
||||
|
||||
def test_vo_1(self):
|
||||
policy = Policy({
|
||||
@@ -112,11 +116,11 @@ class TestIdentifier():
|
||||
{"uid": "foobar01"},
|
||||
name_id_policy)
|
||||
|
||||
assert _eq(nameid.keys(), ['text', 'sp_name_qualifier', 'format'])
|
||||
assert nameid["sp_name_qualifier"] == 'http://vo.example.org/biomed'
|
||||
assert nameid["format"] == \
|
||||
assert _eq(nameid.keyswv(), ['text', 'sp_name_qualifier', 'format'])
|
||||
assert nameid.sp_name_qualifier == 'http://vo.example.org/biomed'
|
||||
assert nameid.format == \
|
||||
CONFIG.vo_conf('http://vo.example.org/biomed')["nameid_format"]
|
||||
assert nameid["text"] == "foobar01"
|
||||
assert nameid.text == "foobar01"
|
||||
|
||||
def test_vo_2(self):
|
||||
policy = Policy({
|
||||
@@ -136,8 +140,8 @@ class TestIdentifier():
|
||||
{"uid": "foobar01"},
|
||||
name_id_policy)
|
||||
|
||||
assert _eq(nameid.keys(), ['text', 'sp_name_qualifier', 'format'])
|
||||
assert nameid["sp_name_qualifier"] == 'http://vo.example.org/design'
|
||||
assert nameid["format"] == NAMEID_FORMAT_PERSISTENT
|
||||
assert nameid["text"] != "foobar01"
|
||||
assert _eq(nameid.keyswv(), ['text', 'sp_name_qualifier', 'format'])
|
||||
assert nameid.sp_name_qualifier == 'http://vo.example.org/design'
|
||||
assert nameid.format == NAMEID_FORMAT_PERSISTENT
|
||||
assert nameid.text != "foobar01"
|
||||
|
||||
|
||||
@@ -4,9 +4,10 @@ import os
|
||||
import base64
|
||||
|
||||
from saml2 import sigver, make_instance
|
||||
from saml2 import utils, class_name
|
||||
from saml2 import class_name
|
||||
from saml2 import time_util
|
||||
from saml2 import saml, samlp
|
||||
from saml2.s_utils import factory, do_attribute_statement
|
||||
import xmldsig as ds
|
||||
from py.test import raises
|
||||
|
||||
@@ -21,11 +22,11 @@ PRIV_KEY = "test.key"
|
||||
def _eq(l1,l2):
|
||||
return set(l1) == set(l2)
|
||||
|
||||
SIGNED_VALUE= """AS1kHHtA4eTOU2XLTWhLMSJQ6V+TSDymRoTF78CqjrYURNLk9wjdPjAReNn9eykv
|
||||
ryFiHNk0p9wMBknha5pH8aeCI/LmcVhLa5xteGZrtE/Udh5vv8z4kRQX51Uz/5x8
|
||||
ToiobGw83MEW6A0dRUn0O20NBMMTaFZZPXye7RvVlHY="""
|
||||
SIGNED_VALUE= """kMuyOK17nyp4CbA1v7KE32rX4+NQQ8EvdglTK61uIMEo3ax0PgFU7bgZGey+Aj8H
|
||||
hTPVyAzWmBDxHpSCFe050PTtNoKHx7nXprLfhuQXsPq8s0KBoZR+2qYfVCkWYVX7
|
||||
T3zG/Tn+fesBA1zLo4lYdAovol7C35KAsAWoknmZdOE="""
|
||||
|
||||
DIGEST_VALUE = "WFRXmImfoO3M6JOLE6BGGpU9Ud0="
|
||||
DIGEST_VALUE = "SXw3kqTf+PtTiUnI8nQ6xmrM3qw="
|
||||
|
||||
def get_xmlsec():
|
||||
for path in os.environ["PATH"].split(":"):
|
||||
@@ -94,24 +95,16 @@ class TestSecurity():
|
||||
self.sec = sigver.SecurityContext(get_xmlsec(), PRIV_KEY, "pem",
|
||||
PUB_KEY, "pem", debug=1)
|
||||
|
||||
self._assertion = {
|
||||
"version": "2.0",
|
||||
"id": "11111",
|
||||
"issue_instant": "2009-10-30T13:20:28Z",
|
||||
"signature": sigver.pre_signature_part("11111", self.sec.my_cert,
|
||||
1),
|
||||
"attribute_statement": {
|
||||
"attribute": [{
|
||||
"friendly_name": "surName",
|
||||
"attribute_value": "Foo",
|
||||
},
|
||||
{
|
||||
"friendly_name": "givenName",
|
||||
"attribute_value": "Bar",
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
self._assertion = factory( saml.Assertion,
|
||||
version="2.0",
|
||||
id="11111",
|
||||
issue_instant="2009-10-30T13:20:28Z",
|
||||
signature=sigver.pre_signature_part("11111", self.sec.my_cert, 1),
|
||||
attribute_statement=do_attribute_statement({
|
||||
("","","surName"): ("Foo",""),
|
||||
("","","givenName") :("Bar",""),
|
||||
})
|
||||
)
|
||||
|
||||
def test_verify_1(self):
|
||||
xml_response = open(SIGNED).read()
|
||||
@@ -130,10 +123,10 @@ class TestSecurity():
|
||||
xml_response)
|
||||
|
||||
def test_sign_assertion(self):
|
||||
ass = make_instance(saml.Assertion, self._assertion)
|
||||
|
||||
ass = self._assertion
|
||||
print ass
|
||||
sign_ass = self.sec.sign_assertion_using_xmlsec("%s" % ass, nodeid=ass.id)
|
||||
|
||||
print sign_ass
|
||||
sass = saml.assertion_from_string(sign_ass)
|
||||
print sass
|
||||
assert _eq(sass.keyswv(), ['attribute_statement', 'issue_instant',
|
||||
@@ -144,15 +137,18 @@ class TestSecurity():
|
||||
sig = sass.signature
|
||||
assert sig.signature_value.text == SIGNED_VALUE
|
||||
assert len(sig.signed_info.reference) == 1
|
||||
assert len(sig.signed_info.reference[0].digest_value) == 1
|
||||
assert sig.signed_info.reference[0].digest_value[0].text == DIGEST_VALUE
|
||||
assert sig.signed_info.reference[0].digest_value
|
||||
assert sig.signed_info.reference[0].digest_value.text == DIGEST_VALUE
|
||||
|
||||
def test_sign_response(self):
|
||||
s_response = sigver.signed_instance_factory(samlp.Response, {
|
||||
"assertion" : self._assertion,
|
||||
"id": "22222",
|
||||
"signature": sigver.pre_signature_part("22222"),
|
||||
}, self.sec)
|
||||
response = factory(samlp.Response,
|
||||
assertion=self._assertion,
|
||||
id="22222",
|
||||
signature=sigver.pre_signature_part("22222", self.sec.my_cert))
|
||||
|
||||
to_sign = [(class_name(self._assertion), self._assertion.id),
|
||||
(class_name(response), response.id)]
|
||||
s_response = sigver.signed_instance_factory( response, self.sec, to_sign)
|
||||
|
||||
assert s_response != None
|
||||
print s_response
|
||||
@@ -166,37 +162,33 @@ class TestSecurity():
|
||||
assert sass.id == "11111"
|
||||
assert time_util.str_to_time(sass.issue_instant)
|
||||
sig = sass.signature
|
||||
assert sig.signature_value.text == """xObs8g6Kr11NopsCxv9IXPQZ4Qfxag3zZrxcza+NBZwQ2JIXEw6vQDmu/OG+EgUP\n2miVoAY4nbklsiwXZfjXYU3OOUDJr7UDhAHUuki/kcKCV6SMEUFlOcEzDFVg+Fr+\nFsPJhsmit5uRRP5rYA92y1fKf42vViw8LFj+LqlTv2Y="""
|
||||
assert sig.signature_value.text == """upeKPE1pkzXLy9BvKFOSTnjn4du59lQQ74TN5CqDGae9D21uY/zLuOWql7LiSTSi\nC945F0WrOvG7s0eZnpuNPZobdfdeCOffCMMrq5RQ2+abPFBamkjmceuEKGdO5PWQ\nt7B1GkzXAMMgeMuU+YmvIJkHbbv5Yn6M0/ICE/COaKQ="""
|
||||
|
||||
assert len(sig.signed_info.reference) == 1
|
||||
assert len(sig.signed_info.reference[0].digest_value) == 1
|
||||
digest = sig.signed_info.reference[0].digest_value[0].text
|
||||
assert digest == """z6O3mlLmX1a6Wk1F4cgMsAXdA6Q="""
|
||||
assert sig.signed_info.reference[0].digest_value
|
||||
digest = sig.signed_info.reference[0].digest_value.text
|
||||
assert digest == """uX92C/YDroqITDfDY1IeekGtZac="""
|
||||
|
||||
def test_sign_response_2(self):
|
||||
assertion2 = {
|
||||
"version": "2.0",
|
||||
"id": "11122",
|
||||
"issue_instant": "2009-10-30T13:20:28Z",
|
||||
"signature": sigver.pre_signature_part("11122"),
|
||||
"attribute_statement": {
|
||||
"attribute": [{
|
||||
"friendly_name": "surName",
|
||||
"attribute_value": "Fox",
|
||||
},
|
||||
{
|
||||
"friendly_name": "givenName",
|
||||
"attribute_value": "Bear",
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
assertion2 = factory( saml.Assertion,
|
||||
version= "2.0",
|
||||
id= "11122",
|
||||
issue_instant= "2009-10-30T13:20:28Z",
|
||||
signature= sigver.pre_signature_part("11122", self.sec.my_cert),
|
||||
attribute_statement=do_attribute_statement({
|
||||
("","","surName"): ("Fox",""),
|
||||
("","","givenName") :("Bear",""),
|
||||
})
|
||||
)
|
||||
response = factory(samlp.Response,
|
||||
assertion=assertion2,
|
||||
id="22233",
|
||||
signature=sigver.pre_signature_part("22233"))
|
||||
|
||||
s_response = sigver.signed_instance_factory(samlp.Response, {
|
||||
"assertion" : [self._assertion,assertion2],
|
||||
"id": "22233",
|
||||
"signature": sigver.pre_signature_part("22233"),
|
||||
}, self.sec)
|
||||
to_sign = [(class_name(assertion2), assertion2.id),
|
||||
(class_name(response), response.id)]
|
||||
|
||||
s_response = sigver.signed_instance_factory( response, self.sec, to_sign)
|
||||
|
||||
assert s_response != None
|
||||
print s_response
|
||||
@@ -207,23 +199,26 @@ class TestSecurity():
|
||||
assert _eq(sass.keyswv(), ['attribute_statement', 'issue_instant',
|
||||
'version', 'signature', 'id'])
|
||||
assert sass.version == "2.0"
|
||||
assert sass.id == "11111"
|
||||
assert sass.id == "11122"
|
||||
assert time_util.str_to_time(sass.issue_instant)
|
||||
sig = sass.signature
|
||||
assert sig.signature_value.text == """xObs8g6Kr11NopsCxv9IXPQZ4Qfxag3zZrxcza+NBZwQ2JIXEw6vQDmu/OG+EgUP\n2miVoAY4nbklsiwXZfjXYU3OOUDJr7UDhAHUuki/kcKCV6SMEUFlOcEzDFVg+Fr+\nFsPJhsmit5uRRP5rYA92y1fKf42vViw8LFj+LqlTv2Y="""
|
||||
assert sig.signature_value.text == """xHECLk1jj4NBvk1jhGrb2mwnrLFKXk6JN3NogjMVMtnnarg9vtk7jYzy1M9RPWdj\nRSa2Jph7yVZJENm4bGuBkT91w+FYm2X4jREULPUsnupPHTQyhJEVZ07EhnluOWa3\n95KkqnZ5gbnTxn1ZvpsANzThLmYY3eSGzNXz+S7758M="""
|
||||
|
||||
assert len(sig.signed_info.reference) == 1
|
||||
assert len(sig.signed_info.reference[0].digest_value) == 1
|
||||
digest = sig.signed_info.reference[0].digest_value[0].text
|
||||
assert digest == """z6O3mlLmX1a6Wk1F4cgMsAXdA6Q="""
|
||||
assert sig.signed_info.reference[0].digest_value
|
||||
digest = sig.signed_info.reference[0].digest_value.text
|
||||
assert digest == """l36wHa6Lyed9ZeAZ3jFL77wPVQ4="""
|
||||
|
||||
def test_sign_verify(self):
|
||||
s_response = sigver.signed_instance_factory(samlp.Response, {
|
||||
"assertion" : self._assertion,
|
||||
"id": "22222",
|
||||
"signature": sigver.pre_signature_part("22222",
|
||||
self.sec.my_cert),
|
||||
}, self.sec)
|
||||
response = factory(samlp.Response,
|
||||
assertion=self._assertion,
|
||||
id="22233",
|
||||
signature=sigver.pre_signature_part("22233", self.sec.my_cert))
|
||||
|
||||
to_sign = [(class_name(self._assertion), self._assertion.id),
|
||||
(class_name(response), response.id)]
|
||||
|
||||
s_response = sigver.signed_instance_factory(response, self.sec, to_sign)
|
||||
|
||||
print s_response
|
||||
res = self.sec.verify_signature("%s" % s_response,
|
||||
@@ -233,16 +228,25 @@ class TestSecurity():
|
||||
assert res
|
||||
|
||||
def test_sign_verify_with_cert_from_instance(self):
|
||||
s_response = sigver.signed_instance_factory(samlp.Response, {
|
||||
"assertion" : self._assertion,
|
||||
"id": "22222",
|
||||
"signature": sigver.pre_signature_part("22222",
|
||||
self.sec.my_cert),
|
||||
}, self.sec)
|
||||
response = factory(samlp.Response,
|
||||
assertion=self._assertion,
|
||||
id="22222",
|
||||
signature=sigver.pre_signature_part("22222", self.sec.my_cert))
|
||||
|
||||
print s_response
|
||||
to_sign = [(class_name(self._assertion), self._assertion.id),
|
||||
(class_name(response), response.id)]
|
||||
|
||||
s_response = sigver.signed_instance_factory(response, self.sec, to_sign)
|
||||
|
||||
print s_response.keyswv()
|
||||
print s_response.signature.keyswv()
|
||||
print s_response.signature.key_info.keyswv()
|
||||
|
||||
ci = "".join(sigver.cert_from_instance(s_response)[0].split())
|
||||
|
||||
print ci
|
||||
print self.sec.my_cert
|
||||
|
||||
assert ci == self.sec.my_cert
|
||||
|
||||
res = self.sec.verify_signature("%s" % s_response,
|
||||
@@ -254,26 +258,19 @@ class TestSecurity():
|
||||
assert res == s_response
|
||||
|
||||
def test_sign_verify_assertion_with_cert_from_instance(self):
|
||||
assertion = {
|
||||
"version": "2.0",
|
||||
"id": "11111",
|
||||
"issue_instant": "2009-10-30T13:20:28Z",
|
||||
"signature": sigver.pre_signature_part("11111", self.sec.my_cert),
|
||||
"attribute_statement": {
|
||||
"attribute": [{
|
||||
"friendly_name": "surName",
|
||||
"attribute_value": "Foo",
|
||||
},
|
||||
{
|
||||
"friendly_name": "givenName",
|
||||
"attribute_value": "Bar",
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
assertion = factory( saml.Assertion,
|
||||
version= "2.0",
|
||||
id= "11100",
|
||||
issue_instant= "2009-10-30T13:20:28Z",
|
||||
signature= sigver.pre_signature_part("11100", self.sec.my_cert),
|
||||
attribute_statement=do_attribute_statement({
|
||||
("","","surName"): ("Fox",""),
|
||||
("","","givenName") :("Bear",""),
|
||||
})
|
||||
)
|
||||
|
||||
s_assertion = sigver.signed_instance_factory(saml.Assertion,
|
||||
assertion, self.sec)
|
||||
to_sign = [(class_name(assertion), assertion.id)]
|
||||
s_assertion = sigver.signed_instance_factory(assertion, self.sec, to_sign)
|
||||
print s_assertion
|
||||
|
||||
ci = "".join(sigver.cert_from_instance(s_assertion)[0].split())
|
||||
@@ -289,30 +286,25 @@ class TestSecurity():
|
||||
assert res
|
||||
|
||||
def test_exception_sign_verify_with_cert_from_instance(self):
|
||||
assertion = {
|
||||
"version": "2.0",
|
||||
"id": "11111",
|
||||
"issue_instant": "2009-10-30T13:20:28Z",
|
||||
#"signature": sigver.pre_signature_part("11111"),
|
||||
"attribute_statement": {
|
||||
"attribute": [{
|
||||
"friendly_name": "surName",
|
||||
"attribute_value": "Foo",
|
||||
},
|
||||
{
|
||||
"friendly_name": "givenName",
|
||||
"attribute_value": "Bar",
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
assertion = factory( saml.Assertion,
|
||||
version= "2.0",
|
||||
id= "11100",
|
||||
issue_instant= "2009-10-30T13:20:28Z",
|
||||
#signature= sigver.pre_signature_part("11100", self.sec.my_cert),
|
||||
attribute_statement=do_attribute_statement({
|
||||
("","","surName"): ("Foo",""),
|
||||
("","","givenName") :("Bar",""),
|
||||
})
|
||||
)
|
||||
|
||||
s_response = sigver.signed_instance_factory(samlp.Response, {
|
||||
"assertion" : assertion,
|
||||
"id": "22222",
|
||||
"signature": sigver.pre_signature_part("22222",
|
||||
self.sec.my_cert),
|
||||
}, self.sec)
|
||||
response = factory(samlp.Response,
|
||||
assertion=assertion,
|
||||
id="22222",
|
||||
signature=sigver.pre_signature_part("22222", self.sec.my_cert))
|
||||
|
||||
to_sign = [(class_name(response), response.id)]
|
||||
|
||||
s_response = sigver.signed_instance_factory(response, self.sec, to_sign)
|
||||
|
||||
|
||||
# Change something that should make everything fail
|
||||
@@ -320,16 +312,3 @@ class TestSecurity():
|
||||
raises(sigver.SignatureError, self.sec._check_signature,
|
||||
"%s" % s_response, s_response, class_name(s_response))
|
||||
|
||||
# def test_sign_verify_assertion_in_response(self):
|
||||
# s_response = sigver.signed_instance_factory(samlp.Response, {
|
||||
# "assertion" : self._assertion,
|
||||
# "id": "22222",
|
||||
# "signature": sigver.pre_signature_part("22222",
|
||||
# self.sec.my_cert, 2),
|
||||
# }, self.sec)
|
||||
#
|
||||
# print s_response
|
||||
# res = self.sec.correctly_signed_response("%s" % s_response)
|
||||
#
|
||||
# print res
|
||||
# assert res
|
||||
|
||||
@@ -1,655 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (C) 2007 SIOS Technology, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Tests for xmldsig"""
|
||||
|
||||
__author__ = 'tmatsuo@example.com (Takashi MATSUO)'
|
||||
|
||||
import unittest
|
||||
try:
|
||||
from xml.etree import ElementTree
|
||||
except ImportError:
|
||||
from elementtree import ElementTree
|
||||
import ds_data
|
||||
import xmldsig as ds
|
||||
|
||||
class TestObject:
|
||||
|
||||
def setup_class(self):
|
||||
self.object = ds.Object()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for Object accessors"""
|
||||
self.object.identifier = "object_id"
|
||||
self.object.mime_type = "test/plain; charset=UTF-8"
|
||||
self.object.encoding = ds.ENCODING_BASE64
|
||||
new_object = ds.object_from_string(self.object.to_string())
|
||||
assert new_object.identifier == "object_id"
|
||||
assert new_object.mime_type == "test/plain; charset=UTF-8"
|
||||
assert new_object.encoding == ds.ENCODING_BASE64
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for object_from_string() using test data"""
|
||||
new_object = ds.object_from_string(ds_data.TEST_OBJECT)
|
||||
assert new_object.identifier == "object_id"
|
||||
assert new_object.encoding == ds.ENCODING_BASE64
|
||||
assert new_object.text.strip() == \
|
||||
"V2VkIEp1biAgNCAxMjoxMTowMyBFRFQgMjAwMwo"
|
||||
|
||||
|
||||
class TestMgmtData:
|
||||
|
||||
def setup_class(self):
|
||||
self.mgmt_data = ds.MgmtData()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for MgmtData accessors"""
|
||||
self.mgmt_data.text = "mgmt data"
|
||||
new_mgmt_data = ds.mgmt_data_from_string(self.mgmt_data.to_string())
|
||||
assert new_mgmt_data.text.strip() == "mgmt data"
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for mgmt_data_from_string() using test data"""
|
||||
new_mgmt_data = ds.mgmt_data_from_string(ds_data.TEST_MGMT_DATA)
|
||||
assert new_mgmt_data.text.strip() == "mgmt data"
|
||||
|
||||
|
||||
class TestSPKISexp:
|
||||
|
||||
def setup_class(self):
|
||||
self.spki_sexp = ds.SPKISexp()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for SPKISexp accessors"""
|
||||
self.spki_sexp.text = "spki sexp"
|
||||
new_spki_sexp = ds.spki_sexp_from_string(self.spki_sexp.to_string())
|
||||
assert new_spki_sexp.text.strip() == "spki sexp"
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for spki_sexp_from_string() using test data"""
|
||||
new_spki_sexp = ds.spki_sexp_from_string(ds_data.TEST_SPKI_SEXP)
|
||||
assert new_spki_sexp.text.strip() == "spki sexp"
|
||||
|
||||
|
||||
class TestSPKIData:
|
||||
|
||||
def setup_class(self):
|
||||
self.spki_data = ds.SPKIData()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for SPKIData accessors"""
|
||||
self.spki_data.spki_sexp.append(
|
||||
ds.spki_sexp_from_string(ds_data.TEST_SPKI_SEXP))
|
||||
new_spki_data = ds.spki_data_from_string(self.spki_data.to_string())
|
||||
assert new_spki_data.spki_sexp[0].text.strip() == "spki sexp"
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for spki_data_from_string() using test data"""
|
||||
new_spki_data = ds.spki_data_from_string(ds_data.TEST_SPKI_DATA)
|
||||
assert new_spki_data.spki_sexp[0].text.strip() == "spki sexp"
|
||||
assert new_spki_data.spki_sexp[1].text.strip() == "spki sexp2"
|
||||
|
||||
|
||||
class TestPGPData:
|
||||
|
||||
def setup_class(self):
|
||||
self.pgp_data = ds.PGPData()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for PGPData accessors"""
|
||||
self.pgp_data.pgp_key_id = ds.PGPKeyID(text="pgp key id")
|
||||
self.pgp_data.pgp_key_packet = ds.PGPKeyPacket(text="pgp key packet")
|
||||
new_pgp_data = ds.pgp_data_from_string(self.pgp_data.to_string())
|
||||
assert isinstance(new_pgp_data.pgp_key_id, ds.PGPKeyID)
|
||||
assert isinstance(new_pgp_data.pgp_key_packet, ds.PGPKeyPacket)
|
||||
assert new_pgp_data.pgp_key_id.text.strip() == "pgp key id"
|
||||
assert new_pgp_data.pgp_key_packet.text.strip() == "pgp key packet"
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for pgp_data_from_string() using test data"""
|
||||
new_pgp_data = ds.pgp_data_from_string(ds_data.TEST_PGP_DATA)
|
||||
assert isinstance(new_pgp_data.pgp_key_id, ds.PGPKeyID)
|
||||
assert isinstance(new_pgp_data.pgp_key_packet, ds.PGPKeyPacket)
|
||||
assert new_pgp_data.pgp_key_id.text.strip() == "pgp key id"
|
||||
assert new_pgp_data.pgp_key_packet.text.strip() == "pgp key packet"
|
||||
|
||||
|
||||
class TestX509IssuerSerial:
|
||||
|
||||
def setup_class(self):
|
||||
self.x509_issuer_serial = ds.X509IssuerSerial()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for X509IssuerSerial accessors"""
|
||||
self.x509_issuer_serial.x509_issuer_name = ds.X509IssuerName(
|
||||
text="issuer name")
|
||||
self.x509_issuer_serial.x509_issuer_number = ds.X509IssuerNumber(text="1")
|
||||
new_x509_issuer_serial = ds.x509_issuer_serial_from_string(
|
||||
self.x509_issuer_serial.to_string())
|
||||
assert new_x509_issuer_serial.x509_issuer_name.text.strip() == \
|
||||
"issuer name"
|
||||
assert new_x509_issuer_serial.x509_issuer_number.text.strip() == "1"
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for x509_issuer_serial_from_string() using test data"""
|
||||
new_x509_issuer_serial = ds.x509_issuer_serial_from_string(
|
||||
ds_data.TEST_X509_ISSUER_SERIAL)
|
||||
assert new_x509_issuer_serial.x509_issuer_name.text.strip() == \
|
||||
"issuer name"
|
||||
assert new_x509_issuer_serial.x509_issuer_number.text.strip() == "1"
|
||||
|
||||
|
||||
class TestX509Data:
|
||||
|
||||
def setup_class(self):
|
||||
self.x509_data = ds.X509Data()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for X509Data accessors"""
|
||||
self.x509_data.x509_issuer_serial.append(ds.x509_issuer_serial_from_string(
|
||||
ds_data.TEST_X509_ISSUER_SERIAL))
|
||||
self.x509_data.x509_ski.append(ds.X509SKI(text="x509 ski"))
|
||||
self.x509_data.x509_subject_name.append(ds.X509SubjectName(
|
||||
text="x509 subject name"))
|
||||
self.x509_data.x509_certificate.append(ds.X509Certificate(
|
||||
text="x509 certificate"))
|
||||
self.x509_data.x509_crl.append(ds.X509CRL(text="x509 crl"))
|
||||
new_x509_data = ds.x509_data_from_string(self.x509_data.to_string())
|
||||
assert isinstance(new_x509_data.x509_issuer_serial[0],
|
||||
ds.X509IssuerSerial)
|
||||
assert new_x509_data.x509_ski[0].text.strip() == "x509 ski"
|
||||
assert isinstance(new_x509_data.x509_ski[0], ds.X509SKI)
|
||||
assert new_x509_data.x509_subject_name[0].text.strip() == \
|
||||
"x509 subject name"
|
||||
assert isinstance(new_x509_data.x509_subject_name[0],
|
||||
ds.X509SubjectName)
|
||||
assert new_x509_data.x509_certificate[0].text.strip() == \
|
||||
"x509 certificate"
|
||||
assert isinstance(new_x509_data.x509_certificate[0],
|
||||
ds.X509Certificate)
|
||||
assert new_x509_data.x509_crl[0].text.strip() == "x509 crl"
|
||||
assert isinstance(new_x509_data.x509_crl[0],ds.X509CRL)
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for x509_data_from_string() using test data"""
|
||||
new_x509_data = ds.x509_data_from_string(ds_data.TEST_X509_DATA)
|
||||
assert isinstance(new_x509_data.x509_issuer_serial[0],
|
||||
ds.X509IssuerSerial)
|
||||
assert new_x509_data.x509_ski[0].text.strip() == "x509 ski"
|
||||
assert isinstance(new_x509_data.x509_ski[0], ds.X509SKI)
|
||||
assert new_x509_data.x509_subject_name[0].text.strip() == \
|
||||
"x509 subject name"
|
||||
assert isinstance(new_x509_data.x509_subject_name[0],
|
||||
ds.X509SubjectName)
|
||||
assert new_x509_data.x509_certificate[0].text.strip() == \
|
||||
"x509 certificate"
|
||||
assert isinstance(new_x509_data.x509_certificate[0],
|
||||
ds.X509Certificate)
|
||||
assert new_x509_data.x509_crl[0].text.strip() == "x509 crl"
|
||||
assert isinstance(new_x509_data.x509_crl[0],ds.X509CRL)
|
||||
|
||||
|
||||
class TestTransform:
|
||||
|
||||
def setup_class(self):
|
||||
self.transform = ds.Transform()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for Transform accessors"""
|
||||
self.transform.xpath.append(ds.XPath(text="xpath"))
|
||||
self.transform.algorithm = ds.TRANSFORM_ENVELOPED
|
||||
new_transform = ds.transform_from_string(self.transform.to_string())
|
||||
assert isinstance(new_transform.xpath[0], ds.XPath)
|
||||
assert new_transform.xpath[0].text.strip() == "xpath"
|
||||
assert new_transform.algorithm == ds.TRANSFORM_ENVELOPED
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for transform_from_string() using test data"""
|
||||
new_transform = ds.transform_from_string(ds_data.TEST_TRANSFORM)
|
||||
assert isinstance(new_transform.xpath[0], ds.XPath)
|
||||
assert new_transform.xpath[0].text.strip() == "xpath"
|
||||
assert new_transform.algorithm == ds.TRANSFORM_ENVELOPED
|
||||
|
||||
|
||||
class TestTransforms:
|
||||
|
||||
def setup_class(self):
|
||||
self.transforms = ds.Transforms()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for Transforms accessors"""
|
||||
self.transforms.transform.append(
|
||||
ds.transform_from_string(ds_data.TEST_TRANSFORM))
|
||||
self.transforms.transform.append(
|
||||
ds.transform_from_string(ds_data.TEST_TRANSFORM))
|
||||
new_transforms = ds.transforms_from_string(self.transforms.to_string())
|
||||
assert isinstance(new_transforms.transform[0], ds.Transform)
|
||||
assert isinstance(new_transforms.transform[1], ds.Transform)
|
||||
assert new_transforms.transform[0].algorithm == \
|
||||
ds.TRANSFORM_ENVELOPED
|
||||
assert new_transforms.transform[1].algorithm == \
|
||||
ds.TRANSFORM_ENVELOPED
|
||||
assert new_transforms.transform[0].xpath[0].text.strip() == "xpath"
|
||||
assert new_transforms.transform[1].xpath[0].text.strip() == "xpath"
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for transform_from_string() using test data"""
|
||||
new_transforms = ds.transforms_from_string(ds_data.TEST_TRANSFORMS)
|
||||
assert isinstance(new_transforms.transform[0], ds.Transform)
|
||||
assert isinstance(new_transforms.transform[1], ds.Transform)
|
||||
assert new_transforms.transform[0].algorithm == \
|
||||
ds.TRANSFORM_ENVELOPED
|
||||
assert new_transforms.transform[1].algorithm == \
|
||||
ds.TRANSFORM_ENVELOPED
|
||||
assert new_transforms.transform[0].xpath[0].text.strip() == "xpath"
|
||||
assert new_transforms.transform[1].xpath[0].text.strip() == "xpath"
|
||||
|
||||
|
||||
class TestRetrievalMethod:
|
||||
|
||||
def setup_class(self):
|
||||
self.retrieval_method = ds.RetrievalMethod()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for RetrievalMethod accessors"""
|
||||
self.retrieval_method.uri = "http://www.example.com/URI"
|
||||
self.retrieval_method.type = "http://www.example.com/Type"
|
||||
self.retrieval_method.transforms.append(ds.transforms_from_string(
|
||||
ds_data.TEST_TRANSFORMS))
|
||||
new_retrieval_method = ds.retrieval_method_from_string(
|
||||
self.retrieval_method.to_string())
|
||||
assert new_retrieval_method.uri == "http://www.example.com/URI"
|
||||
assert new_retrieval_method.type == "http://www.example.com/Type"
|
||||
assert isinstance(new_retrieval_method.transforms[0], ds.Transforms)
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for retrieval_method_from_string() using test data"""
|
||||
new_retrieval_method = ds.retrieval_method_from_string(
|
||||
ds_data.TEST_RETRIEVAL_METHOD)
|
||||
assert new_retrieval_method.uri == "http://www.example.com/URI"
|
||||
assert new_retrieval_method.type == "http://www.example.com/Type"
|
||||
assert isinstance(new_retrieval_method.transforms[0], ds.Transforms)
|
||||
|
||||
|
||||
class TestRSAKeyValue:
|
||||
|
||||
def setup_class(self):
|
||||
self.rsa_key_value = ds.RSAKeyValue()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for RSAKeyValue accessors"""
|
||||
self.rsa_key_value.modulus = ds.Modulus(text="modulus")
|
||||
self.rsa_key_value.exponent = ds.Exponent(text="exponent")
|
||||
new_rsa_key_value = ds.rsa_key_value_from_string(self.rsa_key_value.to_string())
|
||||
assert isinstance(new_rsa_key_value.modulus, ds.Modulus)
|
||||
assert isinstance(new_rsa_key_value.exponent, ds.Exponent)
|
||||
assert new_rsa_key_value.modulus.text.strip() == "modulus"
|
||||
assert new_rsa_key_value.exponent.text.strip() == "exponent"
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for rsa_key_value_from_string() using test data"""
|
||||
new_rsa_key_value = ds.rsa_key_value_from_string(
|
||||
ds_data.TEST_RSA_KEY_VALUE)
|
||||
assert isinstance(new_rsa_key_value.modulus, ds.Modulus)
|
||||
assert isinstance(new_rsa_key_value.exponent, ds.Exponent)
|
||||
assert new_rsa_key_value.modulus.text.strip() == "modulus"
|
||||
assert new_rsa_key_value.exponent.text.strip() == "exponent"
|
||||
|
||||
|
||||
class TestDSAKeyValue:
|
||||
|
||||
def setup_class(self):
|
||||
self.dsa_key_value = ds.DSAKeyValue()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for DSAKeyValue accessors"""
|
||||
self.dsa_key_value.p = ds.DsP(text="p")
|
||||
self.dsa_key_value.q = ds.DsQ(text="q")
|
||||
self.dsa_key_value.g = ds.DsG(text="g")
|
||||
self.dsa_key_value.y = ds.DsY(text="y")
|
||||
self.dsa_key_value.j = ds.DsJ(text="j")
|
||||
self.dsa_key_value.seed = ds.Seed(text="seed")
|
||||
self.dsa_key_value.pgen_counter = ds.PgenCounter(text="pgen counter")
|
||||
new_dsa_key_value = ds.dsa_key_value_from_string(self.dsa_key_value.to_string())
|
||||
assert isinstance(new_dsa_key_value.p, ds.DsP)
|
||||
assert isinstance(new_dsa_key_value.q, ds.DsQ)
|
||||
assert isinstance(new_dsa_key_value.g, ds.DsG)
|
||||
assert isinstance(new_dsa_key_value.y, ds.DsY)
|
||||
assert isinstance(new_dsa_key_value.j, ds.DsJ)
|
||||
assert isinstance(new_dsa_key_value.seed, ds.Seed)
|
||||
assert isinstance(new_dsa_key_value.pgen_counter, ds.PgenCounter)
|
||||
assert new_dsa_key_value.p.text.strip() == "p"
|
||||
assert new_dsa_key_value.q.text.strip() == "q"
|
||||
assert new_dsa_key_value.g.text.strip() == "g"
|
||||
assert new_dsa_key_value.y.text.strip() == "y"
|
||||
assert new_dsa_key_value.j.text.strip() == "j"
|
||||
assert new_dsa_key_value.seed.text.strip() == "seed"
|
||||
assert new_dsa_key_value.pgen_counter.text.strip() == "pgen counter"
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for dsa_key_value_from_string() using test data"""
|
||||
new_dsa_key_value = ds.dsa_key_value_from_string(
|
||||
ds_data.TEST_DSA_KEY_VALUE)
|
||||
assert isinstance(new_dsa_key_value.p, ds.DsP)
|
||||
assert isinstance(new_dsa_key_value.q, ds.DsQ)
|
||||
assert isinstance(new_dsa_key_value.g, ds.DsG)
|
||||
assert isinstance(new_dsa_key_value.y, ds.DsY)
|
||||
assert isinstance(new_dsa_key_value.j, ds.DsJ)
|
||||
assert isinstance(new_dsa_key_value.seed, ds.Seed)
|
||||
assert isinstance(new_dsa_key_value.pgen_counter, ds.PgenCounter)
|
||||
assert new_dsa_key_value.p.text.strip() == "p"
|
||||
assert new_dsa_key_value.q.text.strip() == "q"
|
||||
assert new_dsa_key_value.g.text.strip() == "g"
|
||||
assert new_dsa_key_value.y.text.strip() == "y"
|
||||
assert new_dsa_key_value.j.text.strip() == "j"
|
||||
assert new_dsa_key_value.seed.text.strip() == "seed"
|
||||
assert new_dsa_key_value.pgen_counter.text.strip() == "pgen counter"
|
||||
|
||||
|
||||
class TestKeyValue:
|
||||
|
||||
def setup_class(self):
|
||||
self.key_value = ds.KeyValue()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for KeyValue accessors"""
|
||||
self.key_value.dsa_key_value = ds.dsa_key_value_from_string(
|
||||
ds_data.TEST_DSA_KEY_VALUE)
|
||||
new_key_value = ds.key_value_from_string(self.key_value.to_string())
|
||||
assert isinstance(new_key_value.dsa_key_value, ds.DSAKeyValue)
|
||||
self.key_value.dsa_key_value = None
|
||||
self.key_value.rsa_key_value = ds.rsa_key_value_from_string(
|
||||
ds_data.TEST_RSA_KEY_VALUE)
|
||||
new_key_value = ds.key_value_from_string(self.key_value.to_string())
|
||||
assert isinstance(new_key_value.rsa_key_value, ds.RSAKeyValue)
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for key_value_from_string() using test data"""
|
||||
new_key_value = ds.key_value_from_string(ds_data.TEST_KEY_VALUE1)
|
||||
assert isinstance(new_key_value.dsa_key_value, ds.DSAKeyValue)
|
||||
self.key_value.dsa_key_value = None
|
||||
self.key_value.rsa_key_value = ds.rsa_key_value_from_string(
|
||||
ds_data.TEST_RSA_KEY_VALUE)
|
||||
new_key_value = ds.key_value_from_string(ds_data.TEST_KEY_VALUE2)
|
||||
assert isinstance(new_key_value.rsa_key_value, ds.RSAKeyValue)
|
||||
|
||||
|
||||
class TestKeyName:
|
||||
|
||||
def setup_class(self):
|
||||
self.key_name = ds.KeyName()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for KeyName accessors"""
|
||||
self.key_name.text = "key name"
|
||||
new_key_name = ds.key_name_from_string(self.key_name.to_string())
|
||||
assert new_key_name.text.strip() == "key name"
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for key_name_from_string() using test data"""
|
||||
new_key_name = ds.key_name_from_string(ds_data.TEST_KEY_NAME)
|
||||
assert new_key_name.text.strip() == "key name"
|
||||
|
||||
|
||||
class TestKeyInfo:
|
||||
def setup_class(self):
|
||||
self.key_info = ds.KeyInfo()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for KeyInfo accessors"""
|
||||
self.key_info.key_name.append(
|
||||
ds.key_name_from_string(ds_data.TEST_KEY_NAME))
|
||||
self.key_info.key_value.append(
|
||||
ds.key_value_from_string(ds_data.TEST_KEY_VALUE1))
|
||||
self.key_info.retrieval_method.append(
|
||||
ds.retrieval_method_from_string(ds_data.TEST_RETRIEVAL_METHOD))
|
||||
self.key_info.x509_data.append(
|
||||
ds.x509_data_from_string(ds_data.TEST_X509_DATA))
|
||||
self.key_info.pgp_data.append(
|
||||
ds.pgp_data_from_string(ds_data.TEST_PGP_DATA))
|
||||
self.key_info.spki_data.append(
|
||||
ds.spki_data_from_string(ds_data.TEST_SPKI_DATA))
|
||||
self.key_info.mgmt_data.append(
|
||||
ds.mgmt_data_from_string(ds_data.TEST_MGMT_DATA))
|
||||
self.key_info.identifier = "id"
|
||||
new_key_info = ds.key_info_from_string(self.key_info.to_string())
|
||||
|
||||
assert isinstance(new_key_info.key_name[0], ds.KeyName)
|
||||
assert isinstance(new_key_info.key_value[0], ds.KeyValue)
|
||||
assert isinstance(new_key_info.retrieval_method[0],
|
||||
ds.RetrievalMethod)
|
||||
assert isinstance(new_key_info.x509_data[0], ds.X509Data)
|
||||
assert isinstance(new_key_info.pgp_data[0], ds.PGPData)
|
||||
assert isinstance(new_key_info.spki_data[0], ds.SPKIData)
|
||||
assert isinstance(new_key_info.mgmt_data[0], ds.MgmtData)
|
||||
assert new_key_info.identifier == "id"
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for key_info_from_string() using test data"""
|
||||
new_key_info = ds.key_info_from_string(ds_data.TEST_KEY_INFO)
|
||||
assert isinstance(new_key_info.key_name[0], ds.KeyName)
|
||||
assert isinstance(new_key_info.key_value[0], ds.KeyValue)
|
||||
assert isinstance(new_key_info.retrieval_method[0],
|
||||
ds.RetrievalMethod)
|
||||
assert isinstance(new_key_info.x509_data[0], ds.X509Data)
|
||||
assert isinstance(new_key_info.pgp_data[0], ds.PGPData)
|
||||
assert isinstance(new_key_info.spki_data[0], ds.SPKIData)
|
||||
assert isinstance(new_key_info.mgmt_data[0], ds.MgmtData)
|
||||
assert new_key_info.identifier == "id"
|
||||
|
||||
|
||||
class TestDigestValue:
|
||||
|
||||
def setup_class(self):
|
||||
self.digest_value = ds.DigestValue()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for DigestValue accessors"""
|
||||
self.digest_value.text = "digest value"
|
||||
new_digest_value = ds.digest_value_from_string(self.digest_value.to_string())
|
||||
assert new_digest_value.text.strip() == "digest value"
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for digest_value_from_string() using test data"""
|
||||
new_digest_value = ds.digest_value_from_string(ds_data.TEST_DIGEST_VALUE)
|
||||
assert new_digest_value.text.strip() == "digest value"
|
||||
|
||||
|
||||
class TestDigestMethod:
|
||||
|
||||
def setup_class(self):
|
||||
self.digest_method = ds.DigestMethod()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for DigestMethod accessors"""
|
||||
self.digest_method.algorithm = ds.DIGEST_SHA1
|
||||
new_digest_method = ds.digest_method_from_string(
|
||||
self.digest_method.to_string())
|
||||
assert new_digest_method.algorithm == ds.DIGEST_SHA1
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for digest_method_from_string() using test data"""
|
||||
new_digest_method = ds.digest_method_from_string(
|
||||
ds_data.TEST_DIGEST_METHOD)
|
||||
assert new_digest_method.algorithm == ds.DIGEST_SHA1
|
||||
|
||||
|
||||
class TestReference:
|
||||
|
||||
def setup_class(self):
|
||||
self.reference = ds.Reference()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for Reference accessors"""
|
||||
self.reference.transforms.append(ds.transforms_from_string(
|
||||
ds_data.TEST_TRANSFORMS))
|
||||
self.reference.digest_method.append(ds.digest_method_from_string(
|
||||
ds_data.TEST_DIGEST_METHOD))
|
||||
self.reference.digest_value.append(ds.digest_value_from_string(
|
||||
ds_data.TEST_DIGEST_VALUE))
|
||||
self.reference.identifier = "id"
|
||||
self.reference.uri = "http://www.example.com/URI"
|
||||
self.reference.type = "http://www.example.com/Type"
|
||||
new_reference = ds.reference_from_string(self.reference.to_string())
|
||||
assert isinstance(new_reference.transforms[0], ds.Transforms)
|
||||
assert isinstance(new_reference.digest_method[0], ds.DigestMethod)
|
||||
assert isinstance(new_reference.digest_value[0], ds.DigestValue)
|
||||
assert new_reference.identifier == "id"
|
||||
assert new_reference.uri == "http://www.example.com/URI"
|
||||
assert new_reference.type == "http://www.example.com/Type"
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for reference_from_string() using test data"""
|
||||
new_reference = ds.reference_from_string(ds_data.TEST_REFERENCE)
|
||||
assert isinstance(new_reference.transforms[0], ds.Transforms)
|
||||
assert isinstance(new_reference.digest_method[0], ds.DigestMethod)
|
||||
assert isinstance(new_reference.digest_value[0], ds.DigestValue)
|
||||
assert new_reference.identifier == "id"
|
||||
assert new_reference.uri == "http://www.example.com/URI"
|
||||
assert new_reference.type == "http://www.example.com/Type"
|
||||
|
||||
|
||||
class TestSignatureMethod:
|
||||
|
||||
def setup_class(self):
|
||||
self.signature_method = ds.SignatureMethod()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for SignatureMethod accessors"""
|
||||
self.signature_method.algorithm = ds.SIG_RSA_SHA1
|
||||
self.signature_method.hmac_output_length = ds.HMACOutputLength(text="8")
|
||||
new_signature_method = ds.signature_method_from_string(
|
||||
self.signature_method.to_string())
|
||||
assert isinstance(new_signature_method.hmac_output_length,
|
||||
ds.HMACOutputLength)
|
||||
assert new_signature_method.hmac_output_length.text.strip() == "8"
|
||||
assert new_signature_method.algorithm == ds.SIG_RSA_SHA1
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for signature_method_from_string() using test data"""
|
||||
new_signature_method = ds.signature_method_from_string(
|
||||
ds_data.TEST_SIGNATURE_METHOD)
|
||||
assert isinstance(new_signature_method.hmac_output_length,
|
||||
ds.HMACOutputLength)
|
||||
assert new_signature_method.hmac_output_length.text.strip() == "8"
|
||||
assert new_signature_method.algorithm == ds.SIG_RSA_SHA1
|
||||
|
||||
|
||||
class TestCanonicalizationMethod:
|
||||
|
||||
def setup_class(self):
|
||||
self.canonicalization_method = ds.CanonicalizationMethod()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for CanonicalizationMethod accessors"""
|
||||
self.canonicalization_method.algorithm = ds.C14N_WITH_C
|
||||
new_canonicalization_method = ds.canonicalization_method_from_string(
|
||||
self.canonicalization_method.to_string())
|
||||
assert new_canonicalization_method.algorithm == ds.C14N_WITH_C
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for canonicalization_method_from_string() using test data"""
|
||||
new_canonicalization_method = ds.canonicalization_method_from_string(
|
||||
ds_data.TEST_CANONICALIZATION_METHOD)
|
||||
assert new_canonicalization_method.algorithm == ds.C14N_WITH_C
|
||||
|
||||
|
||||
class TestSignedInfo:
|
||||
|
||||
def setup_class(self):
|
||||
self.si = ds.SignedInfo()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for SignedInfo accessors"""
|
||||
self.si.identifier = "id"
|
||||
self.si.canonicalization_method = ds.canonicalization_method_from_string(
|
||||
ds_data.TEST_CANONICALIZATION_METHOD)
|
||||
self.si.signature_method = ds.signature_method_from_string(
|
||||
ds_data.TEST_SIGNATURE_METHOD)
|
||||
self.si.reference.append(ds.reference_from_string(
|
||||
ds_data.TEST_REFERENCE))
|
||||
new_si = ds.signed_info_from_string(self.si.to_string())
|
||||
assert new_si.identifier == "id"
|
||||
assert isinstance(new_si.canonicalization_method,
|
||||
ds.CanonicalizationMethod)
|
||||
assert isinstance(new_si.signature_method, ds.SignatureMethod)
|
||||
assert isinstance(new_si.reference[0], ds.Reference)
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for signed_info_from_string() using test data"""
|
||||
new_si = ds.signed_info_from_string(ds_data.TEST_SIGNED_INFO)
|
||||
assert new_si.identifier == "id"
|
||||
assert isinstance(new_si.canonicalization_method,
|
||||
ds.CanonicalizationMethod)
|
||||
assert isinstance(new_si.signature_method, ds.SignatureMethod)
|
||||
assert isinstance(new_si.reference[0], ds.Reference)
|
||||
|
||||
class TestSignatureValue:
|
||||
|
||||
def setup_class(self):
|
||||
self.signature_value = ds.SignatureValue()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for SignatureValue accessors"""
|
||||
self.signature_value.identifier = "id"
|
||||
self.signature_value.text = "signature value"
|
||||
new_signature_value = ds.signature_value_from_string(
|
||||
self.signature_value.to_string())
|
||||
assert new_signature_value.identifier == "id"
|
||||
assert new_signature_value.text.strip() == "signature value"
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for signature_value_from_string() using test data"""
|
||||
new_signature_value = ds.signature_value_from_string(
|
||||
ds_data.TEST_SIGNATURE_VALUE)
|
||||
assert new_signature_value.identifier == "id"
|
||||
assert new_signature_value.text.strip() == "signature value"
|
||||
|
||||
|
||||
class TestSignature:
|
||||
|
||||
def setup_class(self):
|
||||
self.signature = ds.Signature()
|
||||
|
||||
def testAccessors(self):
|
||||
"""Test for Signature accessors"""
|
||||
self.signature.id = "id"
|
||||
self.signature.signed_info = ds.signed_info_from_string(
|
||||
ds_data.TEST_SIGNED_INFO)
|
||||
self.signature.signature_value = ds.signature_value_from_string(
|
||||
ds_data.TEST_SIGNATURE_VALUE)
|
||||
self.signature.key_info = ds.key_info_from_string(ds_data.TEST_KEY_INFO)
|
||||
self.signature.object.append(ds.object_from_string(ds_data.TEST_OBJECT))
|
||||
|
||||
new_signature = ds.signature_from_string(self.signature.to_string())
|
||||
assert new_signature.id == "id"
|
||||
assert isinstance(new_signature.signed_info, ds.SignedInfo)
|
||||
assert isinstance(new_signature.signature_value, ds.SignatureValue)
|
||||
assert isinstance(new_signature.key_info, ds.KeyInfo)
|
||||
assert isinstance(new_signature.object[0], ds.Object)
|
||||
|
||||
def testUsingTestData(self):
|
||||
"""Test for signature_value_from_string() using test data"""
|
||||
new_signature = ds.signature_from_string(ds_data.TEST_SIGNATURE)
|
||||
assert new_signature.id == "id"
|
||||
assert isinstance(new_signature.signed_info, ds.SignedInfo)
|
||||
assert isinstance(new_signature.signature_value, ds.SignatureValue)
|
||||
assert isinstance(new_signature.key_info, ds.KeyInfo)
|
||||
assert isinstance(new_signature.object[0], ds.Object)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -1,175 +0,0 @@
|
||||
import saml2
|
||||
import xmlenc
|
||||
import xmldsig
|
||||
|
||||
data1 = """<?xml version='1.0'?>
|
||||
<EncryptedData xmlns='http://www.w3.org/2001/04/xmlenc#'
|
||||
MimeType='text/xml'>
|
||||
<CipherData>
|
||||
<CipherValue>A23B45C56</CipherValue>
|
||||
</CipherData>
|
||||
</EncryptedData>"""
|
||||
|
||||
def test_1():
|
||||
ed = xmlenc.encrypted_data_from_string(data1)
|
||||
assert ed
|
||||
assert ed.mime_type == "text/xml"
|
||||
assert len(ed.cipher_data) == 1
|
||||
cd = ed.cipher_data[0]
|
||||
assert len(cd.cipher_value) == 1
|
||||
assert cd.cipher_value[0].text == "A23B45C56"
|
||||
|
||||
data2 = """<EncryptedData xmlns='http://www.w3.org/2001/04/xmlenc#'
|
||||
Type='http://www.w3.org/2001/04/xmlenc#Element'>
|
||||
<EncryptionMethod
|
||||
Algorithm='http://www.w3.org/2001/04/xmlenc#tripledes-cbc'/>
|
||||
<ds:KeyInfo xmlns:ds='http://www.w3.org/2000/09/xmldsig#'>
|
||||
<ds:KeyName>John Smith</ds:KeyName>
|
||||
</ds:KeyInfo>
|
||||
<CipherData><CipherValue>DEADBEEF</CipherValue></CipherData>
|
||||
</EncryptedData>"""
|
||||
|
||||
def test_2():
|
||||
ed = xmlenc.encrypted_data_from_string(data2)
|
||||
assert ed
|
||||
print ed
|
||||
assert ed.typ == "http://www.w3.org/2001/04/xmlenc#Element"
|
||||
assert len(ed.encryption_method) == 1
|
||||
em = ed.encryption_method[0]
|
||||
assert em.algorithm == 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc'
|
||||
assert len(ed.key_info) == 1
|
||||
ki = ed.key_info[0]
|
||||
assert ki.key_name[0].text == "John Smith"
|
||||
assert len(ed.cipher_data) == 1
|
||||
cd = ed.cipher_data[0]
|
||||
assert len(cd.cipher_value) == 1
|
||||
assert cd.cipher_value[0].text == "DEADBEEF"
|
||||
|
||||
data3 = """<EncryptedData Id='ED'
|
||||
xmlns='http://www.w3.org/2001/04/xmlenc#'>
|
||||
<EncryptionMethod
|
||||
Algorithm='http://www.w3.org/2001/04/xmlenc#aes128-cbc'/>
|
||||
<ds:KeyInfo xmlns:ds='http://www.w3.org/2000/09/xmldsig#'>
|
||||
<ds:RetrievalMethod URI='#EK'
|
||||
Type="http://www.w3.org/2001/04/xmlenc#EncryptedKey"/>
|
||||
<ds:KeyName>Sally Doe</ds:KeyName>
|
||||
</ds:KeyInfo>
|
||||
<CipherData><CipherValue>DEADBEEF</CipherValue></CipherData>
|
||||
</EncryptedData>"""
|
||||
|
||||
def test_3():
|
||||
ed = xmlenc.encrypted_data_from_string(data3)
|
||||
assert ed
|
||||
print ed
|
||||
assert len(ed.encryption_method) == 1
|
||||
em = ed.encryption_method[0]
|
||||
assert em.algorithm == 'http://www.w3.org/2001/04/xmlenc#aes128-cbc'
|
||||
assert len(ed.key_info) == 1
|
||||
ki = ed.key_info[0]
|
||||
assert ki.key_name[0].text == "Sally Doe"
|
||||
assert len(ki.retrieval_method) == 1
|
||||
rm = ki.retrieval_method[0]
|
||||
assert rm.uri == "#EK"
|
||||
assert rm.type == "http://www.w3.org/2001/04/xmlenc#EncryptedKey"
|
||||
assert len(ed.cipher_data) == 1
|
||||
cd = ed.cipher_data[0]
|
||||
assert len(cd.cipher_value) == 1
|
||||
assert cd.cipher_value[0].text == "DEADBEEF"
|
||||
|
||||
data4 = """<EncryptedKey Id='EK' xmlns='http://www.w3.org/2001/04/xmlenc#'>
|
||||
<EncryptionMethod
|
||||
Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5"/>
|
||||
<ds:KeyInfo xmlns:ds='http://www.w3.org/2000/09/xmldsig#'>
|
||||
<ds:KeyName>John Smith</ds:KeyName>
|
||||
</ds:KeyInfo>
|
||||
<CipherData><CipherValue>xyzabc</CipherValue></CipherData>
|
||||
<ReferenceList>
|
||||
<DataReference URI='#ED'/>
|
||||
</ReferenceList>
|
||||
<CarriedKeyName>Sally Doe</CarriedKeyName>
|
||||
</EncryptedKey>"""
|
||||
|
||||
def test_4():
|
||||
ek = xmlenc.encrypted_key_from_string(data4)
|
||||
assert ek
|
||||
print ek
|
||||
assert len(ek.encryption_method) == 1
|
||||
em = ek.encryption_method[0]
|
||||
assert em.algorithm == 'http://www.w3.org/2001/04/xmlenc#rsa-1_5'
|
||||
assert len(ek.key_info) == 1
|
||||
ki = ek.key_info[0]
|
||||
assert ki.key_name[0].text == "John Smith"
|
||||
assert len(ek.reference_list) == 1
|
||||
rl = ek.reference_list[0]
|
||||
assert len(rl.data_reference)
|
||||
dr = rl.data_reference[0]
|
||||
assert dr.uri == "#ED"
|
||||
assert len(ek.cipher_data) == 1
|
||||
cd = ek.cipher_data[0]
|
||||
assert len(cd.cipher_value) == 1
|
||||
assert cd.cipher_value[0].text == "xyzabc"
|
||||
|
||||
data5 = """<CipherReference URI="http://www.example.com/CipherValues.xml"
|
||||
xmlns="http://www.w3.org/2001/04/xmlenc#">
|
||||
<Transforms xmlns:ds='http://www.w3.org/2000/09/xmldsig#'>
|
||||
<ds:Transform
|
||||
Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116">
|
||||
<ds:XPath xmlns:rep="http://www.example.org/repository">
|
||||
self::text()[parent::rep:CipherValue[@Id="example1"]]
|
||||
</ds:XPath>
|
||||
</ds:Transform>
|
||||
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#base64"/>
|
||||
</Transforms>
|
||||
</CipherReference>"""
|
||||
|
||||
def test_5():
|
||||
cr = xmlenc.cipher_reference_from_string(data5)
|
||||
assert cr
|
||||
print cr
|
||||
assert len(cr.transforms) == 1
|
||||
trs = cr.transforms[0]
|
||||
assert len(trs.transform) == 2
|
||||
tr = trs.transform[0]
|
||||
assert tr.algorithm in ["http://www.w3.org/TR/1999/REC-xpath-19991116",
|
||||
"http://www.w3.org/2000/09/xmldsig#base64"]
|
||||
if tr.algorithm == "http://www.w3.org/2000/09/xmldsig#base64":
|
||||
pass
|
||||
elif tr.algorithm == "http://www.w3.org/TR/1999/REC-xpath-19991116":
|
||||
assert len(tr.xpath) == 1
|
||||
xp = tr.xpath[0]
|
||||
assert xp.text.strip() == """self::text()[parent::rep:CipherValue[@Id="example1"]]"""
|
||||
|
||||
|
||||
data6 = """<ReferenceList xmlns="http://www.w3.org/2001/04/xmlenc#">
|
||||
<DataReference URI="#invoice34">
|
||||
<ds:Transforms xmlns:ds='http://www.w3.org/2000/09/xmldsig#'>
|
||||
<ds:Transform Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116">
|
||||
<ds:XPath xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
|
||||
self::xenc:EncryptedData[@Id="example1"]
|
||||
</ds:XPath>
|
||||
</ds:Transform>
|
||||
</ds:Transforms>
|
||||
</DataReference>
|
||||
</ReferenceList>"""
|
||||
|
||||
def test_6():
|
||||
rl = xmlenc.reference_list_from_string(data6)
|
||||
assert rl
|
||||
print rl
|
||||
assert len(rl.data_reference) == 1
|
||||
dr = rl.data_reference[0]
|
||||
assert dr.uri == "#invoice34"
|
||||
assert len(dr.extension_elements) == 1
|
||||
ee = dr.extension_elements[0]
|
||||
assert ee.tag == "Transforms"
|
||||
assert ee.namespace == "http://www.w3.org/2000/09/xmldsig#"
|
||||
trs = saml2.extension_element_to_element(ee, xmldsig.ELEMENT_FROM_STRING,
|
||||
namespace=xmldsig.NAMESPACE)
|
||||
|
||||
assert trs
|
||||
assert len(trs.transform) == 1
|
||||
tr = trs.transform[0]
|
||||
assert tr.algorithm == "http://www.w3.org/TR/1999/REC-xpath-19991116"
|
||||
assert len(tr.xpath) == 1
|
||||
assert tr.xpath[0].text.strip() == """self::xenc:EncryptedData[@Id="example1"]"""
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from saml2 import samlp, BINDING_HTTP_POST
|
||||
from saml2 import saml, utils, config, class_name, make_instance
|
||||
from saml2 import saml, config, class_name, make_instance
|
||||
from saml2.server import Server
|
||||
from saml2.authnresponse import authn_response
|
||||
|
||||
@@ -55,6 +55,7 @@ class TestAuthnResponse:
|
||||
assert self.ar.came_from == 'http://localhost:8088/sso'
|
||||
assert self.ar.session_id() == "12"
|
||||
assert self.ar.ava == {'eduPersonEntitlement': ['Jeter'] }
|
||||
assert self.ar.name_id
|
||||
assert self.ar.issuer() == 'urn:mace:example.com:saml:roland:idp'
|
||||
|
||||
def test_verify_signed_1(self):
|
||||
@@ -72,18 +73,20 @@ class TestAuthnResponse:
|
||||
assert self.ar.session_id() == "12"
|
||||
assert self.ar.ava == {'eduPersonEntitlement': ['Jeter'] }
|
||||
assert self.ar.issuer() == 'urn:mace:example.com:saml:roland:idp'
|
||||
assert self.ar.name_id
|
||||
|
||||
def test_parse_2(self):
|
||||
xml_response = open(XML_RESPONSE_FILE).read()
|
||||
ID = "bahigehogffohiphlfmplepdpcohkhhmheppcdie"
|
||||
self.ar.outstanding_queries = {ID: "http://localhost:8088/foo"}
|
||||
self.ar.requestor = "xenosmilus.umdc.umu.se"
|
||||
self.ar.timeslack = 20000000
|
||||
print self.ar.__dict__
|
||||
# roughly a year, should create the response on the fly
|
||||
self.ar.timeslack = 31536000
|
||||
self.ar.loads(xml_response, decode=False)
|
||||
self.ar.verify()
|
||||
|
||||
print self.ar
|
||||
print self.ar.__dict__
|
||||
assert self.ar.came_from == 'http://localhost:8088/foo'
|
||||
assert self.ar.session_id() == ID
|
||||
assert self.ar.name_id
|
||||
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
|
||||
from saml2.server import Server, Identifier
|
||||
from saml2 import server, make_instance
|
||||
from saml2 import samlp, saml, client, utils, config
|
||||
from saml2.utils import OtherError
|
||||
from saml2.utils import do_attribute_statement
|
||||
from saml2 import samlp, saml, client, config
|
||||
from saml2 import s_utils
|
||||
from saml2.s_utils import OtherError
|
||||
from saml2.s_utils import do_attribute_statement, factory
|
||||
from py.test import raises
|
||||
import shelve
|
||||
import re
|
||||
@@ -25,7 +26,7 @@ class TestServer1():
|
||||
self.client = client.Saml2Client({},conf)
|
||||
|
||||
def test_issuer(self):
|
||||
issuer = make_instance( saml.Issuer, self.server.issuer())
|
||||
issuer = self.server.issuer()
|
||||
assert isinstance(issuer, saml.Issuer)
|
||||
assert _eq(issuer.keyswv(), ["text","format"])
|
||||
assert issuer.format == saml.NAMEID_FORMAT_ENTITY
|
||||
@@ -33,27 +34,24 @@ class TestServer1():
|
||||
|
||||
|
||||
def test_assertion(self):
|
||||
tmp = utils.assertion_factory(
|
||||
subject= utils.args2dict("_aaa",
|
||||
name_id=saml.NAMEID_FORMAT_TRANSIENT),
|
||||
attribute_statement = utils.args2dict(
|
||||
attribute=[
|
||||
utils.args2dict(attribute_value="Derek",
|
||||
friendly_name="givenName"),
|
||||
utils.args2dict(attribute_value="Jeter",
|
||||
friendly_name="surName"),
|
||||
]),
|
||||
assertion = s_utils.assertion_factory(
|
||||
subject= factory(saml.Subject, text="_aaa",
|
||||
name_id=factory(saml.NameID,
|
||||
format=saml.NAMEID_FORMAT_TRANSIENT)),
|
||||
attribute_statement = do_attribute_statement({
|
||||
("","","surName"): ("Jeter",""),
|
||||
("","","givenName") :("Derek",""),
|
||||
}),
|
||||
issuer=self.server.issuer(),
|
||||
)
|
||||
|
||||
assertion = make_instance(saml.Assertion, tmp)
|
||||
assert _eq(assertion.keyswv(),['attribute_statement', 'issuer', 'id',
|
||||
'subject', 'issue_instant', 'version'])
|
||||
assert assertion.version == "2.0"
|
||||
assert assertion.issuer.text == "urn:mace:example.com:saml:roland:idp"
|
||||
#
|
||||
assert len(assertion.attribute_statement) == 1
|
||||
attribute_statement = assertion.attribute_statement[0]
|
||||
assert assertion.attribute_statement
|
||||
attribute_statement = assertion.attribute_statement
|
||||
assert len(attribute_statement.attribute) == 2
|
||||
attr0 = attribute_statement.attribute[0]
|
||||
attr1 = attribute_statement.attribute[1]
|
||||
@@ -70,28 +68,25 @@ class TestServer1():
|
||||
subject = assertion.subject
|
||||
assert _eq(subject.keyswv(),["text", "name_id"])
|
||||
assert subject.text == "_aaa"
|
||||
assert subject.name_id.text == saml.NAMEID_FORMAT_TRANSIENT
|
||||
assert subject.name_id.format == saml.NAMEID_FORMAT_TRANSIENT
|
||||
|
||||
def test_response(self):
|
||||
tmp = utils.response_factory(
|
||||
response = s_utils.response_factory(
|
||||
in_response_to="_012345",
|
||||
destination="https:#www.example.com",
|
||||
status=utils.success_status_factory(),
|
||||
assertion=utils.assertion_factory(
|
||||
subject = utils.args2dict("_aaa",
|
||||
status=s_utils.success_status_factory(),
|
||||
assertion=s_utils.assertion_factory(
|
||||
subject = factory( saml.Subject, text="_aaa",
|
||||
name_id=saml.NAMEID_FORMAT_TRANSIENT),
|
||||
attribute_statement = [
|
||||
utils.args2dict(attribute_value="Derek",
|
||||
friendly_name="givenName"),
|
||||
utils.args2dict(attribute_value="Jeter",
|
||||
friendly_name="surName"),
|
||||
],
|
||||
attribute_statement = do_attribute_statement({
|
||||
("","","surName"): ("Jeter",""),
|
||||
("","","givenName") :("Derek",""),
|
||||
}),
|
||||
issuer=self.server.issuer(),
|
||||
),
|
||||
issuer=self.server.issuer(),
|
||||
)
|
||||
|
||||
response = make_instance(samlp.Response, tmp)
|
||||
print response.keyswv()
|
||||
assert _eq(response.keyswv(),['destination', 'assertion','status',
|
||||
'in_response_to', 'issue_instant',
|
||||
@@ -114,9 +109,9 @@ class TestServer1():
|
||||
my_name = "My real name",
|
||||
)
|
||||
|
||||
intermed = utils.deflate_and_base64_encode(authn_request)
|
||||
intermed = s_utils.deflate_and_base64_encode(authn_request)
|
||||
# should raise an error because faulty spentityid
|
||||
raises(OtherError,self.server.parse_authn_request,intermed)
|
||||
raises(OtherError, self.server.parse_authn_request, intermed)
|
||||
|
||||
def test_parse_faulty_request_to_err_status(self):
|
||||
authn_request = self.client.authn_request(
|
||||
@@ -127,14 +122,13 @@ class TestServer1():
|
||||
my_name = "My real name",
|
||||
)
|
||||
|
||||
intermed = utils.deflate_and_base64_encode(authn_request)
|
||||
intermed = s_utils.deflate_and_base64_encode(authn_request)
|
||||
try:
|
||||
self.server.parse_authn_request(intermed)
|
||||
status = None
|
||||
except OtherError, oe:
|
||||
print oe.args
|
||||
status = make_instance(samlp.Status,
|
||||
utils.status_from_exception_factory(oe))
|
||||
status = s_utils.status_from_exception_factory(oe)
|
||||
|
||||
assert status
|
||||
print status
|
||||
@@ -156,8 +150,9 @@ class TestServer1():
|
||||
)
|
||||
|
||||
print authn_request
|
||||
intermed = utils.deflate_and_base64_encode(authn_request)
|
||||
intermed = s_utils.deflate_and_base64_encode(authn_request)
|
||||
response = self.server.parse_authn_request(intermed)
|
||||
# returns a dictionary
|
||||
print response
|
||||
assert response["consumer_url"] == "http://localhost:8087/"
|
||||
assert response["id"] == "1"
|
||||
@@ -185,12 +180,13 @@ class TestServer1():
|
||||
assert resp.status
|
||||
assert resp.status.status_code.value == samlp.STATUS_SUCCESS
|
||||
assert resp.assertion
|
||||
assert len(resp.assertion) == 1
|
||||
assertion = resp.assertion[0]
|
||||
assert len(assertion.authn_statement) == 1
|
||||
assert resp.assertion
|
||||
assertion = resp.assertion
|
||||
print assertion
|
||||
assert assertion.authn_statement
|
||||
assert assertion.conditions
|
||||
assert len(assertion.attribute_statement) == 1
|
||||
attribute_statement = assertion.attribute_statement[0]
|
||||
assert assertion.attribute_statement
|
||||
attribute_statement = assertion.attribute_statement
|
||||
print attribute_statement
|
||||
assert len(attribute_statement.attribute) == 1
|
||||
attribute = attribute_statement.attribute[0]
|
||||
@@ -200,11 +196,11 @@ class TestServer1():
|
||||
assert attribute.name_format == "urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
|
||||
value = attribute.attribute_value[0]
|
||||
assert value.text.strip() == "Short stop"
|
||||
assert value.type == "xs:string"
|
||||
assert value.get_type() == "xs:string"
|
||||
assert assertion.subject
|
||||
assert assertion.subject.name_id
|
||||
assert len(assertion.subject.subject_confirmation) == 1
|
||||
confirmation = assertion.subject.subject_confirmation[0]
|
||||
assert assertion.subject.subject_confirmation
|
||||
confirmation = assertion.subject.subject_confirmation
|
||||
print confirmation.keyswv()
|
||||
print confirmation.subject_confirmation_data
|
||||
assert confirmation.subject_confirmation_data.in_response_to == "12"
|
||||
@@ -227,7 +223,7 @@ class TestServer1():
|
||||
assert not resp.assertion
|
||||
|
||||
def test_sso_failure_response(self):
|
||||
exc = utils.MissingValue("eduPersonAffiliation missing")
|
||||
exc = s_utils.MissingValue("eduPersonAffiliation missing")
|
||||
resp = self.server.error_response( "http://localhost:8087/", "12",
|
||||
"urn:mace:example.com:saml:roland:sp", exc )
|
||||
|
||||
@@ -253,10 +249,8 @@ class TestServer1():
|
||||
resp_str = self.server.authn_response(ava,
|
||||
"1", "http://local:8087/",
|
||||
"urn:mace:example.com:saml:roland:sp",
|
||||
make_instance(samlp.NameIDPolicy,
|
||||
utils.args2dict(
|
||||
format=saml.NAMEID_FORMAT_TRANSIENT,
|
||||
allow_create="true")),
|
||||
samlp.NameIDPolicy(format=saml.NAMEID_FORMAT_TRANSIENT,
|
||||
allow_create="true"),
|
||||
"foba0001@example.com")
|
||||
|
||||
response = samlp.response_from_string("\n".join(resp_str))
|
||||
@@ -324,12 +318,12 @@ class TestServer2():
|
||||
assert response.version == "2.0"
|
||||
assert response.issuer.text == "urn:mace:example.com:saml:roland:idpr"
|
||||
assert response.status.status_code.value == samlp.STATUS_SUCCESS
|
||||
assert len(response.assertion) == 1
|
||||
assertion = response.assertion[0]
|
||||
assert response.assertion
|
||||
assertion = response.assertion
|
||||
assert assertion.version == "2.0"
|
||||
subject = assertion.subject
|
||||
assert subject.name_id.format == saml.NAMEID_FORMAT_TRANSIENT
|
||||
assert len(subject.subject_confirmation) == 1
|
||||
subject_confirmation = subject.subject_confirmation[0]
|
||||
assert subject.subject_confirmation
|
||||
subject_confirmation = subject.subject_confirmation
|
||||
assert subject_confirmation.subject_confirmation_data.in_response_to == "aaa"
|
||||
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import base64
|
||||
from urlparse import urlparse, parse_qs
|
||||
|
||||
from saml2.client import Saml2Client
|
||||
from saml2 import samlp, client, BINDING_HTTP_POST
|
||||
from saml2 import saml, utils, config, class_name, make_instance
|
||||
from saml2 import saml, s_utils, config, class_name
|
||||
#from saml2.sigver import correctly_signed_authn_request, verify_signature
|
||||
from saml2.server import Server
|
||||
from saml2.s_utils import decode_base64_and_inflate
|
||||
|
||||
import os
|
||||
|
||||
@@ -27,6 +29,8 @@ def ava(attribute_statement):
|
||||
result[name].append(value.text.strip())
|
||||
return result
|
||||
|
||||
def _leq(l1, l2):
|
||||
return set(l1) == set(l2)
|
||||
|
||||
# def test_parse_3():
|
||||
# xml_response = open(XML_RESPONSE_FILE3).read()
|
||||
@@ -41,7 +45,7 @@ def ava(attribute_statement):
|
||||
# assert False
|
||||
|
||||
REQ1 = """<?xml version='1.0' encoding='UTF-8'?>
|
||||
<ns0:AttributeQuery Destination="https://idp.example.com/idp/" ID="1" IssueInstant="%s" Version="2.0" xmlns:ns0="urn:oasis:names:tc:SAML:2.0:protocol"><ns1:Issuer xmlns:ns1="urn:oasis:names:tc:SAML:2.0:assertion">http://vo.example.com/sp1</ns1:Issuer><ns1:Subject xmlns:ns1="urn:oasis:names:tc:SAML:2.0:assertion"><ns1:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent">E8042FB4-4D5B-48C3-8E14-8EDD852790DD</ns1:NameID></ns1:Subject></ns0:AttributeQuery>"""
|
||||
<ns0:AttributeQuery Destination="https://idp.example.com/idp/" ID="1" IssueInstant="%s" Version="2.0" xmlns:ns0="urn:oasis:names:tc:SAML:2.0:protocol"><ns1:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity" xmlns:ns1="urn:oasis:names:tc:SAML:2.0:assertion">urn:mace:example.com:saml:roland:sp</ns1:Issuer><ns1:Subject xmlns:ns1="urn:oasis:names:tc:SAML:2.0:assertion"><ns1:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent">E8042FB4-4D5B-48C3-8E14-8EDD852790DD</ns1:NameID></ns1:Subject></ns0:AttributeQuery>"""
|
||||
|
||||
|
||||
class TestClient:
|
||||
@@ -63,6 +67,7 @@ class TestClient:
|
||||
nameid_format=saml.NAMEID_FORMAT_PERSISTENT)
|
||||
str = "%s" % req.to_string()
|
||||
print str
|
||||
print REQ1 % req.issue_instant
|
||||
assert str == REQ1 % req.issue_instant
|
||||
assert req.destination == "https://idp.example.com/idp/"
|
||||
assert req.id == "1"
|
||||
@@ -72,7 +77,7 @@ class TestClient:
|
||||
assert name_id.format == saml.NAMEID_FORMAT_PERSISTENT
|
||||
assert name_id.text == "E8042FB4-4D5B-48C3-8E14-8EDD852790DD"
|
||||
issuer = req.issuer
|
||||
assert issuer.text == "http://vo.example.com/sp1"
|
||||
assert issuer.text == "urn:mace:example.com:saml:roland:sp"
|
||||
|
||||
def test_create_attribute_query2(self):
|
||||
req = self.client.create_attribute_query("1",
|
||||
@@ -130,7 +135,7 @@ class TestClient:
|
||||
assert req.id == "1"
|
||||
assert req.version == "2.0"
|
||||
assert req.issue_instant
|
||||
assert req.issuer.text == "urn:mace:umu.se:saml/rolandsp"
|
||||
assert req.issuer.text == "urn:mace:example.com:saml:roland:sp"
|
||||
nameid = req.subject.name_id
|
||||
assert nameid.format == saml.NAMEID_FORMAT_TRANSIENT
|
||||
assert nameid.text == "_e7b68a04488f715cda642fbdd90099f5"
|
||||
@@ -146,23 +151,22 @@ class TestClient:
|
||||
assert req == None
|
||||
|
||||
def test_idp_entry(self):
|
||||
idp_entry = make_instance( samlp.IDPEntry,
|
||||
self.client.idp_entry(name="Umeå Universitet",
|
||||
location="https://idp.umu.se/"))
|
||||
idp_entry = self.client.idp_entry(name="Umeå Universitet",
|
||||
location="https://idp.umu.se/")
|
||||
|
||||
assert idp_entry.name == "Umeå Universitet"
|
||||
assert idp_entry.loc == "https://idp.umu.se/"
|
||||
|
||||
def test_scope(self):
|
||||
scope = make_instance(samlp.Scoping, self.client.scoping(
|
||||
[self.client.idp_entry(name="Umeå Universitet",
|
||||
location="https://idp.umu.se/")]))
|
||||
entity_id = "urn:mace:example.com:saml:roland:idp"
|
||||
locs = self.client.metadata.single_sign_on_services(entity_id)
|
||||
scope = self.client.scoping_from_metadata(entity_id, locs)
|
||||
|
||||
assert scope.idp_list
|
||||
assert len(scope.idp_list.idp_entry) == 1
|
||||
idp_entry = scope.idp_list.idp_entry[0]
|
||||
assert idp_entry.name == "Umeå Universitet"
|
||||
assert idp_entry.loc == "https://idp.umu.se/"
|
||||
assert idp_entry.name == 'Example Co'
|
||||
assert idp_entry.loc == ['http://localhost:8088/sso/']
|
||||
|
||||
def test_create_auth_request_0(self):
|
||||
ar_str = self.client.authn_request("1",
|
||||
@@ -186,7 +190,7 @@ class TestClient:
|
||||
assert self.client.config["virtual_organization"].keys() == [
|
||||
"urn:mace:example.com:it:tek"]
|
||||
|
||||
ar_str = self.client.authn_request("1",
|
||||
ar_str = self.client.authn_request("666",
|
||||
"http://www.example.com/sso",
|
||||
"http://www.example.org/service",
|
||||
"urn:mace:example.org:saml:sp",
|
||||
@@ -195,6 +199,7 @@ class TestClient:
|
||||
|
||||
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.destination == "http://www.example.com/sso"
|
||||
assert ar.protocol_binding == BINDING_HTTP_POST
|
||||
@@ -234,17 +239,19 @@ class TestClient:
|
||||
self.client.sec.verify_signature(ar_str, node_name=class_name(ar))
|
||||
|
||||
def test_response(self):
|
||||
IDP = "urn:mace:example.com:saml:roland:idp"
|
||||
|
||||
ava = { "givenName": ["Derek"], "surname": ["Jeter"],
|
||||
"mail": ["derek@nyy.mlb.com"]}
|
||||
|
||||
resp_str = "\n".join(self.server.authn_response(ava,
|
||||
"1", "http://local:8087/",
|
||||
"urn:mace:example.com:saml:roland:sp",
|
||||
make_instance(samlp.NameIDPolicy,
|
||||
utils.args2dict(
|
||||
format=saml.NAMEID_FORMAT_TRANSIENT,
|
||||
allow_create="true")),
|
||||
"foba0001@example.com"))
|
||||
resp_str = "\n".join(self.server.authn_response(
|
||||
identity=ava,
|
||||
in_response_to="1",
|
||||
destination="http://local: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)
|
||||
|
||||
@@ -253,7 +260,106 @@ class TestClient:
|
||||
{"1":"http://foo.example.com/service"})
|
||||
|
||||
assert authn_response != None
|
||||
assert authn_response.issuer() == IDP
|
||||
assert authn_response.response.assertion[0].issuer.text == IDP
|
||||
session_info = authn_response.session_info()
|
||||
assert session_info["ava"] != []
|
||||
|
||||
print session_info
|
||||
assert session_info["ava"] == {'mail': ['derek@nyy.mlb.com'], 'givenName': ['Derek'], 'sn': ['Jeter']}
|
||||
assert session_info["issuer"] == IDP
|
||||
assert session_info["came_from"] == "http://foo.example.com/service"
|
||||
response = samlp.response_from_string(authn_response.xmlstr)
|
||||
assert response.destination == "http://local:8087/"
|
||||
|
||||
# One person in the cache
|
||||
assert len(self.client.users.subjects()) == 1
|
||||
subject_id = self.client.users.subjects()[0]
|
||||
print "||||", self.client.users.get_info_from(subject_id, IDP)
|
||||
# The information I have about the subject comes from one source
|
||||
assert self.client.users.issuers_of_info(subject_id) == [IDP]
|
||||
|
||||
# --- authenticate another person
|
||||
|
||||
ava = { "givenName": ["Alfonson"], "surname": ["Soriano"],
|
||||
"mail": ["alfonson@chc.mlb.com"]}
|
||||
|
||||
resp_str = "\n".join(self.server.authn_response(
|
||||
identity=ava,
|
||||
in_response_to="2",
|
||||
destination="http://local: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 = base64.encodestring(resp_str)
|
||||
|
||||
authn_response = self.client.response({"SAMLResponse":resp_str},
|
||||
"urn:mace:example.com:saml:roland:sp",
|
||||
{"2":"http://foo.example.com/service"})
|
||||
|
||||
# Two persons in the cache
|
||||
assert len(self.client.users.subjects()) == 2
|
||||
issuers = [self.client.users.issuers_of_info(s) for s in self.client.users.subjects()]
|
||||
# The information I have about the subjects comes from the same source
|
||||
print issuers
|
||||
assert issuers == [[IDP], [IDP]]
|
||||
|
||||
def test_init_values(self):
|
||||
print self.client.config["service"]["sp"]
|
||||
spentityid = self.client._spentityid()
|
||||
print spentityid
|
||||
assert spentityid == "urn:mace:example.com:saml:roland:sp"
|
||||
location = self.client._location()
|
||||
print location
|
||||
assert location == 'http://localhost:8088/sso/'
|
||||
service_url = self.client._service_url()
|
||||
print service_url
|
||||
assert service_url == "http://lingon.catalogix.se:8087/"
|
||||
my_name = self.client._my_name()
|
||||
print my_name
|
||||
assert my_name == "urn:mace:example.com:saml:roland:sp"
|
||||
|
||||
def test_authenticate(self):
|
||||
(sid, response) = self.client.authenticate(
|
||||
"http://www.example.com/sso",
|
||||
"http://www.example.org/service",
|
||||
"urn:mace:example.org:saml:sp",
|
||||
"My Name",
|
||||
"http://www.example.com/relay_state")
|
||||
assert sid != None
|
||||
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])
|
||||
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 != None
|
||||
assert request[0] == "Location"
|
||||
o = urlparse(request[1])
|
||||
qdict = parse_qs(o.query)
|
||||
assert _leq(qdict.keys(), ['SAMLRequest', 'RelayState'])
|
||||
saml_request = decode_base64_and_inflate(qdict["SAMLRequest"][0])
|
||||
assert qdict["RelayState"][0] == "http://www.example.com/relay_state"
|
||||
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 == "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
|
||||
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"
|
||||
issuer = authnreq.issuer
|
||||
assert issuer.text == "urn:mace:example.com:saml:roland:sp"
|
||||
|
||||
|
||||
# def test_logout_request(self):
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
#!/usr/bin/env python
|
||||
import os
|
||||
import getopt
|
||||
import xmldsig as ds
|
||||
|
||||
from saml2 import utils, md, samlp, BINDING_HTTP_POST, BINDING_HTTP_REDIRECT
|
||||
from saml2 import BINDING_SOAP, class_name, make_instance
|
||||
from saml2.time_util import in_a_while
|
||||
from saml2.utils import parse_attribute_map, args2dict
|
||||
from saml2.s_utils import parse_attribute_map, factory
|
||||
from saml2.saml import NAME_FORMAT_URI
|
||||
from saml2.sigver import pre_signature_part, SecurityContext
|
||||
from saml2.attribute_converter import from_local_name, ac_factory
|
||||
@@ -30,155 +32,248 @@ class Usage(Exception):
|
||||
DEFAULTS = {
|
||||
"want_assertions_signed": "true",
|
||||
"authn_requests_signed": "false",
|
||||
"want_authn_requests_signed": "true",
|
||||
}
|
||||
|
||||
ORG_ATTR_TRANSL = {
|
||||
"organization_name": "name",
|
||||
"organization_display_name": "display_name",
|
||||
"organization_url": "url",
|
||||
"organization_name": ("name", md.OrganizationName),
|
||||
"organization_display_name": ("display_name", md.OrganizationDisplayName),
|
||||
"organization_url": ("url", md.OrganizationURL)
|
||||
}
|
||||
|
||||
PERSON_ATTR_TRANSL = {
|
||||
"company": "company",
|
||||
"given_name": "givenname",
|
||||
"sur_name": "surname",
|
||||
"email_address": "mail",
|
||||
"telephone_number": "phone",
|
||||
"type": "type",
|
||||
}
|
||||
def _localized_name(val, klass):
|
||||
try:
|
||||
(text,lang) = val
|
||||
return klass(text=text,lang=lang)
|
||||
except ValueError:
|
||||
return klass(text=val)
|
||||
|
||||
def _localized_name(tup):
|
||||
if tup[1]:
|
||||
return args2dict(tup[0],lang=tup[1])
|
||||
else:
|
||||
return tup[0]
|
||||
def do_organization_info(conf):
|
||||
""" decription of an organization in the configuration is
|
||||
a dictionary of keys and values, where the values might be tuples.
|
||||
|
||||
def do_organization_info(conf, desc):
|
||||
""" """
|
||||
"organization": {
|
||||
"name": ("AB Exempel", "se"),
|
||||
"display_name": ("AB Exempel", "se"),
|
||||
"url": "http://www.example.org"
|
||||
}
|
||||
"""
|
||||
try:
|
||||
corg = conf["organization"]
|
||||
dorg = desc["organization"] = {}
|
||||
|
||||
for (dkey, ckey) in ORG_ATTR_TRANSL.items():
|
||||
org = md.Organization()
|
||||
for dkey, (ckey, klass) in ORG_ATTR_TRANSL.items():
|
||||
if ckey not in corg:
|
||||
continue
|
||||
if isinstance(corg[ckey], basestring):
|
||||
dorg[dkey] = [corg[ckey]]
|
||||
elif isinstance(corg[ckey], tuple):
|
||||
dorg[dkey] = [_localized_name(corg[ckey])]
|
||||
setattr(org, dkey, [_localized_name(corg[ckey], klass)])
|
||||
elif isinstance(corg[ckey], list):
|
||||
setattr(org, dkey, [_localized_name(n, klass) for n in corg[ckey]])
|
||||
else:
|
||||
dorg[dkey] = []
|
||||
for val in corg[ckey]:
|
||||
if isinstance(val,tuple):
|
||||
dorg[dkey].append(_localized_name(val))
|
||||
setattr(org, dkey, [_localized_name(corg[ckey], klass)])
|
||||
return org
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
def do_contact_person_info(conf):
|
||||
"""
|
||||
"""
|
||||
contact_person = md.ContactPerson
|
||||
cps = []
|
||||
try:
|
||||
for corg in conf["contact_person"]:
|
||||
cp = md.ContactPerson()
|
||||
for (key, classpec) in contact_person.c_children.values():
|
||||
try:
|
||||
value = corg[key]
|
||||
data = []
|
||||
if isinstance(classpec, list):
|
||||
# What if value is not a list ?
|
||||
if isinstance(value, basestring):
|
||||
data = [classpec[0](text=value)]
|
||||
else:
|
||||
for val in value:
|
||||
data.append(classpec[0](text=val))
|
||||
else:
|
||||
dorg[dkey].append(val)
|
||||
data = classpec(text=value)
|
||||
setattr(cp, key, data)
|
||||
except KeyError:
|
||||
pass
|
||||
for (prop, classpec, req) in contact_person.c_attributes.values():
|
||||
try:
|
||||
# should do a check for valid value
|
||||
setattr(cp, prop, corg[prop])
|
||||
except KeyError:
|
||||
pass
|
||||
cps.append(cp)
|
||||
except KeyError:
|
||||
pass
|
||||
return cps
|
||||
|
||||
def do_contact_person_info(conf, desc):
|
||||
if "contact_person" in conf:
|
||||
desc["contact_person"] = []
|
||||
for corg in conf["contact_person"]:
|
||||
dorg = {}
|
||||
for (dkey, ckey) in PERSON_ATTR_TRANSL.items():
|
||||
try:
|
||||
dorg[dkey] = corg[ckey]
|
||||
except:
|
||||
pass
|
||||
desc["contact_person"].append(dorg)
|
||||
def do_key_descriptor(cert):
|
||||
return md.KeyDescriptor(
|
||||
key_info=ds.KeyInfo(
|
||||
x509_data=ds.X509Data(
|
||||
x509_certificate=ds.X509Certificate(text=cert)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
def do_sp_sso_descriptor(sp, cert, acs):
|
||||
desc = {
|
||||
"protocol_support_enumeration": samlp.NAMESPACE,
|
||||
"assertion_consumer_service": {
|
||||
"binding": BINDING_HTTP_POST ,
|
||||
"location": sp["url"],
|
||||
"index": 0,
|
||||
},
|
||||
"key_descriptor":{
|
||||
"key_info": {
|
||||
"x509_data": {
|
||||
"x509_certificate": cert
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
def do_requested_attribute(attributes, acs, is_required="false"):
|
||||
lista = []
|
||||
for attr in attributes:
|
||||
attr = from_local_name(acs, attr, NAME_FORMAT_URI)
|
||||
args = {}
|
||||
for key in attr.keyswv():
|
||||
args[key] = getattr(attr,key)
|
||||
args["is_required"] = is_required
|
||||
lista.append(md.RequestedAttribute(**args))
|
||||
return lista
|
||||
|
||||
ENDPOINTS = {
|
||||
"sp": {
|
||||
"artifact_resolution_service": (md.ArtifactResolutionService, True),
|
||||
"single_logout_service": (md.SingleLogoutService, False),
|
||||
"manage_name_id_service": (md.ManageNameIDService, False),
|
||||
"assertion_consumer_service": (md.AssertionConsumerService, True),
|
||||
},
|
||||
"idp":{
|
||||
"artifact_resolution_service": (md.ArtifactResolutionService, True),
|
||||
"single_logout_service": (md.SingleLogoutService, False),
|
||||
"manage_name_id_service": (md.ManageNameIDService, False),
|
||||
|
||||
"single_sign_on_service": (md.SingleSignOnService, False),
|
||||
"name_id_mapping_service": (md.NameIDMappingService, False),
|
||||
|
||||
"assertion_id_request_service": (md.AssertionIDRequestService, False),
|
||||
},
|
||||
"aa":{
|
||||
"artifact_resolution_service": (md.ArtifactResolutionService, True),
|
||||
"single_logout_service": (md.SingleLogoutService, False),
|
||||
"manage_name_id_service": (md.ManageNameIDService, False),
|
||||
|
||||
"assertion_id_request_service": (md.AssertionIDRequestService, False),
|
||||
|
||||
"attribute_service": (md.AttributeService, False)
|
||||
},
|
||||
}
|
||||
|
||||
DEFAULT_BINDING = {
|
||||
"assertion_consumer_service": BINDING_HTTP_POST,
|
||||
"single_sign_on_service": BINDING_HTTP_POST,
|
||||
"single_logout_service": BINDING_HTTP_POST,
|
||||
"attribute_service": BINDING_SOAP,
|
||||
"artifact_resolution_service": BINDING_SOAP
|
||||
}
|
||||
|
||||
def do_endpoints(conf, endpoints):
|
||||
service = {}
|
||||
|
||||
for endpoint, (eclass, indexed) in endpoints.items():
|
||||
try:
|
||||
servs = []
|
||||
i = 1
|
||||
for args in conf[endpoint]:
|
||||
if isinstance(args, basestring): # Assume it's the location
|
||||
args = {"location":args, "binding": DEFAULT_BINDING[endpoint]}
|
||||
if indexed:
|
||||
args["index"] = "%d" % i
|
||||
servs.append(factory(eclass, **args))
|
||||
i += 1
|
||||
service[endpoint] = servs
|
||||
except KeyError:
|
||||
pass
|
||||
return service
|
||||
|
||||
def do_sp_sso_descriptor(sp, acs, cert=None):
|
||||
spsso = md.SPSSODescriptor()
|
||||
spsso.protocol_support_enumeration=samlp.NAMESPACE
|
||||
|
||||
if sp["endpoints"]:
|
||||
for (endpoint, instlist) in do_endpoints(sp["endpoints"],
|
||||
ENDPOINTS["sp"]).items():
|
||||
setattr(spsso, endpoint, instlist)
|
||||
|
||||
if cert:
|
||||
spsso.key_descriptor=do_key_descriptor(cert)
|
||||
|
||||
for key in ["want_assertions_signed", "authn_requests_signed"]:
|
||||
try:
|
||||
desc[key] = "%s" % sp[key]
|
||||
setattr(spsso, key, "%s" % sp[key])
|
||||
except KeyError:
|
||||
desc[key] = DEFAULTS[key]
|
||||
setattr(spsso, key, DEFAULTS[key])
|
||||
|
||||
requested_attribute = []
|
||||
requested_attributes = []
|
||||
if "required_attributes" in sp:
|
||||
for attr in sp["required_attributes"]:
|
||||
reqa = from_local_name(acs, attr, NAME_FORMAT_URI)
|
||||
reqa["is_required"] = "true"
|
||||
requested_attribute.append(reqa)
|
||||
requested_attributes.extend(do_requested_attribute(
|
||||
sp["required_attributes"],
|
||||
acs,
|
||||
is_required="true"))
|
||||
|
||||
if "optional_attributes" in sp:
|
||||
for attr in sp["optional_attributes"]:
|
||||
reqa = from_local_name(acs, attr, NAME_FORMAT_URI)
|
||||
requested_attribute.append(reqa)
|
||||
requested_attributes.extend(do_requested_attribute(
|
||||
sp["optional_attributes"],
|
||||
acs,
|
||||
is_required="false"))
|
||||
|
||||
if requested_attribute:
|
||||
desc["attribute_consuming_service"] = {
|
||||
"requested_attribute": requested_attribute,
|
||||
"service_name": {
|
||||
"lang":"en",
|
||||
"text":sp["name"],
|
||||
}
|
||||
}
|
||||
if requested_attributes:
|
||||
spsso.attribute_consuming_service = [md.AttributeConsumingService(
|
||||
requested_attribute=requested_attributes,
|
||||
service_name= [md.ServiceName(lang="en",text=sp["name"])]
|
||||
)]
|
||||
try:
|
||||
spsso.attribute_consuming_service[0].service_description = [
|
||||
md.ServiceDescription(text=sp["description"])]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
if "discovery_service" in sp:
|
||||
desc["extensions"] = {"extension_elements":[
|
||||
{
|
||||
"tag":"DiscoveryResponse",
|
||||
"namespace":md.IDPDISC,
|
||||
"attributes": {
|
||||
"index":"1",
|
||||
"binding": md.IDPDISC,
|
||||
"location":sp["url"]
|
||||
}
|
||||
}
|
||||
]}
|
||||
# if "discovery_service" in sp:
|
||||
# spsso.extensions= {"extension_elements":[
|
||||
# {
|
||||
# "tag":"DiscoveryResponse",
|
||||
# "namespace":md.IDPDISC,
|
||||
# "attributes": {
|
||||
# "index":"1",
|
||||
# "binding": md.IDPDISC,
|
||||
# "location":sp["url"]
|
||||
# }
|
||||
# }
|
||||
# ]}
|
||||
|
||||
return desc
|
||||
return spsso
|
||||
|
||||
def do_idp_sso_descriptor(idp, cert):
|
||||
return {
|
||||
"protocol_support_enumeration": samlp.NAMESPACE,
|
||||
"want_authn_requests_signed": True,
|
||||
"single_sign_on_service": {
|
||||
"binding": BINDING_HTTP_REDIRECT ,
|
||||
"location": idp["url"],
|
||||
},
|
||||
"key_descriptor":{
|
||||
"key_info": {
|
||||
"x509_data": {
|
||||
"x509_certificate": cert
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
def do_idp_sso_descriptor(idp, cert=None):
|
||||
idpsso = md.IDPSSODescriptor()
|
||||
idpsso.protocol_support_enumeration=samlp.NAMESPACE
|
||||
|
||||
if idp["endpoints"]:
|
||||
for (endpoint, instlist) in do_endpoints(idp["endpoints"],
|
||||
ENDPOINTS["idp"]).items():
|
||||
setattr(idpsso, endpoint, instlist)
|
||||
|
||||
if cert:
|
||||
idpsso.key_descriptor=do_key_descriptor(cert)
|
||||
|
||||
for key in ["want_authn_requests_signed"]:
|
||||
try:
|
||||
setattr(idpsso, key, "%s" % idp[key])
|
||||
except KeyError:
|
||||
setattr(idpsso, key, DEFAULTS[key])
|
||||
|
||||
return idpsso
|
||||
|
||||
def do_aa_descriptor(aa, cert):
|
||||
return {
|
||||
"protocol_support_enumeration": samlp.NAMESPACE,
|
||||
"attribute_service": {
|
||||
"binding": BINDING_SOAP ,
|
||||
"location": aa["url"],
|
||||
},
|
||||
"key_descriptor":{
|
||||
"key_info": {
|
||||
"x509_data": {
|
||||
"x509_certificate": cert
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
aa = md.AttributeAuthorityDescriptor()
|
||||
aa.protocol_support_enumeration=samlp.NAMESPACE
|
||||
|
||||
if idp["endpoints"]:
|
||||
for (endpoint, instlist) in do_endpoints(aa["endpoints"],
|
||||
ENDPOINTS["aa"]).items():
|
||||
setattr(aasso, endpoint, instlist)
|
||||
|
||||
if cert:
|
||||
aa.key_descriptor=do_key_descriptor(cert)
|
||||
|
||||
return aa
|
||||
|
||||
def entity_descriptor(confd, valid_for):
|
||||
mycert = "".join(open(confd["cert_file"]).readlines()[1:-1])
|
||||
@@ -193,45 +288,43 @@ def entity_descriptor(confd, valid_for):
|
||||
#else:
|
||||
# backward = {}
|
||||
|
||||
ed = {
|
||||
"entity_id": confd["entityid"],
|
||||
}
|
||||
if valid_for:
|
||||
ed["valid_until"] = in_a_while(hours=valid_for)
|
||||
ed = md.EntityDescriptor(entity_id=confd["entityid"])
|
||||
|
||||
do_organization_info(confd, ed)
|
||||
do_contact_person_info(confd, ed)
|
||||
if valid_for:
|
||||
ed.valid_until = in_a_while(hours=valid_for)
|
||||
|
||||
ed.organization = do_organization_info(confd)
|
||||
ed.contact_person = do_contact_person_info(confd)
|
||||
|
||||
if "sp" in confd["service"]:
|
||||
# The SP
|
||||
ed["sp_sso_descriptor"] = do_sp_sso_descriptor(confd["service"]["sp"],
|
||||
mycert, attrconverters)
|
||||
ed.sp_sso_descriptor = do_sp_sso_descriptor(confd["service"]["sp"],
|
||||
attrconverters, mycert)
|
||||
if "idp" in confd["service"]:
|
||||
ed["idp_sso_descriptor"] = do_idp_sso_descriptor(
|
||||
ed.idp_sso_descriptor = do_idp_sso_descriptor(
|
||||
confd["service"]["idp"], mycert)
|
||||
if "aa" in confd["service"]:
|
||||
ed["attribute_authority_descriptor"] = do_aa_descriptor(
|
||||
ed.attribute_authority_descriptor = do_aa_descriptor(
|
||||
confd["service"]["aa"], mycert)
|
||||
|
||||
return ed
|
||||
|
||||
def entities_descriptor(eds, valid_for, name, id, sign, sc):
|
||||
d = {"entity_descriptor": eds}
|
||||
entities = md.EntitiesDescriptor(entity_descriptor= eds)
|
||||
if valid_for:
|
||||
d["valid_until"] = in_a_while(hours=valid_for)
|
||||
entities.valid_until = in_a_while(hours=valid_for)
|
||||
if name:
|
||||
d["name"] = name
|
||||
entities.name = name
|
||||
if id:
|
||||
d["id"] = id
|
||||
entities.id = id
|
||||
|
||||
if sign:
|
||||
d["signature"] = pre_signature_part(d["id"])
|
||||
entities.signature = pre_signature_part(id)
|
||||
|
||||
statement = make_instance(md.EntitiesDescriptor, d)
|
||||
if sign:
|
||||
statement = sc.sign_statement_using_xmlsec("%s" % statement,
|
||||
class_name(statement))
|
||||
return statement
|
||||
entities = sc.sign_statement_using_xmlsec("%s" % entities,
|
||||
class_name(entities))
|
||||
return entities
|
||||
|
||||
|
||||
def main(args):
|
||||
|
||||
Reference in New Issue
Block a user