src/mercurialserver/ruleset.py
branchdebian
changeset 313 f597eb3b5aaf
parent 311 3cbde66305e4
child 371 e9ce904b62a9
equal deleted inserted replaced
294:31d5c6236f71 313:f597eb3b5aaf
    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