changeset 38533:6beb8347b709

acl: add bookmarks support Originally submitted at https://www.mercurial-scm.org/pipermail/mercurial-devel/2016-March/080650.html as an RFC by timeless. .. feature:: The `acl` extension now has support for bookmarks as well as branches. Differential Revision: https://phab.mercurial-scm.org/D3750
author Sandu Turcan <idlsoft@gmail.com>
date Fri, 15 Jun 2018 14:07:13 -0400
parents c82ea938efbb
children 9ee202679a76
files hgext/acl.py tests/test-acl.t
diffstat 2 files changed, 251 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/hgext/acl.py	Sat Jun 30 18:34:33 2018 -0700
+++ b/hgext/acl.py	Fri Jun 15 14:07:13 2018 -0400
@@ -57,6 +57,28 @@
 a glob syntax by default). The corresponding values follow the same
 syntax as the other sections above.
 
+Bookmark-based Access Control
+-----------------------------
+Use the ``acl.deny.bookmarks`` and ``acl.allow.bookmarks`` sections to
+have bookmark-based access control. Keys in these sections can be
+either:
+
+- a bookmark name, or
+- an asterisk, to match any bookmark;
+
+The corresponding values can be either:
+
+- a comma-separated list containing users and groups, or
+- an asterisk, to match anyone;
+
+You can add the "!" prefix to a user or group name to invert the sense
+of the match.
+
+Note: for interactions between clients and servers using Mercurial 3.6+
+a rejection will generally reject the entire push, for interactions
+involving older clients, the commit transactions will already be accepted,
+and only the bookmark movement will be rejected.
+
 Groups
 ------
 
@@ -326,9 +348,10 @@
 
     ensureenabled(ui)
 
-    if hooktype not in ['pretxnchangegroup', 'pretxncommit']:
-        raise error.Abort(_('config error - hook type "%s" cannot stop '
-                           'incoming changesets nor commits') % hooktype)
+    if hooktype not in ['pretxnchangegroup', 'pretxncommit', 'prepushkey']:
+        raise error.Abort(
+            _('config error - hook type "%s" cannot stop '
+              'incoming changesets, commits, nor bookmarks') % hooktype)
     if (hooktype == 'pretxnchangegroup' and
         source not in ui.configlist('acl', 'sources')):
         ui.debug('acl: changes have source "%s" - skipping\n' % source)
@@ -345,6 +368,30 @@
 
     ui.debug('acl: checking access for user "%s"\n' % user)
 
+    if hooktype == 'prepushkey':
+        _pkhook(ui, repo, hooktype, node, source, user, **kwargs)
+    else:
+        _txnhook(ui, repo, hooktype, node, source, user, **kwargs)
+
+def _pkhook(ui, repo, hooktype, node, source, user, **kwargs):
+    if kwargs['namespace'] == 'bookmarks':
+        bookmark = kwargs['key']
+        ctx = kwargs['new']
+        allowbookmarks = buildmatch(ui, None, user, 'acl.allow.bookmarks')
+        denybookmarks = buildmatch(ui, None, user, 'acl.deny.bookmarks')
+
+        if denybookmarks and denybookmarks(bookmark):
+            raise error.Abort(_('acl: user "%s" denied on bookmark "%s"'
+                               ' (changeset "%s")')
+                               % (user, bookmark, ctx))
+        if allowbookmarks and not allowbookmarks(bookmark):
+            raise error.Abort(_('acl: user "%s" not allowed on bookmark "%s"'
+                               ' (changeset "%s")')
+                               % (user, bookmark, ctx))
+        ui.debug('acl: bookmark access granted: "%s" on bookmark "%s"\n'
+                 % (ctx, bookmark))
+
+def _txnhook(ui, repo, hooktype, node, source, user, **kwargs):
     # deprecated config: acl.config
     cfg = ui.config('acl', 'config')
     if cfg:
--- a/tests/test-acl.t	Sat Jun 30 18:34:33 2018 -0700
+++ b/tests/test-acl.t	Fri Jun 15 14:07:13 2018 -0400
@@ -15,7 +15,7 @@
   >     #  LOGNAME=$user hg --cws a --debug push ../b
   >     # fails with "This variable is read only."
   >     # Use env to work around this.
-  >     env LOGNAME=$user hg --cwd a --debug push ../b
+  >     env LOGNAME=$user hg --cwd a --debug push ../b $*
   >     hg --cwd b rollback
   >     hg --cwd b --quiet tip
   >     echo
@@ -47,6 +47,7 @@
   >     cat > $config <<EOF
   > [hooks]
   > pretxnchangegroup.acl = python:hgext.acl.hook
+  > prepushkey.acl = python:hgext.acl.hook
   > [acl]
   > sources = push
   > [extensions]
