summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/saml2/config.py82
-rw-r--r--src/saml2/eptid.py50
-rw-r--r--src/saml2/mongo_store.py123
-rw-r--r--src/saml2/server.py18
4 files changed, 229 insertions, 44 deletions
diff --git a/src/saml2/config.py b/src/saml2/config.py
index ec28d397..30c44cbb 100644
--- a/src/saml2/config.py
+++ b/src/saml2/config.py
@@ -91,7 +91,8 @@ AA_IDP_ARGS = [
"ui_info",
"name_id_format",
"domain",
- "name_qualifier"
+ "name_qualifier",
+ "edu_person_targeted_id",
]
PDP_ARGS = ["endpoints", "name_form", "name_id_format"]
@@ -159,30 +160,30 @@ class Config(object):
def __init__(self, homedir="."):
self._homedir = homedir
self.entityid = None
- self.xmlsec_binary= None
- self.debug=False
- self.key_file=None
- self.cert_file=None
- self.secret=None
- self.accepted_time_diff=None
- self.name=None
- self.ca_certs=None
+ self.xmlsec_binary = None
+ self.debug = False
+ self.key_file = None
+ self.cert_file = None
+ self.secret = None
+ self.accepted_time_diff = None
+ self.name = None
+ self.ca_certs = None
self.verify_ssl_cert = False
- self.description=None
- self.valid_for=None
- self.organization=None
- self.contact_person=None
- self.name_form=None
- self.nameid_form=None
- self.virtual_organization=None
- self.logger=None
- self.only_use_keys_in_metadata=True
- self.logout_requests_signed=None
- self.disable_ssl_certificate_validation=None
+ self.description = None
+ self.valid_for = None
+ self.organization = None
+ self.contact_person = None
+ self.name_form = None
+ self.nameid_form = None
+ self.virtual_organization = None
+ self.logger = None
+ self.only_use_keys_in_metadata = True
+ self.logout_requests_signed = None
+ self.disable_ssl_certificate_validation = None
self.context = ""
- self.attribute_converters=None
- self.metadata=None
- self.policy=None
+ self.attribute_converters = None
+ self.metadata = None
+ self.policy = None
self.serves = []
self.vorg = {}
self.preferred_binding = PREFERRED_BINDING
@@ -193,7 +194,7 @@ class Config(object):
if context == "":
setattr(self, attr, val)
else:
- setattr(self, "_%s_%s" % (context,attr), val)
+ setattr(self, "_%s_%s" % (context, attr), val)
def getattr(self, attr, context=None):
if context is None:
@@ -202,7 +203,7 @@ class Config(object):
if context == "":
return getattr(self, attr, None)
else:
- return getattr(self, "_%s_%s" % (context,attr), None)
+ return getattr(self, "_%s_%s" % (context, attr), None)
def load_special(self, cnf, typ, metadata_construction=False):
for arg in SPEC[typ]:
@@ -228,8 +229,7 @@ class Config(object):
acs = ac_factory()
if not acs:
- raise Exception(("No attribute converters, ",
- "something is wrong!!"))
+ raise Exception("No attribute converters, something is wrong!!")
_acs = self.getattr("attribute_converters", typ)
if _acs:
@@ -273,23 +273,23 @@ class Config(object):
for arg in COMMON_ARGS:
if arg == "virtual_organization":
if "virtual_organization" in cnf:
- for key,val in cnf["virtual_organization"].items():
+ for key, val in cnf["virtual_organization"].items():
self.vorg[key] = VirtualOrg(None, key, val)
continue
-
try:
setattr(self, arg, _uc(cnf[arg]))
except KeyError:
pass
- except TypeError: # Something that can't be a string
+ except TypeError: # Something that can't be a string
setattr(self, arg, cnf[arg])
if "service" in cnf:
for typ in ["aa", "idp", "sp", "pdp", "aq"]:
try:
- self.load_special(cnf["service"][typ], typ,
- metadata_construction=metadata_construction)
+ self.load_special(
+ cnf["service"][typ], typ,
+ metadata_construction=metadata_construction)
self.serves.append(typ)
except KeyError:
pass
@@ -301,8 +301,8 @@ class Config(object):
# verify that xmlsec is where it's supposed to be
if not os.path.exists(self.xmlsec_binary):
#if not os.access(, os.F_OK):
- raise Exception("xmlsec binary not in '%s' !" % (
- self.xmlsec_binary))
+ raise Exception(
+ "xmlsec binary not in '%s' !" % self.xmlsec_binary)
self.load_complex(cnf, metadata_construction=metadata_construction)
self.context = self.def_context
@@ -340,8 +340,9 @@ class Config(object):
except:
disable_validation = False
- mds = MetadataStore(ONTS.values(), acs, xmlsec_binary, ca_certs,
- disable_ssl_certificate_validation=disable_validation)
+ mds = MetadataStore(
+ ONTS.values(), acs, xmlsec_binary, ca_certs,
+ disable_ssl_certificate_validation=disable_validation)
mds.imp(metadata_conf)
@@ -395,7 +396,7 @@ class Config(object):
raise Exception("Unknown socktype!")
try:
handler = LOG_HANDLER[htyp](**args)
- except TypeError: # difference between 2.6 and 2.7
+ except TypeError: # difference between 2.6 and 2.7
del args["socktype"]
handler = LOG_HANDLER[htyp](**args)
else:
@@ -415,7 +416,7 @@ class Config(object):
return handler
def setup_logger(self):
- if root_logger.level != logging.NOTSET: # Someone got there before me
+ if root_logger.level != logging.NOTSET: # Someone got there before me
return root_logger
_logconf = self.logger
@@ -424,13 +425,14 @@ class Config(object):
try:
root_logger.setLevel(LOG_LEVEL[_logconf["loglevel"].lower()])
- except KeyError: # reasonable default
+ except KeyError: # reasonable default
root_logger.setLevel(logging.INFO)
root_logger.addHandler(self.log_handler())
root_logger.info("Logging started")
return root_logger
+
class SPConfig(Config):
def_context = "sp"
@@ -458,12 +460,14 @@ class SPConfig(Config):
return None
+
class IdPConfig(Config):
def_context = "idp"
def __init__(self):
Config.__init__(self)
+
def config_factory(typ, filename):
if typ == "sp":
conf = SPConfig().load_file(filename)
diff --git a/src/saml2/eptid.py b/src/saml2/eptid.py
new file mode 100644
index 00000000..dd66d8fa
--- /dev/null
+++ b/src/saml2/eptid.py
@@ -0,0 +1,50 @@
+# An eduPersonTargetedID comprises
+# the entity name of the identity provider, the entity name of the service
+# provider, and the opaque string value.
+# These strings are separated by "!" symbols. This form is advocated by
+# Internet2 and may overtake the other form in due course.
+
+import hashlib
+import shelve
+
+import logging
+
+logger = logging.getLogger(__name__)
+
+
+class Eptid(object):
+ def __init__(self, secret):
+ self._db = {}
+ self.secret = secret
+
+ def make(self, idp, sp, args):
+ md5 = hashlib.md5()
+ for arg in args:
+ md5.update(arg.encode("utf-8"))
+ md5.update(sp)
+ md5.update(self.secret)
+ md5.digest()
+ hashval = md5.hexdigest()
+ return "!".join([idp, sp, hashval])
+
+ def __getitem__(self, key):
+ return self._db[key]
+
+ def __setitem__(self, key, value):
+ self._db[key] = value
+
+ def get(self, idp, sp, args):
+ # key is a combination of sp_entity_id and object id
+ key = (".".join([sp, args[0]])).encode("utf-8")
+ try:
+ return self[key]
+ except KeyError:
+ val = self.make(idp, sp, args[1])
+ self[key] = val
+ return val
+
+
+class EptidShelve(Eptid):
+ def __init__(self, secret, filename):
+ Eptid.__init__(self, secret)
+ self._db = shelve.open(filename, writeback=True)
diff --git a/src/saml2/mongo_store.py b/src/saml2/mongo_store.py
index baa8433a..25a36b0e 100644
--- a/src/saml2/mongo_store.py
+++ b/src/saml2/mongo_store.py
@@ -2,6 +2,8 @@ from hashlib import sha1
import logging
from pymongo import MongoClient
+from saml2.eptid import Eptid
+from saml2.mdstore import MetaData
from saml2.s_utils import PolicyError
from saml2.ident import code, IdentDB, Unknown
@@ -35,6 +37,10 @@ __author__ = 'rolandh'
logger = logging.getLogger(__name__)
+class CorruptDatabase(Exception):
+ pass
+
+
def context_match(cfilter, cntx):
# TODO
return True
@@ -185,6 +191,7 @@ class IdentMDB(IdentDB):
pass
+#------------------------------------------------------------------------------
class MDB(object):
primary_key = "mdb"
@@ -193,8 +200,11 @@ class MDB(object):
_db = connection[collection]
self.db = _db[sub_collection]
- def store(self, key, **kwargs):
- doc = {self.primary_key: key}
+ def store(self, value, **kwargs):
+ if value:
+ doc = {self.primary_key: value}
+ else:
+ doc = {}
doc.update(kwargs)
_ = self.db.insert(doc)
@@ -217,6 +227,111 @@ class MDB(object):
for item in self.db.find(doc):
self.db.remove(item["_id"])
+ def keys(self):
+ for item in self.db.find():
+ yield item[self.primary_key]
+
+ def items(self):
+ for item in self.db.find():
+ _key = item[self.primary_key]
+ del item[self.primary_key]
+ del item["_id"]
+ yield _key, item
+
+ def __contains__(self, key):
+ doc = {self.primary_key: key}
+ res = [item for item in self.db.find(doc)]
+ if not res:
+ return False
+ else:
+ return True
+
+
+#------------------------------------------------------------------------------
+class EptidMDB(Eptid):
+ primary_key = "eptid"
+
+ def __init__(self, secret, collection="", sub_collection=""):
+ Eptid.__init__(self, secret)
+ self.mdb = MDB(collection, sub_collection)
+ self.mdb.primary_key = "entity_id"
+
+ def __getitem__(self, key):
+ res = self.mdb.get(key)
+ if not res:
+ raise KeyError(key)
+ elif len(res) == 1:
+ return res[0]
+ else:
+ raise CorruptDatabase("Found more than one EPTID document")
+
+ def __setitem__(self, key, value):
+ if key == self.mdb.primary_key:
+ _ = self.mdb.store(value)
+ else:
+ _ = self.mdb.store(**{key: value})
+
+
+#------------------------------------------------------------------------------
+class MetadataMDB(MetaData):
+ def __init__(self, onts, attrc, collection="", sub_collection=""):
+ MetaData.__init__(self, onts, attrc)
+ 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[entity_id][typ]
+ except KeyError:
+ return None
+
+ if not srvs:
+ return srvs
+
+ res = []
+ for srv in srvs:
+ if "extensions" in srv:
+ for elem in srv["extensions"]["extension_elements"]:
+ if elem["__class__"] == service:
+ if elem["binding"] == binding:
+ res.append(elem)
+
+ return res
+
+ def load(self):
+ pass
+
+ def items(self):
+ return self.mdb.items()
+
+ def keys(self):
+ return self.mdb.keys()
+
+ def __contains__(self, item):
+ pass
+
+ def attribute_requirement(self):
+ pass
+
+ def with_descriptor(self):
+ pass
+
+ def construct_source_id(self):
+ pass
-class MDB_eptid(MDB):
- primary_key = "userid"
+ def bindings(self, entity_id, typ, service):
+ pass \ No newline at end of file
diff --git a/src/saml2/server.py b/src/saml2/server.py
index 4d9dc974..077de45a 100644
--- a/src/saml2/server.py
+++ b/src/saml2/server.py
@@ -23,7 +23,8 @@ import os
import shelve
import memcache
-from saml2.mongo_store import IdentMDB, SessionStorageMDB
+from saml2.eptid import EptidShelve, Eptid
+from saml2.mongo_store import IdentMDB, SessionStorageMDB, EptidMDB
from saml2.sdb import SessionStorage
from saml2.schema import soapenv
@@ -70,6 +71,7 @@ class Server(Entity):
self.symkey = symkey
self.seed = rndstr()
self.iv = os.urandom(16)
+ self.eptid = None
def support_AssertionIDRequest(self):
return True
@@ -128,6 +130,20 @@ class Server(Entity):
raise Exception("Couldn't open identity database: %s" %
(dbspec,))
+ dbspec = self.config.getattr("edu_person_targeted_id", "idp")
+ if not dbspec:
+ pass
+ else:
+ typ = dbspec[0]
+ addr = dbspec[1]
+ secret = dbspec[2]
+ if typ == "shelve":
+ self.eptid = EptidShelve(secret, addr)
+ elif typ == "mongodb":
+ self.eptid = EptidMDB(secret, addr, *dbspec[3:])
+ else:
+ self.eptid = Eptid(secret)
+
def wants(self, sp_entity_id, index=None):
""" Returns what attributes the SP requires and which are optional
if any such demands are registered in the Metadata.