##// END OF EJS Templates
narrow: prevent removal of ACL-defined excludes
Arun Kulshreshtha -
r52202:9b44b25d stable
parent child Browse files
Show More
@@ -1,160 +1,182 b''
1 # narrowwirepeer.py - passes narrow spec with unbundle command
1 # narrowwirepeer.py - passes narrow spec with unbundle command
2 #
2 #
3 # Copyright 2017 Google, Inc.
3 # Copyright 2017 Google, Inc.
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8
8
9 from mercurial.i18n import _
10
11 from mercurial.utils import stringutil
12
9 from mercurial import (
13 from mercurial import (
10 bundle2,
14 bundle2,
11 error,
15 error,
12 exchange,
16 exchange,
13 extensions,
17 extensions,
14 hg,
18 hg,
15 narrowspec,
19 narrowspec,
16 wireprototypes,
20 wireprototypes,
17 wireprotov1peer,
21 wireprotov1peer,
18 wireprotov1server,
22 wireprotov1server,
19 )
23 )
20
24
21 from . import narrowbundle2
25 from . import narrowbundle2
22
26
23
27
24 def uisetup():
28 def uisetup():
25 wireprotov1peer.wirepeer.narrow_widen = peernarrowwiden
29 wireprotov1peer.wirepeer.narrow_widen = peernarrowwiden
26
30
27
31
28 def reposetup(repo):
32 def reposetup(repo):
29 def wirereposetup(ui, peer):
33 def wirereposetup(ui, peer):
30 def wrapped(orig, cmd, *args, **kwargs):
34 def wrapped(orig, cmd, *args, **kwargs):
31 if cmd == b'unbundle':
35 if cmd == b'unbundle':
32 # TODO: don't blindly add include/exclude wireproto
36 # TODO: don't blindly add include/exclude wireproto
33 # arguments to unbundle.
37 # arguments to unbundle.
34 include, exclude = repo.narrowpats
38 include, exclude = repo.narrowpats
35 kwargs["includepats"] = b','.join(include)
39 kwargs["includepats"] = b','.join(include)
36 kwargs["excludepats"] = b','.join(exclude)
40 kwargs["excludepats"] = b','.join(exclude)
37 return orig(cmd, *args, **kwargs)
41 return orig(cmd, *args, **kwargs)
38
42
39 extensions.wrapfunction(peer, '_calltwowaystream', wrapped)
43 extensions.wrapfunction(peer, '_calltwowaystream', wrapped)
40
44
41 hg.wirepeersetupfuncs.append(wirereposetup)
45 hg.wirepeersetupfuncs.append(wirereposetup)
42
46
43
47
44 @wireprotov1server.wireprotocommand(
48 @wireprotov1server.wireprotocommand(
45 b'narrow_widen',
49 b'narrow_widen',
46 b'oldincludes oldexcludes'
50 b'oldincludes oldexcludes'
47 b' newincludes newexcludes'
51 b' newincludes newexcludes'
48 b' commonheads cgversion'
52 b' commonheads cgversion'
49 b' known ellipses',
53 b' known ellipses',
50 permission=b'pull',
54 permission=b'pull',
51 )
55 )
52 def narrow_widen(
56 def narrow_widen(
53 repo,
57 repo,
54 proto,
58 proto,
55 oldincludes,
59 oldincludes,
56 oldexcludes,
60 oldexcludes,
57 newincludes,
61 newincludes,
58 newexcludes,
62 newexcludes,
59 commonheads,
63 commonheads,
60 cgversion,
64 cgversion,
61 known,
65 known,
62 ellipses,
66 ellipses,
63 ):
67 ):
64 """wireprotocol command to send data when a narrow clone is widen. We will
68 """wireprotocol command to send data when a narrow clone is widen. We will
65 be sending a changegroup here.
69 be sending a changegroup here.
66
70
67 The current set of arguments which are required:
71 The current set of arguments which are required:
68 oldincludes: the old includes of the narrow copy
72 oldincludes: the old includes of the narrow copy
69 oldexcludes: the old excludes of the narrow copy
73 oldexcludes: the old excludes of the narrow copy
70 newincludes: the new includes of the narrow copy
74 newincludes: the new includes of the narrow copy
71 newexcludes: the new excludes of the narrow copy
75 newexcludes: the new excludes of the narrow copy
72 commonheads: list of heads which are common between the server and client
76 commonheads: list of heads which are common between the server and client
73 cgversion(maybe): the changegroup version to produce
77 cgversion(maybe): the changegroup version to produce
74 known: list of nodes which are known on the client (used in ellipses cases)
78 known: list of nodes which are known on the client (used in ellipses cases)
75 ellipses: whether to send ellipses data or not
79 ellipses: whether to send ellipses data or not
76 """
80 """
77
81
78 preferuncompressed = False
82 preferuncompressed = False
79 try:
83 try:
80
84
81 def splitpaths(data):
85 def splitpaths(data):
82 # work around ''.split(',') => ['']
86 # work around ''.split(',') => ['']
83 return data.split(b',') if data else []
87 return data.split(b',') if data else []
84
88
85 oldincludes = splitpaths(oldincludes)
89 oldincludes = set(splitpaths(oldincludes))
86 newincludes = splitpaths(newincludes)
90 newincludes = set(splitpaths(newincludes))
87 oldexcludes = splitpaths(oldexcludes)
91 oldexcludes = set(splitpaths(oldexcludes))
88 newexcludes = splitpaths(newexcludes)
92 newexcludes = set(splitpaths(newexcludes))
89
93
90 # enforce narrow acl if set
94 # enforce narrow acl if set
91 if repo.ui.has_section(exchange._NARROWACL_SECTION):
95 if repo.ui.has_section(exchange._NARROWACL_SECTION):
92 exchange.applynarrowacl(repo, {'includepats': newincludes})
96 kwargs = exchange.applynarrowacl(
97 repo, {'includepats': newincludes, 'excludepats': newexcludes}
98 )
99 newincludes = kwargs['includepats']
100 requiredexcludes = kwargs['excludepats'] - newexcludes
101 if requiredexcludes:
102 # XXX: The below code to get the username was copied from exchange.py,
103 # where it is noted that this is technically a layering violation for
104 # assuming the existence of HTTP. Using it anyway to make the error
105 # message consistent with the error message for invalid includes.
106 ui = repo.ui
107 username = ui.shortuser(
108 ui.environ.get(b'REMOTE_USER') or ui.username()
109 )
110 raise error.Abort(
111 _(b"The following excludes cannot be removed for %s: %s")
112 % (username, stringutil.pprint(list(requiredexcludes)))
113 )
114 newexcludes = kwargs['excludepats']
93
115
94 # validate the patterns
116 # validate the patterns
95 narrowspec.validatepatterns(set(oldincludes))
117 narrowspec.validatepatterns(oldincludes)
96 narrowspec.validatepatterns(set(newincludes))
118 narrowspec.validatepatterns(newincludes)
97 narrowspec.validatepatterns(set(oldexcludes))
119 narrowspec.validatepatterns(oldexcludes)
98 narrowspec.validatepatterns(set(newexcludes))
120 narrowspec.validatepatterns(newexcludes)
99
121
100 common = wireprototypes.decodelist(commonheads)
122 common = wireprototypes.decodelist(commonheads)
101 known = wireprototypes.decodelist(known)
123 known = wireprototypes.decodelist(known)
102 if ellipses == b'0':
124 if ellipses == b'0':
103 ellipses = False
125 ellipses = False
104 else:
126 else:
105 ellipses = bool(ellipses)
127 ellipses = bool(ellipses)
106 cgversion = cgversion
128 cgversion = cgversion
107
129
108 bundler = bundle2.bundle20(repo.ui)
130 bundler = bundle2.bundle20(repo.ui)
109 newmatch = narrowspec.match(
131 newmatch = narrowspec.match(
110 repo.root, include=newincludes, exclude=newexcludes
132 repo.root, include=newincludes, exclude=newexcludes
111 )
133 )
112 oldmatch = narrowspec.match(
134 oldmatch = narrowspec.match(
113 repo.root, include=oldincludes, exclude=oldexcludes
135 repo.root, include=oldincludes, exclude=oldexcludes
114 )
136 )
115 if not ellipses:
137 if not ellipses:
116 bundle2.widen_bundle(
138 bundle2.widen_bundle(
117 bundler,
139 bundler,
118 repo,
140 repo,
119 oldmatch,
141 oldmatch,
120 newmatch,
142 newmatch,
121 common,
143 common,
122 known,
144 known,
123 cgversion,
145 cgversion,
124 ellipses,
146 ellipses,
125 )
147 )
126 else:
148 else:
127 narrowbundle2.generate_ellipses_bundle2_for_widening(
149 narrowbundle2.generate_ellipses_bundle2_for_widening(
128 bundler,
150 bundler,
129 repo,
151 repo,
130 oldmatch,
152 oldmatch,
131 newmatch,
153 newmatch,
132 cgversion,
154 cgversion,
133 common,
155 common,
134 known,
156 known,
135 )
157 )
136 except error.Abort as exc:
158 except error.Abort as exc:
137 bundler = bundle2.bundle20(repo.ui)
159 bundler = bundle2.bundle20(repo.ui)
138 manargs = [(b'message', exc.message)]
160 manargs = [(b'message', exc.message)]
139 advargs = []
161 advargs = []
140 if exc.hint is not None:
162 if exc.hint is not None:
141 advargs.append((b'hint', exc.hint))
163 advargs.append((b'hint', exc.hint))
142 bundler.addpart(bundle2.bundlepart(b'error:abort', manargs, advargs))
164 bundler.addpart(bundle2.bundlepart(b'error:abort', manargs, advargs))
143 preferuncompressed = True
165 preferuncompressed = True
144
166
145 chunks = bundler.getchunks()
167 chunks = bundler.getchunks()
146 return wireprototypes.streamres(
168 return wireprototypes.streamres(
147 gen=chunks, prefer_uncompressed=preferuncompressed
169 gen=chunks, prefer_uncompressed=preferuncompressed
148 )
170 )
149
171
150
172
151 def peernarrowwiden(remote, **kwargs):
173 def peernarrowwiden(remote, **kwargs):
152 for ch in ('commonheads', 'known'):
174 for ch in ('commonheads', 'known'):
153 kwargs[ch] = wireprototypes.encodelist(kwargs[ch])
175 kwargs[ch] = wireprototypes.encodelist(kwargs[ch])
154
176
155 for ch in ('oldincludes', 'newincludes', 'oldexcludes', 'newexcludes'):
177 for ch in ('oldincludes', 'newincludes', 'oldexcludes', 'newexcludes'):
156 kwargs[ch] = b','.join(kwargs[ch])
178 kwargs[ch] = b','.join(kwargs[ch])
157
179
158 kwargs['ellipses'] = b'%i' % bool(kwargs['ellipses'])
180 kwargs['ellipses'] = b'%i' % bool(kwargs['ellipses'])
159 f = remote._callcompressable(b'narrow_widen', **kwargs)
181 f = remote._callcompressable(b'narrow_widen', **kwargs)
160 return bundle2.getunbundler(remote.ui, f)
182 return bundle2.getunbundler(remote.ui, f)
@@ -1,81 +1,75 b''
1 Test exclusion-based ACL enforcement
1 Test exclusion-based ACL enforcement
2 $ . "$TESTDIR/narrow-library.sh"
2 $ . "$TESTDIR/narrow-library.sh"
3
3
4 $ hg init master
4 $ hg init master
5 $ cd master
5 $ cd master
6
6
7 $ for x in `$TESTDIR/seq.py 3`; do
7 $ for x in `$TESTDIR/seq.py 3`; do
8 > echo $x > "f$x"
8 > echo $x > "f$x"
9 > hg add "f$x"
9 > hg add "f$x"
10 > hg commit -m "Add $x"
10 > hg commit -m "Add $x"
11 > done
11 > done
12 $ cat >> .hg/hgrc << EOF
12 $ cat >> .hg/hgrc << EOF
13 > [narrowacl]
13 > [narrowacl]
14 > default.includes=*
14 > default.includes=*
15 > default.excludes=f2 f3
15 > default.excludes=f2 f3
16 > test.excludes=f3
16 > test.excludes=f3
17 > EOF
17 > EOF
18 $ hg serve -a localhost -p $HGPORT1 -d --pid-file=hg.pid
18 $ hg serve -a localhost -p $HGPORT1 -d --pid-file=hg.pid
19 $ cat hg.pid >> "$DAEMON_PIDS"
19 $ cat hg.pid >> "$DAEMON_PIDS"
20
20
21 $ cd ..
21 $ cd ..
22 $ hg clone http://localhost:$HGPORT1 narrowclone1
22 $ hg clone http://localhost:$HGPORT1 narrowclone1
23 requesting all changes
23 requesting all changes
24 adding changesets
24 adding changesets
25 adding manifests
25 adding manifests
26 adding file changes
26 adding file changes
27 added 3 changesets with 2 changes to 2 files
27 added 3 changesets with 2 changes to 2 files
28 new changesets * (glob)
28 new changesets * (glob)
29 updating to branch default
29 updating to branch default
30 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
30 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
31
31
32 The clone directory should only contain f1 and f2
32 The clone directory should only contain f1 and f2
33 $ ls -A -1 narrowclone1 | sort
33 $ ls -A -1 narrowclone1 | sort
34 .hg
34 .hg
35 f1
35 f1
36 f2
36 f2
37
37
38 Requirements should contain narrowhg
38 Requirements should contain narrowhg
39 $ hg debugrequires -R narrowclone1 | grep narrowhg
39 $ hg debugrequires -R narrowclone1 | grep narrowhg
40 narrowhg-experimental
40 narrowhg-experimental
41
41
42 NarrowHG should exclude f3.
42 NarrowHG should exclude f3.
43 $ hg -R narrowclone1 tracked
43 $ hg -R narrowclone1 tracked
44 I path:.
44 I path:.
45 X path:f3
45 X path:f3
46
46
47 Narrow should not be able to widen to include f3
47 Narrow should not be able to widen to include f3
48 $ hg -R narrowclone1 tracked --addinclude f3
48 $ hg -R narrowclone1 tracked --addinclude f3
49 comparing with http://localhost:$HGPORT1/
49 comparing with http://localhost:$HGPORT1/
50 searching for changes
50 searching for changes
51 adding changesets
51 adding changesets
52 adding manifests
52 adding manifests
53 adding file changes
53 adding file changes
54 $ ls -A -1 narrowclone1 | sort
54 $ ls -A -1 narrowclone1 | sort
55 .hg
55 .hg
56 f1
56 f1
57 f2
57 f2
58 $ hg -R narrowclone1 tracked
58 $ hg -R narrowclone1 tracked
59 I path:.
59 I path:.
60 X path:f3
60 X path:f3
61
61
62
62
63 Narrow should not be able to remove the exclusion for f3
63 Narrow should not be able to remove the exclusion for f3
64 $ hg -R narrowclone1 tracked --removeexclude f3
64 $ hg -R narrowclone1 tracked --removeexclude f3
65 comparing with http://localhost:$HGPORT1/
65 comparing with http://localhost:$HGPORT1/
66 searching for changes
66 searching for changes
67 adding changesets
67 abort: The following excludes cannot be removed for test: ['path:f3']
68 adding manifests
68 [255]
69 adding file changes
70 added 0 changesets with 1 changes to 1 files
71 $ ls -A -1 narrowclone1 | sort
69 $ ls -A -1 narrowclone1 | sort
72 .hg
70 .hg
73 f1
71 f1
74 f2
72 f2
75 f3
76 $ hg -R narrowclone1 tracked
73 $ hg -R narrowclone1 tracked
77 I path:.
74 I path:.
78
75 X path:f3
79
80 XXX: BUG! This test demonstrates that we are presently
81 able to gain access to f3 by removing the exclusion.
General Comments 0
You need to be logged in to leave comments. Login now