narrow: prevent removal of ACL-defined excludes stable
authorArun Kulshreshtha <akulshreshtha@janestreet.com>
Thu, 04 Jan 2024 14:45:31 -0500
branchstable
changeset 51309 9b44b25dece1
parent 51308 bf7c24e12fad
child 51310 f0e7d51bb454
narrow: prevent removal of ACL-defined excludes
hgext/narrow/narrowwirepeer.py
tests/test-narrow-acl-excludes.t
--- a/hgext/narrow/narrowwirepeer.py	Thu Jan 04 14:41:18 2024 -0500
+++ b/hgext/narrow/narrowwirepeer.py	Thu Jan 04 14:45:31 2024 -0500
@@ -6,6 +6,10 @@
 # GNU General Public License version 2 or any later version.
 
 
+from mercurial.i18n import _
+
+from mercurial.utils import stringutil
+
 from mercurial import (
     bundle2,
     error,
@@ -82,20 +86,38 @@
             # work around ''.split(',') => ['']
             return data.split(b',') if data else []
 
-        oldincludes = splitpaths(oldincludes)
-        newincludes = splitpaths(newincludes)
-        oldexcludes = splitpaths(oldexcludes)
-        newexcludes = splitpaths(newexcludes)
+        oldincludes = set(splitpaths(oldincludes))
+        newincludes = set(splitpaths(newincludes))
+        oldexcludes = set(splitpaths(oldexcludes))
+        newexcludes = set(splitpaths(newexcludes))
 
         # enforce narrow acl if set
         if repo.ui.has_section(exchange._NARROWACL_SECTION):
-            exchange.applynarrowacl(repo, {'includepats': newincludes})
+            kwargs = exchange.applynarrowacl(
+                repo, {'includepats': newincludes, 'excludepats': newexcludes}
+            )
+            newincludes = kwargs['includepats']
+            requiredexcludes = kwargs['excludepats'] - newexcludes
+            if requiredexcludes:
+                # XXX: The below code to get the username was copied from exchange.py,
+                # where it is noted that this is technically a layering violation for
+                # assuming the existence of HTTP. Using it anyway to make the error
+                # message consistent with the error message for invalid includes.
+                ui = repo.ui
+                username = ui.shortuser(
+                    ui.environ.get(b'REMOTE_USER') or ui.username()
+                )
+                raise error.Abort(
+                    _(b"The following excludes cannot be removed for %s: %s")
+                    % (username, stringutil.pprint(list(requiredexcludes)))
+                )
+            newexcludes = kwargs['excludepats']
 
         # validate the patterns
-        narrowspec.validatepatterns(set(oldincludes))
-        narrowspec.validatepatterns(set(newincludes))
-        narrowspec.validatepatterns(set(oldexcludes))
-        narrowspec.validatepatterns(set(newexcludes))
+        narrowspec.validatepatterns(oldincludes)
+        narrowspec.validatepatterns(newincludes)
+        narrowspec.validatepatterns(oldexcludes)
+        narrowspec.validatepatterns(newexcludes)
 
         common = wireprototypes.decodelist(commonheads)
         known = wireprototypes.decodelist(known)
--- a/tests/test-narrow-acl-excludes.t	Thu Jan 04 14:41:18 2024 -0500
+++ b/tests/test-narrow-acl-excludes.t	Thu Jan 04 14:45:31 2024 -0500
@@ -64,18 +64,12 @@
   $ hg -R narrowclone1 tracked --removeexclude f3
   comparing with http://localhost:$HGPORT1/
   searching for changes
-  adding changesets
-  adding manifests
-  adding file changes
-  added 0 changesets with 1 changes to 1 files
+  abort: The following excludes cannot be removed for test: ['path:f3']
+  [255]
   $ ls -A -1 narrowclone1 | sort
   .hg
   f1
   f2
-  f3
   $ hg -R narrowclone1 tracked
   I path:.
-
-
-XXX: BUG! This test demonstrates that we are presently
-able to gain access to f3 by removing the exclusion.
+  X path:f3