@@ -148,6 +149,7 @@
 
   $ echo '[hooks]' >> $config
   $ echo 'pretxnchangegroup.acl = python:hgext.acl.hook' >> $config
+  $ echo 'prepushkey.acl = python:hgext.acl.hook' >> $config
 
 Extension disabled for lack of acl.sources
 
@@ -156,6 +158,7 @@
   hgrc = """
   [hooks]
   pretxnchangegroup.acl = python:hgext.acl.hook
+  prepushkey.acl = python:hgext.acl.hook
   """
   pushing to ../b
   query 1; heads
@@ -220,6 +223,7 @@
   hgrc = """
   [hooks]
   pretxnchangegroup.acl = python:hgext.acl.hook
+  prepushkey.acl = python:hgext.acl.hook
   [acl]
   sources = push
   """
@@ -295,6 +299,7 @@
   hgrc = """
   [hooks]
   pretxnchangegroup.acl = python:hgext.acl.hook
+  prepushkey.acl = python:hgext.acl.hook
   [acl]
   sources = push
   [acl.allow]
@@ -362,6 +367,7 @@
   hgrc = """
   [hooks]
   pretxnchangegroup.acl = python:hgext.acl.hook
+  prepushkey.acl = python:hgext.acl.hook
   [acl]
   sources = push
   [acl.allow]
@@ -434,6 +440,7 @@
   hgrc = """
   [hooks]
   pretxnchangegroup.acl = python:hgext.acl.hook
+  prepushkey.acl = python:hgext.acl.hook
   [acl]
   sources = push
   [acl.allow]
@@ -503,6 +510,7 @@
   hgrc = """
   [hooks]
   pretxnchangegroup.acl = python:hgext.acl.hook
+  prepushkey.acl = python:hgext.acl.hook
   [acl]
   sources = push
   [acl.allow]
@@ -577,6 +585,7 @@
   hgrc = """
   [hooks]
   pretxnchangegroup.acl = python:hgext.acl.hook
+  prepushkey.acl = python:hgext.acl.hook
   [acl]
   sources = push
   [acl.allow]
@@ -649,6 +658,7 @@
   hgrc = """
   [hooks]
   pretxnchangegroup.acl = python:hgext.acl.hook
+  prepushkey.acl = python:hgext.acl.hook
   [acl]
   sources = push
   [acl.allow]
@@ -712,6 +722,178 @@
   0:6675d58eff77
   
 
