From patchwork Thu May 5 08:45:55 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: [2,of,2,v2] acl: support revsets [RFC] From: timeless X-Patchwork-Id: 14910 Message-Id: To: mercurial-devel@mercurial-scm.org Date: Thu, 05 May 2016 08:45:55 +0000 # HG changeset patch # User timeless # Date 1462327099 0 # Wed May 04 01:58:19 2016 +0000 # Node ID d4a9c04f6c504f24b7c8e228d5bff61118760573 # Parent c5ad786b58c50a6b751217f88ca1626a19c3ff30 # Available At bb://timeless/mercurial-crew # hg pull bb://timeless/mercurial-crew -r d4a9c04f6c50 acl: support revsets [RFC] This is an attempt to support revsets. It works. I can update the documentation if we want to go this route, and add direct samples to the tests for it. This change makes branch acl items into revset: items, which means there is automatic testing of the revset: path. ---- An unfortunate side-effect of this change is that I've lost the reporting indicating why a commit was rejected. It's possible to re-engineer things so that such reporting could be done. ---- Afaict, it does not help w/ bookmarks at all, as bookmarks aren't applied until after the hook runs. And I don't think one can roll back a bookmark by that point, as I think the previous information would be lost. diff -r c5ad786b58c5 -r d4a9c04f6c50 hgext/acl.py --- a/hgext/acl.py Thu Mar 03 23:29:26 2016 +0000 +++ b/hgext/acl.py Wed May 04 01:58:19 2016 +0000 @@ -216,11 +216,12 @@ from __future__ import absolute_import import getpass +import os from mercurial.i18n import _ from mercurial import ( error, - match, + scmutil, util, ) @@ -277,30 +278,47 @@ ui.debug('acl: %s not enabled\n' % key) return None - pats = [pat for pat, users in ui.configitems(key) + pats = [pat.lstrip() for pat, users in ui.configitems(key) if _usermatch(ui, user, users)] ui.debug('acl: %s enabled, %d entries for user %s\n' % (key, len(pats), user)) + # Arg-based ACL + if not repo: + if not pats: + return util.never + # If there's an asterisk (meaning "any"), always return True; + # Otherwise, test if b is in pats + if '*' in pats: + return util.always + return lambda b: b in pats + # Branch-based ACL - if not repo: - if pats: - # If there's an asterisk (meaning "any branch"), always return True; - # Otherwise, test if b is in pats - if '*' in pats: - return util.always - return lambda b: b in pats + if key.endswith("branches"): + # If there's an asterisk (meaning "any branch"), always return True; + # Otherwise, test if b is in pats + pats = [('revset:branch("%s")' % branch if branch != '*' + else 'revset:all()') + for branch in pats] + # Convert Path based ACL to revsets + revsets = [(pat[7:] if pat.startswith('revset:') else 'file("%s")' % pat) + for pat in pats] + + if not revsets: return util.never - # Path-based ACL - if pats: - return match.match(repo.root, '', pats) - return util.never + # Revset-based ACL + rule = ' + '.join(revsets) + # it might be nice to expose the rule to the client, but + # this API isn't particularly friendly to that. + criteria = '(%s) & (%s)' % (rule, '%s') + return lambda b: scmutil.revrange(repo, [criteria % b]) def hook(ui, repo, hooktype, node=None, source=None, **kwargs): if hooktype not in ['pretxnchangegroup', 'pretxncommit', 'prepushkey']: raise error.Abort(_('config error - hook type "%s" cannot stop ' - 'incoming changesets, commits, nor bookmarks') % hooktype) + 'incoming changesets, commits, nor bookmarks') + % hooktype) if (hooktype == 'pretxnchangegroup' and source not in ui.config('acl', 'sources', 'serve').split()): ui.debug('acl: changes have source "%s" - skipping\n' % source) @@ -347,30 +365,39 @@ ui.readconfig(cfg, sections=['acl.groups', 'acl.allow.branches', 'acl.deny.branches', 'acl.allow', 'acl.deny']) - allowbranches = buildmatch(ui, None, user, 'acl.allow.branches') - denybranches = buildmatch(ui, None, user, 'acl.deny.branches') + allowbranches = buildmatch(ui, repo, user, 'acl.allow.branches') + denybranches = buildmatch(ui, repo, user, 'acl.deny.branches') allow = buildmatch(ui, repo, user, 'acl.allow') deny = buildmatch(ui, repo, user, 'acl.deny') - for rev in xrange(repo[node], len(repo)): - ctx = repo[rev] - branch = ctx.branch() - if denybranches and denybranches(branch): - raise error.Abort(_('acl: user "%s" denied on branch "%s"' + def checkbranches(user, ctx): + if denybranches and denybranches(ctx): + raise error.Abort(_('acl: user "%s" denied' ' (changeset "%s")') - % (user, branch, ctx)) - if allowbranches and not allowbranches(branch): - raise error.Abort(_('acl: user "%s" not allowed on branch "%s"' + % (user, ctx)) + if allowbranches and not allowbranches(ctx): + raise error.Abort(_('acl: user "%s" not allowed' ' (changeset "%s")') - % (user, branch, ctx)) - ui.debug('acl: branch access granted: "%s" on branch "%s"\n' - % (ctx, branch)) + % (user, ctx)) - for f in ctx.files(): - if deny and deny(f): - raise error.Abort(_('acl: user "%s" denied on "%s"' - ' (changeset "%s")') % (user, f, ctx)) - if allow and not allow(f): - raise error.Abort(_('acl: user "%s" not allowed on "%s"' - ' (changeset "%s")') % (user, f, ctx)) - ui.debug('acl: path access granted: "%s"\n' % ctx) + def checkctx(user, ctx): + if deny and deny(ctx): + raise error.Abort(_('acl: user "%s" denied' + ' (changeset "%s")') % (user, ctx)) + if allow and not allow(ctx): + raise error.Abort(_('acl: user "%s" not allowed' + ' (changeset "%s")') % (user, ctx)) + cwd = os.getcwd() + os.chdir(os.path.dirname(repo.path)) + try: + for rev in xrange(repo[node], len(repo)): + ctx = repo[rev] + branch = ctx.branch() + checkbranches(user, ctx) + ui.debug('acl: branch access granted: "%s" on branch "%s"\n' + % (ctx, branch)) + + checkctx(user, ctx) + ui.debug('acl: path access granted: "%s"\n' % ctx) + finally: + os.chdir(cwd) diff -r c5ad786b58c5 -r d4a9c04f6c50 tests/test-acl.t --- a/tests/test-acl.t Thu Mar 03 23:29:26 2016 +0000 +++ b/tests/test-acl.t Wed May 04 01:58:19 2016 +0000 @@ -340,12 +340,12 @@ acl: acl.allow enabled, 0 entries for user fred acl: acl.deny not enabled acl: branch access granted: "ef1ea85a6374" on branch "default" - error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "foo/file.txt" (changeset "ef1ea85a6374") + error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed (changeset "ef1ea85a6374") bundle2-input-part: total payload size 1606 bundle2-input-bundle: 3 parts total transaction abort! rollback completed - abort: acl: user "fred" not allowed on "foo/file.txt" (changeset "ef1ea85a6374") + abort: acl: user "fred" not allowed (changeset "ef1ea85a6374") no rollback information available 0:6675d58eff77 @@ -410,12 +410,12 @@ acl: branch access granted: "f9cafe1212c8" on branch "default" acl: path access granted: "f9cafe1212c8" acl: branch access granted: "911600dab2ae" on branch "default" - error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae") + error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed (changeset "911600dab2ae") bundle2-input-part: total payload size 1606 bundle2-input-bundle: 3 parts total transaction abort! rollback completed - abort: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae") + abort: acl: user "fred" not allowed (changeset "911600dab2ae") no rollback information available 0:6675d58eff77 @@ -477,12 +477,12 @@ acl: acl.allow enabled, 0 entries for user barney acl: acl.deny enabled, 0 entries for user barney acl: branch access granted: "ef1ea85a6374" on branch "default" - error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374") + error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed (changeset "ef1ea85a6374") bundle2-input-part: total payload size 1606 bundle2-input-bundle: 3 parts total transaction abort! rollback completed - abort: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374") + abort: acl: user "barney" not allowed (changeset "ef1ea85a6374") no rollback information available 0:6675d58eff77 @@ -549,12 +549,12 @@ acl: branch access granted: "f9cafe1212c8" on branch "default" acl: path access granted: "f9cafe1212c8" acl: branch access granted: "911600dab2ae" on branch "default" - error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae") + error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed (changeset "911600dab2ae") bundle2-input-part: total payload size 1606 bundle2-input-bundle: 3 parts total transaction abort! rollback completed - abort: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae") + abort: acl: user "fred" not allowed (changeset "911600dab2ae") no rollback information available 0:6675d58eff77 @@ -620,12 +620,12 @@ acl: branch access granted: "ef1ea85a6374" on branch "default" acl: path access granted: "ef1ea85a6374" acl: branch access granted: "f9cafe1212c8" on branch "default" - error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8") + error: pretxnchangegroup.acl hook failed: acl: user "fred" denied (changeset "f9cafe1212c8") bundle2-input-part: total payload size 1606 bundle2-input-bundle: 3 parts total transaction abort! rollback completed - abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8") + abort: acl: user "fred" denied (changeset "f9cafe1212c8") no rollback information available 0:6675d58eff77 @@ -688,12 +688,12 @@ acl: acl.allow enabled, 0 entries for user barney acl: acl.deny enabled, 0 entries for user barney acl: branch access granted: "ef1ea85a6374" on branch "default" - error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374") + error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed (changeset "ef1ea85a6374") bundle2-input-part: total payload size 1606 bundle2-input-bundle: 3 parts total transaction abort! rollback completed - abort: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374") + abort: acl: user "barney" not allowed (changeset "ef1ea85a6374") no rollback information available 0:6675d58eff77 @@ -754,8 +754,8 @@ acl: acl.allow enabled, 1 entries for user fred acl: acl.deny enabled, 2 entries for user fred acl: branch access granted: "ef1ea85a6374" on branch "default" + invalid branchheads cache (served): tip differs acl: path access granted: "ef1ea85a6374" - invalid branchheads cache (served): tip differs bundle2-input-part: total payload size 537 bundle2-input-part: "pushkey" (params: 4 mandatory) supported calling hook prepushkey.acl: hgext.acl.hook @@ -841,8 +841,8 @@ acl: acl.allow enabled, 1 entries for user fred acl: acl.deny enabled, 2 entries for user fred acl: branch access granted: "ef1ea85a6374" on branch "default" + invalid branchheads cache (served): tip differs acl: path access granted: "ef1ea85a6374" - invalid branchheads cache (served): tip differs bundle2-input-part: total payload size 537 bundle2-input-part: "pushkey" (params: 4 mandatory) supported calling hook prepushkey.acl: hgext.acl.hook @@ -1020,12 +1020,12 @@ acl: branch access granted: "f9cafe1212c8" on branch "default" acl: path access granted: "f9cafe1212c8" acl: branch access granted: "911600dab2ae" on branch "default" - error: pretxnchangegroup.acl hook failed: acl: user "wilma" not allowed on "quux/file.py" (changeset "911600dab2ae") + error: pretxnchangegroup.acl hook failed: acl: user "wilma" not allowed (changeset "911600dab2ae") bundle2-input-part: total payload size 1606 bundle2-input-bundle: 3 parts total transaction abort! rollback completed - abort: acl: user "wilma" not allowed on "quux/file.py" (changeset "911600dab2ae") + abort: acl: user "wilma" not allowed (changeset "911600dab2ae") no rollback information available 0:6675d58eff77 @@ -1173,12 +1173,12 @@ acl: branch access granted: "f9cafe1212c8" on branch "default" acl: path access granted: "f9cafe1212c8" acl: branch access granted: "911600dab2ae" on branch "default" - error: pretxnchangegroup.acl hook failed: acl: user "betty" not allowed on "quux/file.py" (changeset "911600dab2ae") + error: pretxnchangegroup.acl hook failed: acl: user "betty" not allowed (changeset "911600dab2ae") bundle2-input-part: total payload size 1606 bundle2-input-bundle: 3 parts total transaction abort! rollback completed - abort: acl: user "betty" not allowed on "quux/file.py" (changeset "911600dab2ae") + abort: acl: user "betty" not allowed (changeset "911600dab2ae") no rollback information available 0:6675d58eff77 @@ -1430,12 +1430,12 @@ acl: branch access granted: "ef1ea85a6374" on branch "default" acl: path access granted: "ef1ea85a6374" acl: branch access granted: "f9cafe1212c8" on branch "default" - error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8") + error: pretxnchangegroup.acl hook failed: acl: user "fred" denied (changeset "f9cafe1212c8") bundle2-input-part: total payload size 1606 bundle2-input-bundle: 3 parts total transaction abort! rollback completed - abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8") + abort: acl: user "fred" denied (changeset "f9cafe1212c8") no rollback information available 0:6675d58eff77 @@ -1595,12 +1595,12 @@ acl: branch access granted: "ef1ea85a6374" on branch "default" acl: path access granted: "ef1ea85a6374" acl: branch access granted: "f9cafe1212c8" on branch "default" - error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8") + error: pretxnchangegroup.acl hook failed: acl: user "fred" denied (changeset "f9cafe1212c8") bundle2-input-part: total payload size 1606 bundle2-input-bundle: 3 parts total transaction abort! rollback completed - abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8") + abort: acl: user "fred" denied (changeset "f9cafe1212c8") no rollback information available 0:6675d58eff77 @@ -1808,12 +1808,12 @@ acl: path access granted: "f9cafe1212c8" acl: branch access granted: "911600dab2ae" on branch "default" acl: path access granted: "911600dab2ae" - error: pretxnchangegroup.acl hook failed: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82") + error: pretxnchangegroup.acl hook failed: acl: user "astro" denied (changeset "e8fc755d4d82") bundle2-input-part: total payload size 2101 bundle2-input-bundle: 4 parts total transaction abort! rollback completed - abort: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82") + abort: acl: user "astro" denied (changeset "e8fc755d4d82") no rollback information available 2:fb35475503ef @@ -1840,6 +1840,7 @@ listing keys for "phases" checking for updated bookmarks listing keys for "bookmarks" + invalid branchheads cache (served): tip differs listing keys for "bookmarks" 4 changesets found list of changesets: @@ -1877,12 +1878,12 @@ acl: acl.deny.branches not enabled acl: acl.allow not enabled acl: acl.deny not enabled - error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374") + error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed (changeset "ef1ea85a6374") bundle2-input-part: total payload size 2101 bundle2-input-bundle: 4 parts total transaction abort! rollback completed - abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374") + abort: acl: user "astro" not allowed (changeset "ef1ea85a6374") no rollback information available 2:fb35475503ef @@ -1948,12 +1949,12 @@ acl: acl.deny.branches not enabled acl: acl.allow not enabled acl: acl.deny not enabled - error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374") + error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed (changeset "ef1ea85a6374") bundle2-input-part: total payload size 2101 bundle2-input-bundle: 4 parts total transaction abort! rollback completed - abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374") + abort: acl: user "astro" not allowed (changeset "ef1ea85a6374") no rollback information available 2:fb35475503ef @@ -2208,12 +2209,12 @@ acl: acl.deny.branches enabled, 1 entries for user george acl: acl.allow not enabled acl: acl.deny not enabled - error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374") + error: pretxnchangegroup.acl hook failed: acl: user "george" denied (changeset "ef1ea85a6374") bundle2-input-part: total payload size 2101 bundle2-input-bundle: 4 parts total transaction abort! rollback completed - abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374") + abort: acl: user "george" denied (changeset "ef1ea85a6374") no rollback information available 2:fb35475503ef @@ -2241,6 +2242,7 @@ listing keys for "phases" checking for updated bookmarks listing keys for "bookmarks" + invalid branchheads cache (served): tip differs listing keys for "bookmarks" 4 changesets found list of changesets: @@ -2369,12 +2371,12 @@ acl: acl.deny.branches enabled, 1 entries for user george acl: acl.allow not enabled acl: acl.deny not enabled - error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374") + error: pretxnchangegroup.acl hook failed: acl: user "george" denied (changeset "ef1ea85a6374") bundle2-input-part: total payload size 2101 bundle2-input-bundle: 4 parts total transaction abort! rollback completed - abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374") + abort: acl: user "george" denied (changeset "ef1ea85a6374") no rollback information available 2:fb35475503ef