Merge remote-tracking branch 'upstream/master'
This commit is contained in:
@@ -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,
|
||||
|
@@ -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,
|
||||
|
@@ -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 """
|
||||
|
@@ -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:
|
||||
|
@@ -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.
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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:
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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]
|
||||
|
@@ -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"/>
|
||||
|
@@ -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
70292
tests/swamid-2.0.xml
Normal file
File diff suppressed because one or more lines are too long
@@ -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',
|
||||
|
@@ -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({
|
||||
|
@@ -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"] == []
|
||||
|
@@ -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()
|
||||
|
Reference in New Issue
Block a user