+fred is not blocked from moving bookmarks
+
+  $ hg -R a book -q moving-bookmark -r 1
+  $ hg -R b book -q moving-bookmark -r 0
+  $ cp $config normalconfig
+  $ do_push fred -r 1
+  Pushing as user fred
+  hgrc = """
+  [hooks]
+  pretxnchangegroup.acl = python:hgext.acl.hook
+  prepushkey.acl = python:hgext.acl.hook
+  [acl]
+  sources = push
+  [acl.allow]
+  foo/** = fred
+  [acl.deny]
+  foo/bar/** = fred
+  foo/Bar/** = fred
+  """
+  pushing to ../b
+  query 1; heads
+  searching for changes
+  all remote heads known locally
+  listing keys for "phases"
+  checking for updated bookmarks
+  listing keys for "bookmarks"
+  listing keys for "bookmarks"
+  1 changesets found
+  list of changesets:
+  ef1ea85a6374b77d6da9dcda9541f498f2d17df7
+  bundle2-output-bundle: "HG20", 7 parts total
+  bundle2-output-part: "replycaps" 205 bytes payload
+  bundle2-output-part: "check:bookmarks" 37 bytes payload
+  bundle2-output-part: "check:phases" 24 bytes payload
+  bundle2-output-part: "check:heads" streamed payload
+  bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
+  bundle2-output-part: "phase-heads" 24 bytes payload
+  bundle2-output-part: "bookmarks" 37 bytes payload
+  bundle2-input-bundle: with-transaction
+  bundle2-input-part: "replycaps" supported
+  bundle2-input-part: total payload size 205
+  bundle2-input-part: "check:bookmarks" supported
+  bundle2-input-part: total payload size 37
+  bundle2-input-part: "check:phases" supported
+  bundle2-input-part: total payload size 24
+  bundle2-input-part: "check:heads" supported
+  bundle2-input-part: total payload size 20
+  bundle2-input-part: "changegroup" (params: 1 mandatory) supported
+  adding changesets
+  add changeset ef1ea85a6374
+  adding manifests
+  adding file changes
+  adding foo/file.txt revisions
+  added 1 changesets with 1 changes to 1 files
+  calling hook pretxnchangegroup.acl: hgext.acl.hook
+  acl: checking access for user "fred"
+  acl: acl.allow.branches not enabled
+  acl: acl.deny.branches not enabled
+  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"
+  acl: path access granted: "ef1ea85a6374"
+  bundle2-input-part: total payload size 520
+  bundle2-input-part: "phase-heads" supported
+  bundle2-input-part: total payload size 24
+  bundle2-input-part: "bookmarks" supported
+  bundle2-input-part: total payload size 37
+  calling hook prepushkey.acl: hgext.acl.hook
+  acl: checking access for user "fred"
+  acl: acl.allow.bookmarks not enabled
+  acl: acl.deny.bookmarks not enabled
+  acl: bookmark access granted: "ef1ea85a6374b77d6da9dcda9541f498f2d17df7" on bookmark "moving-bookmark"
+  bundle2-input-bundle: 6 parts total
+  updating the branch cache
+  bundle2-output-bundle: "HG20", 1 parts total
+  bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
+  bundle2-input-bundle: no-transaction
+  bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
+  bundle2-input-bundle: 0 parts total
+  updating bookmark moving-bookmark
+  listing keys for "phases"
+  repository tip rolled back to revision 0 (undo push)
+  0:6675d58eff77
+  
+
+fred is not allowed to move bookmarks
+
+  $ echo '[acl.deny.bookmarks]' >> $config
+  $ echo '* = fred' >> $config
+  $ do_push fred -r 1
+  Pushing as user fred
+  hgrc = """
+  [hooks]
+  pretxnchangegroup.acl = python:hgext.acl.hook
+  prepushkey.acl = python:hgext.acl.hook
+  [acl]
+  sources = push
+  [acl.allow]
+  foo/** = fred
+  [acl.deny]
+  foo/bar/** = fred
+  foo/Bar/** = fred
+  [acl.deny.bookmarks]
+  * = fred
+  """
+  pushing to ../b
+  query 1; heads
+  searching for changes
+  all remote heads known locally
+  listing keys for "phases"
+  checking for updated bookmarks
+  listing keys for "bookmarks"
+  listing keys for "bookmarks"
+  1 changesets found
+  list of changesets:
+  ef1ea85a6374b77d6da9dcda9541f498f2d17df7
+  bundle2-output-bundle: "HG20", 7 parts total
+  bundle2-output-part: "replycaps" 205 bytes payload
+  bundle2-output-part: "check:bookmarks" 37 bytes payload
+  bundle2-output-part: "check:phases" 24 bytes payload
+  bundle2-output-part: "check:heads" streamed payload
+  bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
+  bundle2-output-part: "phase-heads" 24 bytes payload
+  bundle2-output-part: "bookmarks" 37 bytes payload
+  bundle2-input-bundle: with-transaction
+  bundle2-input-part: "replycaps" supported
+  bundle2-input-part: total payload size 205
+  bundle2-input-part: "check:bookmarks" supported
+  bundle2-input-part: total payload size 37
+  bundle2-input-part: "check:phases" supported
+  bundle2-input-part: total payload size 24
+  bundle2-input-part: "check:heads" supported
+  bundle2-input-part: total payload size 20
+  bundle2-input-part: "changegroup" (params: 1 mandatory) supported
+  adding changesets
+  add changeset ef1ea85a6374
+  adding manifests
+  adding file changes
+  adding foo/file.txt revisions
+  added 1 changesets with 1 changes to 1 files
+  calling hook pretxnchangegroup.acl: hgext.acl.hook
+  acl: checking access for user "fred"
+  acl: acl.allow.branches not enabled
+  acl: acl.deny.branches not enabled
+  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"
+  acl: path access granted: "ef1ea85a6374"
+  bundle2-input-part: total payload size 520
+  bundle2-input-part: "phase-heads" supported
+  bundle2-input-part: total payload size 24
+  bundle2-input-part: "bookmarks" supported
+  bundle2-input-part: total payload size 37
+  calling hook prepushkey.acl: hgext.acl.hook
+  acl: checking access for user "fred"
+  acl: acl.allow.bookmarks not enabled
+  acl: acl.deny.bookmarks enabled, 1 entries for user fred
+  error: prepushkey.acl hook failed: acl: user "fred" denied on bookmark "moving-bookmark" (changeset "ef1ea85a6374b77d6da9dcda9541f498f2d17df7")
+  bundle2-input-bundle: 6 parts total
+  transaction abort!
+  rollback completed
+  abort: acl: user "fred" denied on bookmark "moving-bookmark" (changeset "ef1ea85a6374b77d6da9dcda9541f498f2d17df7")
+  no rollback information available
+  0:6675d58eff77
+  
+
+cleanup bookmark stuff
+
+  $ hg book -R a -d moving-bookmark
+  $ hg book -R b -d moving-bookmark
+  $ cp normalconfig $config
+
 barney is allowed everywhere
 
   $ echo '[acl.allow]' >> $config
