[PATCH 2 of 2 v2] acl: support revsets [RFC]

timeless timeless at fmr.im
Thu May 5 04:45:55 EDT 2016


# HG changeset patch
# User timeless <timeless at mozdev.org>
# 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
   


More information about the Mercurial-devel mailing list