# HG changeset patch # User Paul Crowley # Date 1236330900 0 # Node ID fd16d9a1234b16c09da162b16fdb4964c7f77268 # Parent 2f0ea1163b9eb079d6868cc76e75cacee4ecd83d Put .py files into a directory of their own diff -r 2f0ea1163b9e -r fd16d9a1234b doc/PLAN --- a/doc/PLAN Fri Mar 06 08:56:48 2009 +0000 +++ b/doc/PLAN Fri Mar 06 09:15:00 2009 +0000 @@ -1,8 +1,11 @@ Plan: do away with shell scripts, and un-hard-wire the path /usr/local/lib/mercurial-server. -Steps: +Done: - move all modules into mercurial_server directory + +Todo: + - make mercurial_server.paths module; a function sets a global based on sys.argv[0] - reduce hg-ssh-wrapper to nothing by moving all functionality into hg-ssh - same for refresh-auth diff -r 2f0ea1163b9e -r fd16d9a1234b install --- a/install Fri Mar 06 08:56:48 2009 +0000 +++ b/install Fri Mar 06 09:15:00 2009 +0000 @@ -6,11 +6,13 @@ install -o root -g root -t /usr/local/lib/mercurial-server \ src/hg-ssh \ src/do-refresh-auth -install -o root -g root -t /usr/local/lib/mercurial-server -m 644 \ - src/changes.py \ - src/access.py \ - src/servelog.py \ - src/ruleset.py +install -o root -g root -d /usr/local/lib/mercurial-server/mercurialserver +install -o root -g root -t /usr/local/lib/mercurial-server/mercurialserver -m 644 \ + src/mercurialserver/__init__.py \ + src/mercurialserver/changes.py \ + src/mercurialserver/access.py \ + src/mercurialserver/servelog.py \ + src/mercurialserver/ruleset.py install -o root -g root -d /usr/local/lib/mercurial-server/init install -o root -g root -t /usr/local/lib/mercurial-server/init \ src/init/hginit diff -r 2f0ea1163b9e -r fd16d9a1234b src/access.py --- a/src/access.py Fri Mar 06 08:56:48 2009 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ -# Copyright 2008-2009 LShift Ltd -# Copyright 2006 Vadim Gelfer -# -# Authors: -# Paul Crowley -# 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 _ -import mercurial.util -import mercurial.node - -import os -import ruleset -import changes - -class Checker(object): - '''acl checker.''' - - def __init__(self, ui, repo): - self.ui = ui - self.repo = repo - - self.rules = ruleset.rules_from_env() - self.rules.set(user = os.environ['REMOTE_USER']) - self.rules.set(repo = os.environ['HG_REPO_PATH']) - - def allow(self, ctx): - branch = ctx.branch() - if not self.rules.allow("write", branch=branch, file=None): - return False - for f in ctx.files(): - if not self.rules.allow("write", branch=branch, file=f): - return False - return True - - def check(self, ctx): - '''return if access allowed, raise exception if not.''' - if not self.allow(ctx): - raise mercurial.util.Abort(_('%s: access denied for changeset %s') % - (__name__, mercurial.node.short(ctx.node()))) - -def hook(ui, repo, hooktype, node=None, source=None, **kwargs): - if hooktype != 'pretxnchangegroup': - raise mercurial.util.Abort(_('config error - hook type "%s" cannot stop ' - 'incoming changesets') % hooktype) - c = Checker(ui, repo) - for ctx in changes.changes(repo, node): - c.check(ctx) diff -r 2f0ea1163b9e -r fd16d9a1234b src/changes.py --- a/src/changes.py Fri Mar 06 08:56:48 2009 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ -# Copyright 2008-2009 LShift Ltd -# Copyright 2006 Vadim Gelfer -# -# Authors: -# Paul Crowley -# Vadim Gelfer -# -# This software may be used and distributed according to the terms -# of the GNU General Public License, incorporated herein by reference. - -def changes(repo, node): - start = repo.changectx(node).rev() - try: - end = len(repo.changelog) - except: - end = repo.changelog.count() - for rev in xrange(start, end): - yield repo.changectx(rev) diff -r 2f0ea1163b9e -r fd16d9a1234b src/do-refresh-auth --- a/src/do-refresh-auth Fri Mar 06 08:56:48 2009 +0000 +++ b/src/do-refresh-auth Fri Mar 06 09:15:00 2009 +0000 @@ -9,8 +9,8 @@ import sys import os import os.path -import ruleset import subprocess +from mercurialserver import ruleset if len(sys.argv) <= 3: sys.stderr.write("refresh-auth: wrong number of arguments (%s)\n" % sys.argv) diff -r 2f0ea1163b9e -r fd16d9a1234b src/hg-ssh --- a/src/hg-ssh Fri Mar 06 08:56:48 2009 +0000 +++ b/src/hg-ssh Fri Mar 06 09:15:00 2009 +0000 @@ -34,7 +34,7 @@ from mercurial import dispatch import sys, os -import ruleset +from mercurialserver import ruleset def fail(message): #logfile.write("Fail: %s\n" % message) diff -r 2f0ea1163b9e -r fd16d9a1234b src/init/conf/remote-hgrc --- a/src/init/conf/remote-hgrc Fri Mar 06 08:56:48 2009 +0000 +++ b/src/init/conf/remote-hgrc Fri Mar 06 09:15:00 2009 +0000 @@ -1,10 +1,10 @@ # hgrc to use for all remote users -[extensions] -access = /usr/local/lib/mercurial-server/access.py -servelog = /usr/local/lib/mercurial-server/servelog.py +#[extensions] +#access = /usr/local/lib/mercurial-server/mercurialserver/access.py +#servelog = /usr/local/lib/mercurial-server/servelog.py [hooks] -pretxnchangegroup.access = python:access.hook -changegroup.aaaaa_servelog = python:servelog.hook -outgoing.aaaaa_servelog = python:servelog.hook +pretxnchangegroup.access = python:mercurialserver.access.hook +changegroup.aaaaa_servelog = python:mercurialserver.servelog.hook +outgoing.aaaaa_servelog = python:mercurialserver.servelog.hook diff -r 2f0ea1163b9e -r fd16d9a1234b src/mercurialserver/access.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mercurialserver/access.py Fri Mar 06 09:15:00 2009 +0000 @@ -0,0 +1,51 @@ +# Copyright 2008-2009 LShift Ltd +# Copyright 2006 Vadim Gelfer +# +# Authors: +# Paul Crowley +# 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 _ +import mercurial.util +import mercurial.node + +import os +from mercurialserver import ruleset +from mercurialserver import changes + +class Checker(object): + '''acl checker.''' + + def __init__(self, ui, repo): + self.ui = ui + self.repo = repo + + self.rules = ruleset.rules_from_env() + self.rules.set(user = os.environ['REMOTE_USER']) + self.rules.set(repo = os.environ['HG_REPO_PATH']) + + def allow(self, ctx): + branch = ctx.branch() + if not self.rules.allow("write", branch=branch, file=None): + return False + for f in ctx.files(): + if not self.rules.allow("write", branch=branch, file=f): + return False + return True + + def check(self, ctx): + '''return if access allowed, raise exception if not.''' + if not self.allow(ctx): + raise mercurial.util.Abort(_('%s: access denied for changeset %s') % + (__name__, mercurial.node.short(ctx.node()))) + +def hook(ui, repo, hooktype, node=None, source=None, **kwargs): + if hooktype != 'pretxnchangegroup': + raise mercurial.util.Abort(_('config error - hook type "%s" cannot stop ' + 'incoming changesets') % hooktype) + c = Checker(ui, repo) + for ctx in changes.changes(repo, node): + c.check(ctx) diff -r 2f0ea1163b9e -r fd16d9a1234b src/mercurialserver/changes.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mercurialserver/changes.py Fri Mar 06 09:15:00 2009 +0000 @@ -0,0 +1,18 @@ +# Copyright 2008-2009 LShift Ltd +# Copyright 2006 Vadim Gelfer +# +# Authors: +# Paul Crowley +# Vadim Gelfer +# +# This software may be used and distributed according to the terms +# of the GNU General Public License, incorporated herein by reference. + +def changes(repo, node): + start = repo.changectx(node).rev() + try: + end = len(repo.changelog) + except: + end = repo.changelog.count() + for rev in xrange(start, end): + yield repo.changectx(rev) diff -r 2f0ea1163b9e -r fd16d9a1234b src/mercurialserver/ruleset.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mercurialserver/ruleset.py Fri Mar 06 09:15:00 2009 +0000 @@ -0,0 +1,96 @@ +# Copyright 2008-2009 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 sys +import re +import os +import os.path + +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 = [] + self.preset = {} + + def add(self, action, conditions): + self.rules.append((action, conditions)) + + def set(self, **kw): + self.preset.update(kw) + + 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 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 + +def rules_from_env(): + res = Ruleset() + for f in os.environ['HG_ACCESS_RULES_PATH'].split(os.pathsep): + if os.path.isfile(f): + res.readfile(f) + return res diff -r 2f0ea1163b9e -r fd16d9a1234b src/mercurialserver/servelog.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mercurialserver/servelog.py Fri Mar 06 09:15:00 2009 +0000 @@ -0,0 +1,34 @@ +# Copyright 2008-2009 LShift Ltd +# +# Authors: +# Paul Crowley +# +# 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 _ +import mercurial.util +import mercurial.node + +import os +import time +from mercurialserver import changes + +def hook(ui, repo, hooktype, node=None, source=None, **kwargs): + if hooktype == 'changegroup': + op = "push" + elif hooktype == 'outgoing': + op = "pull" + else: + raise mercurial.util.Abort(_('servelog installed as wrong hook type,' + ' must be changegroup or outgoing but is %s') % hooktype) + t = time.strftime("%Y-%m-%d_%H:%M:%S", time.gmtime()) + user = os.environ['REMOTE_USER'] + # FIXME: lock it + log = open(repo.join("serve-log"), "a+") + try: + for ctx in changes.changes(repo, node): + log.write("%s %s key=%s changeset=%s\n" % + (t, op, user, mercurial.node.hex(ctx.node()))) + finally: + log.close() diff -r 2f0ea1163b9e -r fd16d9a1234b src/ruleset.py --- a/src/ruleset.py Fri Mar 06 08:56:48 2009 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,96 +0,0 @@ -# Copyright 2008-2009 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 sys -import re -import os -import os.path - -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 = [] - self.preset = {} - - def add(self, action, conditions): - self.rules.append((action, conditions)) - - def set(self, **kw): - self.preset.update(kw) - - 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 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 - -def rules_from_env(): - res = Ruleset() - for f in os.environ['HG_ACCESS_RULES_PATH'].split(os.pathsep): - if os.path.isfile(f): - res.readfile(f) - return res diff -r 2f0ea1163b9e -r fd16d9a1234b src/servelog.py --- a/src/servelog.py Fri Mar 06 08:56:48 2009 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -# Copyright 2008-2009 LShift Ltd -# -# Authors: -# Paul Crowley -# -# 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 _ -import mercurial.util -import mercurial.node - -import os -import time -import changes - -def hook(ui, repo, hooktype, node=None, source=None, **kwargs): - if hooktype == 'changegroup': - op = "push" - elif hooktype == 'outgoing': - op = "pull" - else: - raise mercurial.util.Abort(_('servelog installed as wrong hook type,' - ' must be changegroup or outgoing but is %s') % hooktype) - t = time.strftime("%Y-%m-%d_%H:%M:%S", time.gmtime()) - user = os.environ['REMOTE_USER'] - # FIXME: lock it - log = open(repo.join("serve-log"), "a+") - try: - for ctx in changes.changes(repo, node): - log.write("%s %s key=%s changeset=%s\n" % - (t, op, user, mercurial.node.hex(ctx.node()))) - finally: - log.close()