11 def globmatcher(pattern): |
11 def globmatcher(pattern): |
12 p = "[^/]*".join(re.escape(c) for c in pattern.split("*")) |
12 p = "[^/]*".join(re.escape(c) for c in pattern.split("*")) |
13 # ** means "match recursively" ie "ignore directories" |
13 # ** means "match recursively" ie "ignore directories" |
14 return re.compile(p.replace("[^/]*[^/]*", ".*") + "$") |
14 return re.compile(p.replace("[^/]*[^/]*", ".*") + "$") |
15 |
15 |
16 # Returns True for a definite match |
16 # Returns 1 for a definite match |
17 # False for a definite non-match |
17 # -1 for a definite non-match |
18 # None where we can't be sure because a key is None |
18 # 0 where we can't be sure because a key is None |
|
19 def rmatch(k, m, kw): |
|
20 if k not in kw: |
|
21 return -1 |
|
22 kkw = kw[k] |
|
23 if kkw is None: |
|
24 return 0 |
|
25 elif m.match(kkw) is None: |
|
26 return -1 |
|
27 else: |
|
28 return 1 |
|
29 |
19 def rule(pairs): |
30 def rule(pairs): |
20 matchers = [(k, globmatcher(v)) for k, v in pairs] |
31 matchers = [(k, globmatcher(v)) for k, v in pairs] |
21 def c(kw): |
32 def c(kw): |
22 for k, m in matchers: |
33 return min(rmatch(k, m, kw) for k, m in matchers) |
23 if k not in kw: |
|
24 return False |
|
25 kkw = kw[k] |
|
26 if kkw is None: |
|
27 return None |
|
28 if m.match(kkw) is None: |
|
29 return False |
|
30 return True |
|
31 return c |
34 return c |
32 |
35 |
33 class Ruleset(object): |
36 class Ruleset(object): |
34 '''Class representing the rules in a rule file''' |
37 '''Class representing the rules in a rule file''' |
35 |
38 |
36 levels = ["init", "write", "read", "deny"] |
39 levels = ["init", "write", "read", "deny"] |
37 |
40 |
38 def __init__(self): |
41 def __init__(self): |
39 self.rules = [] |
42 self.rules = [] |
40 self.preset = {} |
43 self.preset = {} |
41 |
44 |
42 def add(self, action, conditions): |
|
43 self.rules.append((action, conditions)) |
|
44 |
|
45 def set(self, **kw): |
45 def set(self, **kw): |
46 self.preset.update(kw) |
46 self.preset.update(kw) |
47 |
47 |
48 def get(self, k): |
48 def get(self, k): |
49 return self.preset.get(k, None) |
49 return self.preset.get(k, None) |
50 |
50 |
51 def matchrules(self, kw): |
51 def allow(self, level, **kw): |
|
52 levelindex = self.levels.index(level) |
52 d = self.preset.copy() |
53 d = self.preset.copy() |
53 d.update(kw) |
54 d.update(kw) |
54 res = set() |
|
55 for a, c in self.rules: |
55 for a, c in self.rules: |
56 m = c(d) |
56 m = c(d) |
57 if m is None: |
57 if m == 1: |
58 # "Maybe match" - add it and carry on |
58 # Definite match - what it says goes |
59 res.add(a) |
59 return a <= levelindex |
60 elif m: |
60 elif m == 0: |
61 # Definite match - add it and stop |
61 # "Maybe match" - allow if it says yes, ignore if no |
62 res.add(a) |
62 if a <= levelindex: |
63 break |
|
64 return res |
|
65 |
|
66 def allow(self, level, **kw): |
|
67 for a in self.matchrules(kw): |
|
68 if a in self.levels: |
|
69 if self.levels.index(a) <= self.levels.index(level): |
|
70 return True |
63 return True |
71 return False |
64 return False |
72 |
65 |
73 def readfile(self, fn): |
66 def readfile(self, fn): |
74 f = open(fn) |
67 f = open(fn) |
75 try: |
68 try: |
76 for l in f: |
69 for l in f: |
77 l = l.strip() |
70 l = l.strip() |
78 if len(l) == 0 or l.startswith("#"): |
71 if len(l) == 0 or l.startswith("#"): |
79 continue |
72 continue |
80 l = l.split() |
73 l = l.split() |
81 self.add(l[0], rule([c.split("=", 1) for c in l[1:]])) |
74 # Unrecognized actions are off the high end |
|
75 if l[0] in self.levels: |
|
76 ix = self.levels.index(l[0]) |
|
77 else: |
|
78 ix = len(self.levels) |
|
79 self.rules.append((ix, |
|
80 rule([c.split("=", 1) for c in l[1:]]))) |
82 finally: |
81 finally: |
83 f.close() |
82 f.close() |
84 |
83 |
85 rules = Ruleset() |
84 rules = Ruleset() |
86 |
|