src/ruleset.py
changeset 33 18e93dbdaf12
parent 32 4059dbe9f26a
child 39 f5055ce263c7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ruleset.py	Mon Jun 16 17:12:20 2008 +0100
@@ -0,0 +1,95 @@
+# Copyright 2008 LShift Ltd
+# Author(s):
+# Paul Crowley <paul@lshift.net>
+#
+# This software may be used and distributed according to the terms
+# of the GNU General Public License, incorporated herein by reference.
+
+import sys
+import re
+
+allowedchars = "A-Za-z0-9_-"
+
+goodpathre = re.compile("([%s]+/)*[%s]+$" % (allowedchars, allowedchars))
+def goodpath(path):
+    return goodpathre.match(path) is not None
+
+goodglobre = re.compile("[*/%s]+$" % allowedchars)
+
+def goodglob(pattern):
+    return goodglobre.match(pattern) is not None
+
+# Don't put anything except *A-Za-z0-9_- in rule globs or   
+# it will match nothing.  No regexp metachars, not even .
+# We may fix this later.
+def globmatcher(pattern):
+    if not goodglob(pattern):
+        #fail("Bad glob pattern in auth config: %s" % pattern)
+        # FIXME: report it somehow
+        return lambda x: False
+    # Substitution cunning so ** can be different from *
+    pattern = pattern.replace("*", "[]")
+    pattern = pattern.replace("[][]", "[/%s]*" % allowedchars)
+    pattern = pattern.replace("[]", "[%s]*" % allowedchars)
+    rex = re.compile(pattern + "$")
+    # None matches everything
+    return lambda x: x is None or rex.match(x) is not None
+
+def rule(pairs):
+    matchers = [(k, globmatcher(v)) for k, v in pairs]
+    def c(**kw):
+        for k, m in matchers:
+            if k not in kw or not m(kw[k]):
+                return False
+        return True
+    return c
+
+class Ruleset(object):
+    '''Class representing the rules in a rule file'''
+    
+    levels = ["init", "write", "read", "deny"]
+
+    def __init__(self):
+        # The user called "root" automatically has the highest
+        # privilege
+        self.rules = [(self.levels[0], rule([('user', 'root')]))]
+        self.preset = {}
+
+    def add(self, action, conditions):
+        self.rules.append((action, conditions))
+
+
+    def set(self, **kw):
+        self.preset.update(kw)
+        
+    def matchrule(self, **kw):
+        d = self.preset.copy()
+        d.update(**kw)
+        for a, c in self.rules:
+            if c(**d):
+                return a
+        return None
+
+    def allow(self, level, **kw):
+        a = self.matchrule(**kw)
+        return a in self.levels and self.levels.index(a) <= self.levels.index(level)
+    
+    @classmethod
+    def readfile(cls, fn):
+        res = cls()
+        try:
+            f = open(fn)
+            try:
+                for l in f:
+                    l = l.strip()
+                    if len(l) == 0 or l.startswith("#"):
+                        continue
+                    l = l.split()
+                    res.add(l[0], rule([c.split("=", 1) for c in l[1:]]))
+            finally:
+                f.close()
+        except Exception, e:
+            print >> sys.stderr, "Failure reading rules file:", e
+            return cls()
+        return res
+