[PATCH - BACL] Branch access control extension
Elifarley Callado Coelho Cruz
elifarley at gmail.com
Tue Apr 13 12:24:05 CDT 2010
Here's an example configuration:
[hooks]
pretxncommit.acl = python:hgext.acl.hook
pretxnchangegroup.acl = python:hgext.acl.hook
[acl]
# Works as before.
# Branch checks are performed before file checks.
sources = serve
[acl.deny.branches]
# acl.deny.branches is checked first.
# Everyone is denied to the frozen branch:
#frozen-branch = *
# A bad user is denied on all branches:
#* = bad-user
[acl.allow.branches]
# A few users are allowed on branch-a:
#branch-a = user-1, user-2, user-3
# Only one user is allowed on branch-b:
#branch-b = user-1
# The super user is allowed on any branch:
#* = super-user
# Everyone is allowed on branch-for-tests:
#branch-for-tests = *
[acl.deny]
# Works as before.
[acl.allow]
# Works as before.
-------
And here's the patch (taken from
http://bitbucket.org/elifarley/hg-ecc/changeset/2816980e6012/raw/hg-ecc-2816980e6012.diff
):
-------
# HG changeset patch -- Bitbucket.org
# Project hg-ecc
# URL http://bitbucket.org/elifarley/hg-ecc/overview/
# User Elifarley Callado Coelho Cruz <elifarley at gmail.com>
# Date 1271165117 10800
# Node ID 2816980e6012a30d3a225437e76a78130117e753
# Parent 17fd494d0792452204d82b1ea9df25500a310041
Added support for branch access control. Now it is possible to allow
or deny write access to named branches of a repository when receiving
incoming changesets (via 'pretxncommit' and 'pretxnchangegroup').
--- a/acl.py
+++ b/acl.py
@@ -56,7 +56,61 @@ from mercurial.i18n import _
from mercurial import util, match
import getpass, urllib
-def buildmatch(ui, repo, user, key):
+ALLOW_BRANCHES = 'acl.allow.branches'
+DENY_BRANCHES = 'acl.deny.branches'
+
+def hook(ui, repo, hooktype, node=None, source=None, **kwargs):
+ hook_branches(ui, repo, hooktype, node, source, **kwargs)
+ hook_files(ui, repo, hooktype, node, source, **kwargs)
+
+def buildmatch_branches(ui, user, key):
+ '''return tuple of (match function, list enabled).'''
+ if not ui.has_section(key):
+ ui.debug('bacl: "%s" not enabled\n' % key)
+ return None
+
+ branches = [branch for branch, users in ui.configitems(key)
+ if users == '*' or user in users.replace(',', ' ').split()]
+ ui.debug('bacl: "%s" enabled, %d entries for user "%s"\n' %
+ (key, len(branches), user))
+ if branches:
+ return lambda b: '*' in branches or b in branches
+ return lambda b: False
+
+def hook_branches(ui, repo, hooktype, node=None, source=None, **kwargs):
+ if hooktype not in ['pretxnchangegroup', 'pretxncommit']:
+ raise util.Abort(_('config error - hook_branches type "%s"
cannot stop '
+ 'incoming changesets nor commits') % hooktype)
+ if 'pretxnchangegroup' == hooktype and source not in
ui.config('acl', 'sources', 'serve').split():
+ ui.debug('bacl: changes have source "%s" - skipping\n' % source)
+ return
+
+ user = None
+ if source == 'serve' and 'url' in kwargs:
+ url = kwargs['url'].split(':')
+ if url[0] == 'remote' and url[1].startswith('http'):
+ user = urllib.unquote(url[3])
+ ui.debug('bacl: got user "%s" from url.\n' % user)
+
+ if user is None:
+ user = getpass.getuser()
+ ui.debug('bacl: got user "%s" from getpass.\n')
+
+ cfg = ui.config('acl', 'config')
+ if cfg:
+ ui.readconfig(cfg, sections = [ALLOW_BRANCHES, DENY_BRANCHES])
+ allow = buildmatch_branches(ui, user, ALLOW_BRANCHES)
+ deny = buildmatch_branches(ui, user, DENY_BRANCHES)
+
+ for rev in xrange(repo[node], len(repo)):
+ branch = repo[rev].branch()
+ if deny and deny(branch):
+ raise util.Abort(_('bacl: user "%s" denied on branch
"%s"') % (user, branch))
+ if allow and not allow(branch):
+ raise util.Abort(_('bacl: user "%s" not allowed on branch
"%s"') % (user, branch))
+ ui.debug('bacl: allowing user "%s" on branch "%s"\n' % (user, branch))
+
+def buildmatch_files(ui, repo, user, key):
'''return tuple of (match function, list enabled).'''
if not ui.has_section(key):
ui.debug('acl: %s not enabled\n' % key)
@@ -70,8 +124,7 @@ def buildmatch(ui, repo, user, key):
return match.match(repo.root, '', pats)
return match.exact(repo.root, '', [])
-
-def hook(ui, repo, hooktype, node=None, source=None, **kwargs):
+def hook_files(ui, repo, hooktype, node=None, source=None, **kwargs):
if hooktype != 'pretxnchangegroup':
raise util.Abort(_('config error - hook type "%s" cannot stop '
'incoming changesets') % hooktype)
@@ -91,8 +144,8 @@ def hook(ui, repo, hooktype, node=None,
cfg = ui.config('acl', 'config')
if cfg:
ui.readconfig(cfg, sections = ['acl.allow', 'acl.deny'])
- allow = buildmatch(ui, repo, user, 'acl.allow')
- deny = buildmatch(ui, repo, user, 'acl.deny')
+ allow = buildmatch_files(ui, repo, user, 'acl.allow')
+ deny = buildmatch_files(ui, repo, user, 'acl.deny')
for rev in xrange(repo[node], len(repo)):
ctx = repo[rev]
More information about the Mercurial-devel
mailing list