hgext/acl.py
changeset 38531 6beb8347b709
parent 37120 a8a902d7176e
child 38783 e7aa113b14f7
--- 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: