# HG changeset patch # User Paul Crowley # Date 1208865356 -3600 # Node ID 62ee928ac9b3f7c3c5be23276e69a805a8d64f69 # Parent 538d6b198f4a17be01feaa79ae1e97e501f96d3c fixes following actual testing diff -r 538d6b198f4a -r 62ee928ac9b3 access.py --- a/access.py Tue Apr 22 09:46:29 2008 +0100 +++ b/access.py Tue Apr 22 12:55:56 2008 +0100 @@ -13,7 +13,7 @@ from mercurial import util import os -import rules +import ruleset class Checker(object): '''acl checker.''' @@ -23,7 +23,7 @@ self.repo = repo self.repo_path = os.environ['HG_REPO_PATH'] self.user = os.environ['REMOTE_USER'] - self.rules = rules.Ruleset.readfile(os.environ['HG_ACCESS_RULES_FILE']) + self.rules = ruleset.Ruleset.readfile(os.environ['HG_ACCESS_RULES_FILE']) def check(self, node): '''return if access allowed, raise exception if not.''' diff -r 538d6b198f4a -r 62ee928ac9b3 hg-ssh --- a/hg-ssh Tue Apr 22 09:46:29 2008 +0100 +++ b/hg-ssh Tue Apr 22 12:55:56 2008 +0100 @@ -34,7 +34,7 @@ from mercurial import dispatch import sys, os -import rules +import ruleset def fail(message): #logfile.write("Fail: %s\n" % message) @@ -44,7 +44,7 @@ def getpath(path): if path.endswith("/"): path = path[:-1] - if not rules.goodpath(path): + if not ruleset.goodpath(path): fail("Disallowing path: %s" % path) return path @@ -68,7 +68,7 @@ fail("hg-ssh must have no arguments (%s)" % sys.argv) -rules = rules.Ruleset.readfile(os.environ['HG_ACCESS_RULES_FILE']) +rules = ruleset.Ruleset.readfile(os.environ['HG_ACCESS_RULES_FILE']) remoteuser = getpath(os.environ['REMOTE_USER']) todispatch = get_cmd(rules, remoteuser, os.environ.get('SSH_ORIGINAL_COMMAND', '?')) diff -r 538d6b198f4a -r 62ee928ac9b3 rules.py --- a/rules.py Tue Apr 22 09:46:29 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,81 +0,0 @@ -# Copyright 2008 LShift Ltd -# Author(s): -# Paul Crowley -# -# This software may be used and distributed according to the terms -# of the GNU General Public License, incorporated herein by reference. - -import re - -allowedchars = "A-Za-z0-9_-" - -goodpathre = re.compile("([%s]+/)*[%s]+$" % (allowedchars, allowedchars)) -def goodpath(path): - return goodpathre.match(path) is not None: - -goodglobre = re.compile("[*/%s]+$" % allowedchars) - -def goodglob(pattern): - return goodglobre.match(pattern) is not None - -# 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 goodglob(pattern): - #fail("Bad glob pattern in auth config: %s" % pattern) - # FIXME: report it somehow - return lambda x: False - # Substitution cunning so ** can be different from * - pattern = pattern.replace("*", "[]") - pattern = pattern.replace("[][]", "[/%s]*" % allowedchars) - pattern = pattern.replace("[]", "[%s]*" % allowedchars) - rex = re.compile(pattern + "$") - # 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 Ruleset(object): - '''Class representing the rules in a rule file''' - - 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) - - @classmethod - def read_rules(cls, fn): - res = cls() - 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() - return res - - diff -r 538d6b198f4a -r 62ee928ac9b3 ruleset.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ruleset.py Tue Apr 22 12:55:56 2008 +0100 @@ -0,0 +1,82 @@ +# Copyright 2008 LShift Ltd +# Author(s): +# Paul Crowley +# +# This software may be used and distributed according to the terms +# of the GNU General Public License, incorporated herein by reference. + +import re + +allowedchars = "A-Za-z0-9_-" + +goodpathre = re.compile("([%s]+/)*[%s]+$" % (allowedchars, allowedchars)) +def goodpath(path): + return goodpathre.match(path) is not None + +goodglobre = re.compile("[*/%s]+$" % allowedchars) + +def goodglob(pattern): + return goodglobre.match(pattern) is not None + +# 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 goodglob(pattern): + #fail("Bad glob pattern in auth config: %s" % pattern) + # FIXME: report it somehow + return lambda x: False + # Substitution cunning so ** can be different from * + pattern = pattern.replace("*", "[]") + pattern = pattern.replace("[][]", "[/%s]*" % allowedchars) + pattern = pattern.replace("[]", "[%s]*" % allowedchars) + rex = re.compile(pattern + "$") + # 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 Ruleset(object): + '''Class representing the rules in a rule file''' + + 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 = self.matchrule(**kw) + return a in self.levels and self.levels.index(a) <= self.levels.index(level) + + @classmethod + def readfile(cls, fn): + res = cls() + f = open(fn) + try: + for l in f: + l = l.strip() + if len(l) == 0 or l.startswith("#"): + continue + l = l.split() + res.add(l[0], rule([c.split("=", 1) for c in l[1:]])) + finally: + f.close() + return res + +