diff --git a/src/saml2/mdie.py b/src/saml2/mdie.py index deca0cc..645700b 100644 --- a/src/saml2/mdie.py +++ b/src/saml2/mdie.py @@ -79,6 +79,8 @@ def to_dict(_dict, onts, mdb_safe=False): for key, val in _dict.items(): _val = _eval(val, onts, mdb_safe) if _val: + if mdb_safe and "." in key: + key = key.replace(".", "__") res[key] = _val return res diff --git a/src/saml2/mdstore.py b/src/saml2/mdstore.py index 92a1d1a..ac0058a 100644 --- a/src/saml2/mdstore.py +++ b/src/saml2/mdstore.py @@ -103,6 +103,9 @@ class MetaData(object): def keys(self): return self.entity.keys() + def values(self): + return self.entity.keys() + def __contains__(self, item): return item in self.entity diff --git a/src/saml2/mongo_store.py b/src/saml2/mongo_store.py index ce76d34..7568df9 100644 --- a/src/saml2/mongo_store.py +++ b/src/saml2/mongo_store.py @@ -246,6 +246,9 @@ class MDB(object): else: return True + def reset(self): + self.db.drop() + #------------------------------------------------------------------------------ class EptidMDB(Eptid): @@ -268,12 +271,60 @@ class EptidMDB(Eptid): #------------------------------------------------------------------------------ -def export_mdstore_to_mongo_db(mds, onts, collection, sub_collection=""): + +def protect(dic): + res = {} + for key, val in dic.items(): + key = key.replace(".", "__") + if isinstance(val, basestring): + pass + elif isinstance(val, dict): + val = protect(val) + elif isinstance(val, list): + li = [] + for va in val: + if isinstance(va, basestring): + pass + elif isinstance(va, dict): + va = protect(va) + # I don't think lists of lists will appear am I wrong ? + li.append(va) + val = li + res[key] = val + return res + + +def unprotect(dic): + res = {} + for key, val in dic.items(): + if key == "__class__": + pass + else: + key = key.replace("__", ".") + if isinstance(val, basestring): + pass + elif isinstance(val, dict): + val = unprotect(val) + elif isinstance(val, list): + li = [] + for va in val: + if isinstance(va, basestring): + pass + elif isinstance(val, dict): + va = unprotect(va) + li.append(va) + val = li + res[key] = val + return res + + +def export_mdstore_to_mongo_db(mds, collection, sub_collection=""): mdb = MDB(collection, sub_collection) + mdb.reset() mdb.primary_key = "entity_id" for key, desc in mds.items(): kwargs = { - "entity_description": to_dict(desc, onts.values(), True), + "entity_description": protect(desc), } mdb.store(key, **kwargs) @@ -284,20 +335,6 @@ class MetadataMDB(MetaData): self.mdb = MDB(collection, sub_collection) self.mdb.primary_key = "entity_id" - def _service(self, entity_id, typ, service, binding=None): - """ Get me all services with a specified - entity ID and type, that supports the specified version of binding. - - - :param entity_id: The EntityId - :param typ: Type of service (idp, attribute_authority, ...) - :param service: which service that is sought for - :param binding: A binding identifier - :return: list of service descriptions. - Or if no binding was specified a list of 2-tuples (binding, srv) - """ - pass - def _ext_service(self, entity_id, typ, service, binding): try: srvs = self[entity_id][typ] @@ -322,11 +359,15 @@ class MetadataMDB(MetaData): def items(self): for key, item in self.mdb.items(): - yield key, from_dict(item["entity_description"], self.onts, True) + yield key, unprotect(item["entity_description"]) def keys(self): return self.mdb.keys() + def values(self): + for key, item in self.mdb.items(): + yield unprotect(item["entity_description"]) + def __contains__(self, item): return item in self.mdb @@ -335,7 +376,7 @@ class MetadataMDB(MetaData): if not res: raise KeyError(item) elif len(res) == 1: - return from_dict(res[0]["entity_description"], self.onts, True) + return unprotect(res[0]["entity_description"]) else: raise CorruptDatabase("More then one document with key %s" % item) diff --git a/tests/test_75_mongodb.py b/tests/test_75_mongodb.py index a3e2a10..9c4dd82 100644 --- a/tests/test_75_mongodb.py +++ b/tests/test_75_mongodb.py @@ -1,4 +1,5 @@ from saml2 import BINDING_HTTP_POST +from saml2.mdstore import MetadataStore from saml2.saml import AUTHN_PASSWORD from saml2.client import Saml2Client from saml2.server import Server @@ -72,5 +73,7 @@ def test_eptid_mongo_db(): assert e4 != e1 assert e4 != e3 + + if __name__ == "__main__": test_flow() diff --git a/tests/test_76_metadata_in_mdb.py b/tests/test_76_metadata_in_mdb.py new file mode 100644 index 0000000..9be2f7c --- /dev/null +++ b/tests/test_76_metadata_in_mdb.py @@ -0,0 +1,82 @@ +# -*- coding: utf-8 -*- +from saml2.mongo_store import export_mdstore_to_mongo_db, MetadataMDB +from saml2.mdstore import MetadataStore, destinations, name + +__author__ = 'rolandh' + +from saml2.attribute_converter import d_to_local_name, ac_factory + +from saml2 import saml +from saml2 import md + +from saml2.extension import mdui +from saml2.extension import idpdisc +from saml2.extension import dri +from saml2.extension import mdattr +from saml2.extension import ui +import xmldsig +import xmlenc + +from pathutils import full_path, xmlsec_path + +ONTS = { + saml.NAMESPACE: saml, + mdui.NAMESPACE: mdui, + mdattr.NAMESPACE: mdattr, + dri.NAMESPACE: dri, + ui.NAMESPACE: ui, + idpdisc.NAMESPACE: idpdisc, + md.NAMESPACE: md, + xmldsig.NAMESPACE: xmldsig, + xmlenc.NAMESPACE: xmlenc +} + +ATTRCONV = ac_factory(full_path("attributemaps")) + + +def _eq(l1, l2): + return set(l1) == set(l2) + + +def test_metadata(): + UMU_IDP = 'https://idp.umu.se/saml2/idp/metadata.php' + mds = MetadataStore(ONTS.values(), ATTRCONV, xmlsec_path, + disable_ssl_certificate_validation=True) + + mds.imp({"local": [full_path("swamid-1.0.xml")]}) + assert len(mds) == 1 # One source + + export_mdstore_to_mongo_db(mds, "metadata", "test") + + mdmdb = MetadataMDB(ONTS, ATTRCONV, "metadata", "test") + # replace all metadata instances with this one + mds.metadata = {"mongo_db": mdmdb} + + idps = mds.with_descriptor("idpsso") + assert idps.keys() + idpsso = mds.single_sign_on_service(UMU_IDP) + assert len(idpsso) == 1 + assert destinations(idpsso) == [ + 'https://idp.umu.se/saml2/idp/SSOService.php'] + + _name = name(mds[UMU_IDP]) + assert _name == u'UmeƄ University (SAML2)' + certs = mds.certs(UMU_IDP, "idpsso", "signing") + assert len(certs) == 1 + + sps = mds.with_descriptor("spsso") + assert len(sps) == 108 + + wants = mds.attribute_requirement('https://connect8.sunet.se/shibboleth') + lnamn = [d_to_local_name(mds.attrc, attr) for attr in wants["optional"]] + assert _eq(lnamn, ['eduPersonPrincipalName', 'mail', 'givenName', 'sn', + 'eduPersonScopedAffiliation']) + + wants = mds.attribute_requirement('https://beta.lobber.se/shibboleth') + assert wants["required"] == [] + lnamn = [d_to_local_name(mds.attrc, attr) for attr in wants["optional"]] + assert _eq(lnamn, ['eduPersonPrincipalName', 'mail', 'givenName', 'sn', + 'eduPersonScopedAffiliation', 'eduPersonEntitlement']) + +if __name__ == "__main__": + test_metadata() \ No newline at end of file