src/mercurialserver/ruleset.py
changeset 67 fd16d9a1234b
parent 62 f1e319d3672a
child 77 8d14aac93b5d
equal deleted inserted replaced
66:2f0ea1163b9e 67:fd16d9a1234b
       
     1 # Copyright 2008-2009 LShift Ltd
       
     2 # Author(s):
       
     3 # Paul Crowley <paul@lshift.net>
       
     4 #
       
     5 # This software may be used and distributed according to the terms
       
     6 # of the GNU General Public License, incorporated herein by reference.
       
     7 
       
     8 import sys
       
     9 import re
       
    10 import os
       
    11 import os.path
       
    12 
       
    13 allowedchars = "A-Za-z0-9_-"
       
    14 
       
    15 goodpathre = re.compile("([%s]+/)*[%s]+$" % (allowedchars, allowedchars))
       
    16 def goodpath(path):
       
    17     return goodpathre.match(path) is not None
       
    18 
       
    19 goodglobre = re.compile("[*/%s]+$" % allowedchars)
       
    20 
       
    21 def goodglob(pattern):
       
    22     return goodglobre.match(pattern) is not None
       
    23 
       
    24 # Don't put anything except *A-Za-z0-9_- in rule globs or   
       
    25 # it will match nothing.  No regexp metachars, not even .
       
    26 # We may fix this later.
       
    27 def globmatcher(pattern):
       
    28     if not goodglob(pattern):
       
    29         #fail("Bad glob pattern in auth config: %s" % pattern)
       
    30         # FIXME: report it somehow
       
    31         return lambda x: False
       
    32     # Substitution cunning so ** can be different from *
       
    33     pattern = pattern.replace("*", "[]")
       
    34     pattern = pattern.replace("[][]", "[/%s]*" % allowedchars)
       
    35     pattern = pattern.replace("[]", "[%s]*" % allowedchars)
       
    36     rex = re.compile(pattern + "$")
       
    37     # None matches everything
       
    38     return lambda x: x is None or rex.match(x) is not None
       
    39 
       
    40 def rule(pairs):
       
    41     matchers = [(k, globmatcher(v)) for k, v in pairs]
       
    42     def c(kw):
       
    43         for k, m in matchers:
       
    44             if k not in kw or not m(kw[k]):
       
    45                 return False
       
    46         return True
       
    47     return c
       
    48 
       
    49 class Ruleset(object):
       
    50     '''Class representing the rules in a rule file'''
       
    51     
       
    52     levels = ["init", "write", "read", "deny"]
       
    53 
       
    54     def __init__(self):
       
    55         self.rules = []
       
    56         self.preset = {}
       
    57 
       
    58     def add(self, action, conditions):
       
    59         self.rules.append((action, conditions))
       
    60 
       
    61     def set(self, **kw):
       
    62         self.preset.update(kw)
       
    63         
       
    64     def matchrule(self, kw):
       
    65         d = self.preset.copy()
       
    66         d.update(kw)
       
    67         for a, c in self.rules:
       
    68             if c(d):
       
    69                 return a
       
    70         return None
       
    71 
       
    72     def allow(self, level, **kw):
       
    73         a = self.matchrule(kw)
       
    74         return a in self.levels and self.levels.index(a) <= self.levels.index(level)
       
    75     
       
    76     def readfile(self, fn):
       
    77         try:
       
    78             f = open(fn)
       
    79             try:
       
    80                 for l in f:
       
    81                     l = l.strip()
       
    82                     if len(l) == 0 or l.startswith("#"):
       
    83                         continue
       
    84                     l = l.split()
       
    85                     self.add(l[0], rule([c.split("=", 1) for c in l[1:]]))
       
    86             finally:
       
    87                 f.close()
       
    88         except Exception, e:
       
    89             print >> sys.stderr, "Failure reading rules file:", e
       
    90 
       
    91 def rules_from_env():
       
    92     res = Ruleset()
       
    93     for f in os.environ['HG_ACCESS_RULES_PATH'].split(os.pathsep):
       
    94         if os.path.isfile(f):
       
    95             res.readfile(f)
       
    96     return res