Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Hans Hörberg
2014-02-28 08:30:27 +01:00
17 changed files with 70400 additions and 53 deletions

View File

@@ -844,6 +844,14 @@ def extension_elements_to_elements(extension_elements, schemas):
according to the schemas.
"""
res = []
if isinstance(schemas, list):
pass
elif isinstance(schemas, dict):
schemas = schemas.values()
else:
return res
for extension_element in extension_elements:
for schema in schemas:
inst = extension_element_to_element(extension_element,

View File

@@ -117,7 +117,8 @@ class Saml2Client(Base):
entity_ids = self.users.issuers_of_info(name_id)
return self.do_logout(name_id, entity_ids, reason, expire, sign)
def do_logout(self, name_id, entity_ids, reason, expire, sign=None):
def do_logout(self, name_id, entity_ids, reason, expire, sign=None,
expected_binding=None):
"""
:param name_id: Identifier of the Subject (a NameID instance)
@@ -126,6 +127,8 @@ class Saml2Client(Base):
:param reason: The reason for doing the logout
:param expire: Try to logout before this time.
:param sign: Whether to sign the request or not
:param expected_binding: Specify the expected binding then not try it
all
:return:
"""
# check time
@@ -142,6 +145,8 @@ class Saml2Client(Base):
# for all where I can use the SOAP binding, do those first
for binding in [BINDING_SOAP, BINDING_HTTP_POST,
BINDING_HTTP_REDIRECT]:
if expected_binding and binding != expected_binding:
continue
try:
srvs = self.metadata.single_logout_service(entity_id,
binding,

View File

@@ -2,6 +2,7 @@
__author__ = 'rolandh'
import copy
import sys
import os
import re
@@ -48,7 +49,7 @@ ONTS = {
COMMON_ARGS = [
"entityid", "xmlsec_binary", "debug", "key_file", "cert_file",
"secret", "accepted_time_diff", "name", "ca_certs",
"encryption_type", "secret", "accepted_time_diff", "name", "ca_certs",
"description", "valid_for", "verify_ssl_cert",
"organization",
"contact_person",
@@ -175,6 +176,7 @@ class Config(object):
self.debug = False
self.key_file = None
self.cert_file = None
self.encryption_type = 'both'
self.secret = None
self.accepted_time_diff = None
self.name = None
@@ -349,7 +351,7 @@ class Config(object):
mod = self._load(config_file)
#return self.load(eval(open(config_file).read()))
return self.load(mod.CONFIG, metadata_construction)
return self.load(copy.deepcopy(mod.CONFIG), metadata_construction)
def load_metadata(self, metadata_conf):
""" Loads metadata into an internal structure """

View File

@@ -291,7 +291,8 @@ class Entity(HTTPBase):
def unravel(self, txt, binding, msgtype="response"):
#logger.debug("unravel '%s'" % txt)
if binding not in [BINDING_HTTP_REDIRECT, BINDING_HTTP_POST,
BINDING_SOAP, BINDING_URI, None]:
BINDING_SOAP, BINDING_URI, BINDING_HTTP_ARTIFACT,
None]:
raise ValueError("Don't know how to handle '%s'" % binding)
else:
try:
@@ -302,6 +303,8 @@ class Entity(HTTPBase):
elif binding == BINDING_SOAP:
func = getattr(soap, "parse_soap_enveloped_saml_%s" % msgtype)
xmlstr = func(txt)
elif binding == BINDING_HTTP_ARTIFACT:
xmlstr = base64.b64decode(txt)
else:
xmlstr = txt
except Exception:

View File

@@ -120,6 +120,9 @@ class IdentDB(object):
_id = "%s@%s" % (_id, self.domain)
if nformat == NAMEID_FORMAT_PERSISTENT:
_id = userid
nameid = NameID(format=nformat, sp_name_qualifier=sp_name_qualifier,
name_qualifier=name_qualifier, text=_id)
@@ -281,7 +284,7 @@ class IdentDB(object):
# else create and return a new one
return self.construct_nameid(_id, name_id_policy=name_id_policy)
def handle_manage_name_id_request(self, name_id, new_id="",
def handle_manage_name_id_request(self, name_id, new_id=None,
new_encrypted_id="", terminate=""):
"""
Requests from the SP is about the SPProvidedID attribute.

