# HG changeset patch # User Paul Crowley # Date 1208865797 -3600 # Node ID 9fa62cfd28213641b83ee7061c08c61246a3d913 # Parent 62ee928ac9b3f7c3c5be23276e69a805a8d64f69# Parent 578555227599ab8fe2863c50995deaba1defc2ec merge in branch support fixes diff -r 62ee928ac9b3 -r 9fa62cfd2821 README --- a/README Tue Apr 22 12:55:56 2008 +0100 +++ b/README Tue Apr 22 13:03:17 2008 +0100 @@ -132,6 +132,7 @@ user= - user's key repo= - repo (as the user supplies it) file= - file in the repo +branch= - name of the branch The first rule in the file which has all its conditions satisfied is used to determine whether an action is allowed. @@ -145,10 +146,11 @@ FILE CONDITIONS -The rules file is used to make three decisions: +The rules file is used to make four decisions: - Whether to allow a repository to be created - Whether to allow access to a repository +- Whether to allow a changeset on a particular branch at all - Whether to allow a changeset to change a particular file When the first two of these decisions are being made, nothing is known @@ -172,6 +174,18 @@ - For similar reasons, don't give "init" rules file conditions. +LOCKING YOURSELF OUT + +If you find yourself "locked out" - that is, that you no longer have +the permissions needed in hgadmin - you can break back in again if +you're able to become the "hg" user on the repository host. Once you +are that user, delete ~hg/.ssh/authorized_keys (to stop any user who +might have access but shouldn't from using the repository while you +fix things). Then go into ~hg/repos/hgadmin, do an "hg update", edit +things to your satisfaction, and commit the change. Finally, run +~/admin/hg-admin-tools/refresh-auth to regenerate +~hg/.ssh/authorized_keys. + THANKS Thanks for reading this far. If you use hg-admin-tools, please tell diff -r 62ee928ac9b3 -r 9fa62cfd2821 access.py --- a/access.py Tue Apr 22 12:55:56 2008 +0100 +++ b/access.py Tue Apr 22 13:03:17 2008 +0100 @@ -21,21 +21,32 @@ def __init__(self, ui, repo): self.ui = ui self.repo = repo - self.repo_path = os.environ['HG_REPO_PATH'] - self.user = os.environ['REMOTE_USER'] self.rules = ruleset.Ruleset.readfile(os.environ['HG_ACCESS_RULES_FILE']) + self.rules.set(user = os.environ['REMOTE_USER']) + self.rules.set(repo = os.environ['HG_REPO_PATH']) + + def allow(self, node): + '''return if access allowed, raise exception if not.''' + ctx = self.repo.changectx(node) + branch = ctx.branch() + if not self.rules.allow("write", branch=branch, file=None): + self.ui.debug(_('%s: user %s not allowed on branch %s\n') % + (__name__, self.user, branch)) + return False + for f in ctx.files(): + if not self.rules.allow("write", branch=branch, file=f): + self.ui.debug(_('%s: user %s not allowed on %s\n') % + (__name__, self.user, f)) + return False + self.ui.debug(_('%s: allowing changeset %s\n') % (__name__, short(node))) + return True 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, repo=self.repo_path, file=f): - 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))) + if not allow(self, node): + raise util.Abort(_('%s: access denied for changeset %s') % + (__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 ' diff -r 62ee928ac9b3 -r 9fa62cfd2821 hg-ssh --- a/hg-ssh Tue Apr 22 12:55:56 2008 +0100 +++ b/hg-ssh Tue Apr 22 13:03:17 2008 +0100 @@ -48,15 +48,15 @@ fail("Disallowing path: %s" % path) return path -def get_cmd(rules, remoteuser, cmd): +def get_cmd(rules, cmd): if cmd.startswith('hg -R ') and cmd.endswith(' serve --stdio'): repo = getpath(cmd[6:-14]) - if rules.allow("read", user=remoteuser, repo=repo, file=None): + if rules.allow("read", repo=repo): os.environ["HG_REPO_PATH"] = repo return ['-R', repo, 'serve', '--stdio'] elif cmd.startswith('hg init '): repo = getpath(cmd[8:]) - if rules.allow("init", user=remoteuser, repo=repo, file=None): + if rules.allow("init", repo=repo): os.environ["HG_REPO_PATH"] = repo return ['init', repo] fail("Illegal command %r" % cmd) @@ -69,8 +69,9 @@ % sys.argv) rules = ruleset.Ruleset.readfile(os.environ['HG_ACCESS_RULES_FILE']) -remoteuser = getpath(os.environ['REMOTE_USER']) -todispatch = get_cmd(rules, remoteuser, +rules.set(remoteuser = getpath(os.environ['REMOTE_USER'])) +rules.set(branch = None, file = None) +todispatch = get_cmd(rules, os.environ.get('SSH_ORIGINAL_COMMAND', '?')) dispatch.dispatch(todispatch) diff -r 62ee928ac9b3 -r 9fa62cfd2821 ruleset.py --- a/ruleset.py Tue Apr 22 12:55:56 2008 +0100 +++ b/ruleset.py Tue Apr 22 13:03:17 2008 +0100 @@ -50,13 +50,20 @@ 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(**kw): + if c(**d): return a return None @@ -78,5 +85,3 @@ finally: f.close() return res - -