src/mercurialserver/ruleset.py
author Paul Crowley <paul@lshift.net>
Mon, 12 Oct 2009 16:25:02 +0100
changeset 107 84e9e33d866b
parent 106 0519745e7a57
child 109 72100d3ed1bd
permissions -rw-r--r--
Fixes, plus base64 what you don't trust

# Copyright 2008-2009 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
import os
import os.path

allowedchars = "A-Za-z0-9_-"

def globmatcher(pattern):
    p = "[^/]*".join(re.escape(c) for c in pattern.split("*"))
    # ** means "match recursively" ie "ignore directories"
    rex = re.compile(p.replace("[^/]*[^/]*", ".*") + "$")
    # 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 AccessException(Exception):
    pass

class Ruleset(object):
    '''Class representing the rules in a rule file'''
    
    levels = ["init", "write", "read", "deny"]

    def __init__(self):
        self.rules = []
        self.preset = {}

    def add(self, action, conditions):
        self.rules.append((action, conditions))

    def set(self, **kw):
        self.preset.update(kw)
        
    def get(self, k):
        return self.preset.get(k, None)
        
    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)
    
    def check(self, level, **kw):
        if not self.allow(level, **kw):
            raise AccessException()
    
    def readfile(self, fn):
        try:
            f = open(fn)
            try:
                for l in f:
                    l = l.strip()
                    if len(l) == 0 or l.startswith("#"):
                        continue
                    l = l.split()
                    self.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

rules = Ruleset()