# HG changeset patch # User Paul Crowley # Date 1208777876 -3600 # Node ID 4c98440de85177987daf257132356725cdce44fc # Parent 9fac559c3d55362ddcf1184690472ab8f98b25b8 Started work on acl.py replacement - currently broken. diff -r 9fac559c3d55 -r 4c98440de851 access.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/access.py Mon Apr 21 12:37:56 2008 +0100 @@ -0,0 +1,107 @@ +# Copyright 2008 Paul Crowley +# Copyright 2006 Vadim Gelfer +# +# This software may be used and distributed according to the terms +# of the GNU General Public License, incorporated herein by reference. + +from mercurial.i18n import _ +from mercurial.node import * +from mercurial import util + +import os +import re + +allowedchars = "A-Za-z0-9_-" +goodglobre = re.compile("[*/%s]+$" % allowedchars) + +# 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 goodglobre.match(pattern): + #fail("Bad glob pattern in auth config: %s" % pattern) + # FIXME: report it somehow + return lambda x: False + pattern = pattern.replace("*", "[]") + pattern = pattern.replace("[][]", "[/%s]*" % allowedchars) + pattern = pattern.replace("[]", "[%s]*" % allowedchars) + rex = re.compile(pattern + "$") + return lambda x: 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 Rulefile(object): + '''Class representing the rules in a rule file''' + + self.levels = ["init", "write", "read", "deny"] + + def __init__(self): + self.rules = [] + + def add(self, action, conditions): + self.rules.append((action, conditions)) + + def matchrule(self, **kw): + for a, c in self.rules: + if c(**kw): + return a + return None + + def allow(self, level, **kw): + a = matchrule(self, **kw) + return a in self.levels and self.levels.index(a) <= self.levels.index(level) + +def read_rules(fn): + res = Rulefile() + f = open(fn) + try: + for l in f: + l = l.strip() + if len(l) == 0 or l.startswith["#"]: + continue + res.add(l[0], rule([c.split("=", 1) for c in l[1:]])) + finally: + f.close() + +class Checker(object): + '''acl checker.''' + + def getuser(self): + '''return name of hg-ssh user''' + return os.environ['REMOTE_USER'] + + def __init__(self, ui, repo): + self.ui = ui + self.repo = repo + self.rules = read_rules(os.environ['HG_ACCESS_RULES_FILE']) + + def check(self, node): + '''return if access allowed, raise exception if not.''' + files = self.repo.changectx(node).files() + for f in files: + if not self.rules.allow("write", user=self.user, ): + self.ui.debug(_('%s: user %s not allowed on %s\n') % + (__name__, self.getuser(), f)) + raise util.Abort(_('%s: access denied for changeset %s') % + (__name__, short(node))) + self.ui.debug(_('%s: allowing changeset %s\n') % (__name__, short(node))) + +def hook(ui, repo, hooktype, node=None, source=None, **kwargs): + if hooktype != 'pretxnchangegroup': + raise util.Abort(_('config error - hook type "%s" cannot stop ' + 'incoming changesets') % hooktype) + + c = checker(ui, repo) + + start = repo.changelog.rev(bin(node)) + end = repo.changelog.count() + for rev in xrange(start, end): + c.check(repo.changelog.node(rev)) +