@@ -721,6 +903,7 @@
   hgrc = """
   [hooks]
   pretxnchangegroup.acl = python:hgext.acl.hook
+  prepushkey.acl = python:hgext.acl.hook
   [acl]
   sources = push
   [acl.allow]
@@ -803,6 +986,7 @@
   hgrc = """
   [hooks]
   pretxnchangegroup.acl = python:hgext.acl.hook
+  prepushkey.acl = python:hgext.acl.hook
   [acl]
   sources = push
   [acl.allow]
@@ -882,6 +1066,7 @@
   hgrc = """
   [hooks]
   pretxnchangegroup.acl = python:hgext.acl.hook
+  prepushkey.acl = python:hgext.acl.hook
   [acl]
   sources = push
   [acl.allow]
@@ -954,6 +1139,7 @@
   hgrc = """
   [hooks]
   pretxnchangegroup.acl = python:hgext.acl.hook
+  prepushkey.acl = python:hgext.acl.hook
   [acl]
   sources = push
   [acl.allow]
@@ -1039,6 +1225,7 @@
   hgrc = """
   [hooks]
   pretxnchangegroup.acl = python:hgext.acl.hook
+  prepushkey.acl = python:hgext.acl.hook
   [acl]
   sources = push
   [acl.allow]
@@ -1138,6 +1325,7 @@
   hgrc = """
   [hooks]
   pretxnchangegroup.acl = python:hgext.acl.hook
+  prepushkey.acl = python:hgext.acl.hook
   [acl]
   sources = push
   [extensions]
@@ -1219,6 +1407,7 @@
   hgrc = """
   [hooks]
   pretxnchangegroup.acl = python:hgext.acl.hook
+  prepushkey.acl = python:hgext.acl.hook
   [acl]
   sources = push
   [extensions]
@@ -1301,6 +1490,7 @@
   hgrc = """
   [hooks]
   pretxnchangegroup.acl = python:hgext.acl.hook
+  prepushkey.acl = python:hgext.acl.hook
   [acl]
   sources = push
   [extensions]
@@ -1383,6 +1573,7 @@
   hgrc = """
   [hooks]
   pretxnchangegroup.acl = python:hgext.acl.hook
+  prepushkey.acl = python:hgext.acl.hook
   [acl]
   sources = push
   [extensions]
@@ -1507,6 +1698,7 @@
   hgrc = """
   [hooks]
   pretxnchangegroup.acl = python:hgext.acl.hook
+  prepushkey.acl = python:hgext.acl.hook
   [acl]
   sources = push
   [extensions]
@@ -1590,6 +1782,7 @@
   hgrc = """
   [hooks]
   pretxnchangegroup.acl = python:hgext.acl.hook
+  prepushkey.acl = python:hgext.acl.hook
   [acl]
   sources = push
   [extensions]
@@ -1669,6 +1862,7 @@
   hgrc = """
   [hooks]
   pretxnchangegroup.acl = python:hgext.acl.hook
+  prepushkey.acl = python:hgext.acl.hook
   [acl]
   sources = push
   [extensions]
@@ -1742,6 +1936,7 @@
   hgrc = """
   [hooks]
   pretxnchangegroup.acl = python:hgext.acl.hook
+  prepushkey.acl = python:hgext.acl.hook
   [acl]
   sources = push
   [extensions]
@@ -1810,6 +2005,7 @@
   hgrc = """
   [hooks]
   pretxnchangegroup.acl = python:hgext.acl.hook
+  prepushkey.acl = python:hgext.acl.hook
   [acl]
   sources = push
   [extensions]
@@ -1899,6 +2095,7 @@
   hgrc = """
   [hooks]
   pretxnchangegroup.acl = python:hgext.acl.hook
+  prepushkey.acl = python:hgext.acl.hook
   [acl]
   sources = push
   [extensions]
@@ -1987,6 +2184,7 @@
   hgrc = """
   [hooks]
   pretxnchangegroup.acl = python:hgext.acl.hook
+  prepushkey.acl = python:hgext.acl.hook
   [acl]
   sources = push
   [extensions]
@@ -2062,6 +2260,7 @@
   hgrc = """
   [hooks]
   pretxnchangegroup.acl = python:hgext.acl.hook
+  prepushkey.acl = python:hgext.acl.hook
   [acl]
   sources = push
   [extensions]
@@ -2145,6 +2344,7 @@
   hgrc = """
   [hooks]
   pretxnchangegroup.acl = python:hgext.acl.hook
+  prepushkey.acl = python:hgext.acl.hook
   [acl]
   sources = push
   [extensions]