summaryrefslogtreecommitdiff
path: root/src/saml2/authn_context/__init__.py
diff options
context:
space:
mode:
authorRoland Hedberg <roland.hedberg@adm.umu.se>2013-04-28 09:52:57 +0200
committerRoland Hedberg <roland.hedberg@adm.umu.se>2013-04-28 09:52:57 +0200
commit3bc4cd1d3db79389204d5ed70c12b7a2110769cf (patch)
tree85ef2a5ac61df5186710b284257b0946d11a9923 /src/saml2/authn_context/__init__.py
parent4d138a9b38928efdb3419bb19129af99ffd8d910 (diff)
downloadpysaml2-3bc4cd1d3db79389204d5ed70c12b7a2110769cf.tar.gz
Added support for comparision types.
Diffstat (limited to 'src/saml2/authn_context/__init__.py')
-rw-r--r--src/saml2/authn_context/__init__.py142
1 files changed, 115 insertions, 27 deletions
diff --git a/src/saml2/authn_context/__init__.py b/src/saml2/authn_context/__init__.py
index b54aee40..20316eb6 100644
--- a/src/saml2/authn_context/__init__.py
+++ b/src/saml2/authn_context/__init__.py
@@ -1,7 +1,13 @@
+from saml2.saml import AuthnContext, AuthnContextClassRef
+
__author__ = 'rolandh'
+import hashlib
+
from saml2 import extension_elements_to_elements
+UNSPECIFIED = "urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified"
+
INTERNETPROTOCOLPASSWORD = \
'urn:oasis:names:tc:SAML:2.0:ac:classes:InternetProtocolPassword'
MOBILETWOFACTORCONTRACT = \
@@ -22,10 +28,24 @@ from saml2.authn_context import ppt
from saml2.authn_context import pword
from saml2.authn_context import sslcert
+CMP_TYPE = ['exact', 'minimum', 'maximum', 'better']
+
class AuthnBroker(object):
def __init__(self):
- self.db = {}
+ self.db = {"info": {}, "key": {}}
+
+ def exact(self, a, b):
+ return a == b
+
+ def minimum(self, a, b):
+ return b >= a
+
+ def maximum(self, a, b):
+ return b <= a
+
+ def better(self, a, b):
+ return b > a
def add(self, spec, method, level=0, authn_authority=""):
"""
@@ -41,8 +61,9 @@ class AuthnBroker(object):
"""
if spec.authn_context_class_ref:
- _ref = spec.authn_context_class_ref.text
- self.db[_ref] = {
+ key = spec.authn_context_class_ref.text
+ _info = {
+ "class_ref": key,
"method": method,
"level": level,
"authn_auth": authn_authority
@@ -55,12 +76,76 @@ class AuthnBroker(object):
"level": level,
"authn_auth": authn_authority
}
+ else:
+ raise NotImplementedError()
+
+ m = hashlib.md5()
+ for attr in ["method", "level", "authn_auth"]:
+ m.update(str(_info[attr]))
+ try:
+ _txt = "%s" % _info["decl"]
+ except KeyError:
+ pass
+ else:
+ m.update(_txt)
+
+ _ref = m.hexdigest()
+ self.db["info"][_ref] = _info
+ try:
+ self.db["key"][key].append(_ref)
+ except KeyError:
+ self.db["key"][key] = [_ref]
+
+ def remove(self, spec, method=None, level=0, authn_authority=""):
+ if spec.authn_context_class_ref:
+ _cls_ref = spec.authn_context_class_ref.text
try:
- self.db[key].append(_info)
+ _refs = self.db["key"][_cls_ref]
except KeyError:
- self.db[key] = [_info]
-
- def pick(self, req_authn_context):
+ return
+ else:
+ _remain = []
+ for _ref in _refs:
+ item = self.db["info"][_ref]
+ if method and method != item["method"]:
+ _remain.append(_ref)
+ if level and level != item["level"]:
+ _remain.append(_ref)
+ if authn_authority and \
+ authn_authority != item["authn_authority"]:
+ _remain.append(_ref)
+ if _remain:
+ self.db[_cls_ref] = _remain
+
+ def _pick_by_class_ref(self, cls_ref, comparision_type="exact"):
+ func = getattr(self, comparision_type)
+ try:
+ _refs = self.db["key"][cls_ref]
+ except KeyError:
+ return []
+ else:
+ _item = self.db["info"][_refs[0]]
+ _level = _item["level"]
+ if _item["method"]:
+ res = [(_item["method"], _refs[0])]
+ else:
+ res = []
+ for ref in _refs[1:]:
+ item = self.db[ref]
+ res.append((item["method"], ref))
+ if func(_level, item["level"]):
+ _level = item["level"]
+ for ref, _dic in self.db["info"].items():
+ if ref in _refs:
+ continue
+ elif func(_level, _dic["level"]):
+ if _dic["method"]:
+ _val = (_dic["method"], ref)
+ if _val not in res:
+ res.append(_val)
+ return res
+
+ def pick(self, req_authn_context=None):
"""
Given the authentication context find zero or more places where
the user could be sent next. Ordered according to security level.
@@ -70,29 +155,25 @@ class AuthnBroker(object):
:return: An URL
"""
+ if req_authn_context is None:
+ return self._pick_by_class_ref(UNSPECIFIED, "minimum")
if req_authn_context.authn_context_class_ref:
- _ref = req_authn_context.authn_context_class_ref.text
- try:
- _info = self.db[_ref]
- except KeyError:
- return []
+ if req_authn_context.comparison:
+ _cmp = req_authn_context.comparison
else:
- _level = _info["level"]
- res = []
- for key, _dic in self.db.items():
- if key == _ref:
- continue
- elif _dic["level"] >= _level:
- res.append(_dic["method"])
- res.insert(0, _info["method"])
- return res
+ _cmp = "minimum"
+ return self._pick_by_class_ref(
+ req_authn_context.authn_context_class_ref.text, _cmp)
elif req_authn_context.authn_context_decl:
- key = req_authn_context.authn_context_decl.c_namespace
+ _decl = req_authn_context.authn_context_decl
+ key = _decl.c_namespace
_methods = []
- for _dic in self.db[key]:
- if self.match(req_authn_context.authn_context_decl,
- _dic["decl"]):
- _methods.append(_dic["method"])
+ for _ref in self.db["key"][key]:
+ _dic = self.db["info"][_ref]
+ if self.match(_decl, _dic["decl"]):
+ _val = (_dic["method"], _ref)
+ if _val not in _methods:
+ _methods.append(_val)
return _methods
def match(self, requested, provided):
@@ -101,6 +182,9 @@ class AuthnBroker(object):
else:
return False
+ def __getitem__(self, ref):
+ return self.db["info"][ref]
+
def authn_context_factory(text):
# brute force
@@ -118,4 +202,8 @@ def authn_context_decl_from_extension_elements(extelems):
try:
return res[0]
except IndexError:
- return None \ No newline at end of file
+ return None
+
+
+def authn_context_class_ref(ref):
+ return AuthnContext(authn_context_class_ref=AuthnContextClassRef(text=ref)) \ No newline at end of file