View File

@@ -103,12 +103,13 @@ def repack_cert(cert):
class MetaData(object):
def __init__(self, onts, attrc, metadata=""):
def __init__(self, onts, attrc, metadata="", node_name=None, **kwargs):
self.onts = onts
self.attrc = attrc
self.entity = {}
self.metadata = metadata
self.security = None
self.node_name = node_name
def items(self):
return self.entity.items()
@@ -371,8 +372,8 @@ class MetaDataFile(MetaData):
Handles Metadata file on the same machine. The format of the file is
the SAML Metadata format.
"""
def __init__(self, onts, attrc, filename, cert=None):
MetaData.__init__(self, onts, attrc)
def __init__(self, onts, attrc, filename, cert=None, **kwargs):
MetaData.__init__(self, onts, attrc, **kwargs)
self.filename = filename
self.cert = cert
@@ -382,7 +383,8 @@ class MetaDataFile(MetaData):
def load(self):
_txt = self.get_metadata_content()
if self.cert:
node_name = "%s:%s" % (md.EntitiesDescriptor.c_namespace,
node_name = self.node_name \
or "%s:%s" % (md.EntitiesDescriptor.c_namespace,
md.EntitiesDescriptor.c_tag)
if self.security.verify_signature(_txt,
@@ -400,8 +402,8 @@ class MetaDataLoader(MetaDataFile):
Handles Metadata file loaded by a passed in function.
The format of the file is the SAML Metadata format.
"""
def __init__(self, onts, attrc, loader_callable, cert=None):
MetaData.__init__(self, onts, attrc)
def __init__(self, onts, attrc, loader_callable, cert=None, **kwargs):
MetaData.__init__(self, onts, attrc, **kwargs)
self.metadata_provider_callable = self.get_metadata_loader(loader_callable)
self.cert = cert
@@ -444,7 +446,7 @@ class MetaDataExtern(MetaData):
Accessible but HTTP GET.
"""
def __init__(self, onts, attrc, url, security, cert, http):
def __init__(self, onts, attrc, url, security, cert, http, **kwargs):
"""
:params onts:
:params attrc:
@@ -453,7 +455,7 @@ class MetaDataExtern(MetaData):
:params cert:
:params http:
"""
MetaData.__init__(self, onts, attrc)
MetaData.__init__(self, onts, attrc, **kwargs)
self.url = url
self.security = security
self.cert = cert
@@ -466,7 +468,8 @@ class MetaDataExtern(MetaData):
"""
response = self.http.send(self.url)
if response.status_code == 200:
node_name = "%s:%s" % (md.EntitiesDescriptor.c_namespace,
node_name = self.node_name \
or "%s:%s" % (md.EntitiesDescriptor.c_namespace,
md.EntitiesDescriptor.c_tag)
_txt = response.text.encode("utf-8")
@@ -480,7 +483,7 @@ class MetaDataExtern(MetaData):
self.parse(_txt)
return True
else:
logger.info("Response status: %s" % response.status)
logger.info("Response status: %s" % response.status_code)
return False
@@ -489,8 +492,8 @@ class MetaDataMD(MetaData):
Handles locally stored metadata, the file format is the text representation
of the Python representation of the metadata.
"""
def __init__(self, onts, attrc, filename):
MetaData.__init__(self, onts, attrc)
def __init__(self, onts, attrc, filename, **kwargs):
MetaData.__init__(self, onts, attrc, **kwargs)
self.filename = filename
def load(self):
@@ -523,12 +526,13 @@ class MetadataStore(object):
elif typ == "inline":
self.ii += 1
key = self.ii
md = MetaData(self.onts, self.attrc, args[0])
md = MetaData(self.onts, self.attrc, args[0], **kwargs)
elif typ == "remote":
key = kwargs["url"]
md = MetaDataExtern(self.onts, self.attrc,
kwargs["url"], self.security,
kwargs["cert"], self.http)
kwargs["cert"], self.http,
node_name=kwargs.get('node_name'))
elif typ == "mdfile":
key = args[0]
md = MetaDataMD(self.onts, self.attrc, args[0])
@@ -804,7 +808,7 @@ class MetadataStore(object):
res = []
for md in self.metadata.values():
for ent_id, ent_desc in md.items():
if "spsso_descriptor" in ent_desc:
if descriptor in ent_desc:
res.append(ent_id)
return res

View File

@@ -197,7 +197,7 @@ def do_key_descriptor(cert, use="both"):
)
]
elif use in ["signing", "encryption"]:
md.KeyDescriptor(
return md.KeyDescriptor(
key_info=ds.KeyInfo(
x509_data=ds.X509Data(
x509_certificate=ds.X509Certificate(text=cert)
@@ -429,7 +429,8 @@ def do_spsso_descriptor(conf, cert=None):
spsso.extensions.add_extension_element(val)
if cert:
spsso.key_descriptor = do_key_descriptor(cert, "both")
encryption_type = conf.encryption_type
spsso.key_descriptor = do_key_descriptor(cert, encryption_type)
for key in ["want_assertions_signed", "authn_requests_signed"]:
try:

View File

@@ -153,7 +153,8 @@ class IdentMDB(IdentDB):
self.mdb.store(ident, name_id=to_dict(name_id, ONTS.values(), True))
def find_nameid(self, userid, nformat=None, sp_name_qualifier=None,
name_qualifier=None, sp_provided_id=None):
name_qualifier=None, sp_provided_id=None, **kwargs):
# reset passed for compatibility kwargs for next usage
kwargs = {}
if nformat:
kwargs["name_format"] = nformat

View File

@@ -762,6 +762,10 @@ class AuthnResponse(StatusResponse):
return self._assertion(assertion)
def parse_assertion(self):
if self.context == "AuthnQuery":
# can contain one or more assertions
pass
else: # This is a saml2int limitation
try:
assert len(self.response.assertion) == 1 or \
len(self.response.encrypted_assertion) == 1
@@ -770,11 +774,16 @@ class AuthnResponse(StatusResponse):
if self.response.assertion:
logger.debug("***Unencrypted response***")
return self._assertion(self.response.assertion[0])
for assertion in self.response.assertion:
if not self._assertion(assertion):
return False
return True
else:
logger.debug("***Encrypted response***")
return self._encrypted_assertion(
self.response.encrypted_assertion[0])
for assertion in self.response.encrypted_assertion:
if not self._encrypted_assertion(assertion):
return False
return True
def verify(self):
""" Verify that the assertion is syntactically correct and
@@ -883,7 +892,7 @@ class AuthnQueryResponse(AuthnResponse):
self.entity_id = entity_id
self.attribute_converters = attribute_converters
self.assertion = None
self.context = "AuthnQueryResponse"
self.context = "AuthnQuery"
def condition_ok(self, lax=False): # Should I care about conditions ?
return True

View File

@@ -151,6 +151,10 @@ class Server(Entity):
raise Exception("Couldn't open identity database: %s" %
(dbspec,))
_domain = self.config.getattr("domain", "idp")
if _domain:
self.ident.domain = _domain
self.ident.name_qualifier = self.config.entityid
dbspec = self.config.getattr("edu_person_targeted_id", "idp")
@@ -465,7 +469,14 @@ class Server(Entity):
if not snq:
snq = sp_entity_id
_nids = self.ident.find_nameid(userid, sp_name_qualifier=snq)
kwa = {"sp_name_qualifier": snq}
try:
kwa["format"] = name_id_policy.format
except AttributeError:
pass
_nids = self.ident.find_nameid(userid, **kwa)
# either none or one
if _nids:
name_id = _nids[0]

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?><EntitiesDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:shibmd="urn:mace:shibboleth:metadata:1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ID="INC20140204T195141" Name="urn:mace:incommon" validUntil="2014-02-18T10:00:00Z" xsi:schemaLocation="urn:oasis:names:tc:SAML:2.0:metadata sstc-saml-schema-metadata-2.0.xsd urn:mace:shibboleth:metadata:1.0 shibboleth-metadata-1.0.xsd http://www.w3.org/2000/09/xmldsig# xmldsig-core-schema.xsd"><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<?xml version="1.0" encoding="UTF-8"?><EntitiesDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:shibmd="urn:mace:shibboleth:metadata:1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ID="INC20140204T195141" Name="urn:mace:incommon" validUntil="2020-02-18T10:00:00Z" xsi:schemaLocation="urn:oasis:names:tc:SAML:2.0:metadata sstc-saml-schema-metadata-2.0.xsd urn:mace:shibboleth:metadata:1.0 shibboleth-metadata-1.0.xsd http://www.w3.org/2000/09/xmldsig# xmldsig-core-schema.xsd"><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?><EntitiesDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:mdrpi="urn:oasis:names:tc:SAML:metadata:rpi" xmlns:mdui="urn:oasis:names:tc:SAML:metadata:ui" xmlns:shibmd="urn:mace:shibboleth:metadata:1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ID="AAITest-20140205105921" Name="urn:mace:switch.ch:aaitest" validUntil="2014-02-10T09:59:21Z" xsi:schemaLocation="urn:oasis:names:tc:SAML:2.0:metadata saml-schema-metadata-2.0.xsd urn:mace:shibboleth:metadata:1.0 shibboleth-metadata-1.0.xsd http://www.w3.org/2000/09/xmldsig# xmldsig-core-schema.xsd"><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<?xml version="1.0" encoding="UTF-8"?><EntitiesDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:mdrpi="urn:oasis:names:tc:SAML:metadata:rpi" xmlns:mdui="urn:oasis:names:tc:SAML:metadata:ui" xmlns:shibmd="urn:mace:shibboleth:metadata:1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ID="AAITest-20140205105921" Name="urn:mace:switch.ch:aaitest" validUntil="2020-02-10T09:59:21Z" xsi:schemaLocation="urn:oasis:names:tc:SAML:2.0:metadata saml-schema-metadata-2.0.xsd urn:mace:shibboleth:metadata:1.0 shibboleth-metadata-1.0.xsd http://www.w3.org/2000/09/xmldsig# xmldsig-core-schema.xsd"><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>

70292
tests/swamid-2.0.xml Normal file

File diff suppressed because one or more lines are too long

View File

@@ -39,9 +39,11 @@ class TestAC():
for ac in self.acs:
try:
ava = ac.fro(ats)
break
except attribute_converter.UnknownNameFormat:
pass
# break if we have something
if ava:
break
print ava.keys()
assert _eq(ava.keys(), ['givenName', 'displayName', 'uid',
'eduPersonNickname', 'street',

View File

@@ -96,6 +96,7 @@ class TestIdentifier():
assert _eq(nameid.keyswv(), ['text', 'format', 'sp_name_qualifier',
'name_qualifier'])
assert nameid.format == NAMEID_FORMAT_TRANSIENT
assert nameid.text != "foobar"
def test_vo_1(self):
policy = Policy({
@@ -119,7 +120,8 @@ class TestIdentifier():
'name_qualifier'])
assert nameid.sp_name_qualifier == 'http://vo.example.org/biomed'
assert nameid.format == NAMEID_FORMAT_PERSISTENT
assert nameid.text != "foobar"
# we want to keep the user identifier in the nameid node
assert nameid.text == "foobar"
def test_vo_2(self):
policy = Policy({

View File

@@ -74,7 +74,7 @@ def test_metadata():
assert len(certs) == 1
sps = mds.with_descriptor("spsso")
assert len(sps) == 418
assert len(sps) == 417
wants = mds.attribute_requirement('https://connect.sunet.se/shibboleth')
assert wants["optional"] == []

View File

@@ -52,6 +52,7 @@ MDIMPORT = {
}
def main():
item = MDIMPORT[sys.argv[1]]
metad = None
@@ -66,3 +67,6 @@ if metad:
metad.load()
print metad.dumps()
if __name__ == '__main__':
main()