Merge lp:~twom/launchpad/rework-git-permissions-for-shadowing into lp:launchpad
- rework-git-permissions-for-shadowing
- Merge into devel
Proposed by
Tom Wardill
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | 18801 | ||||
Proposed branch: | lp:~twom/launchpad/rework-git-permissions-for-shadowing | ||||
Merge into: | lp:launchpad | ||||
Diff against target: |
959 lines (+190/-682) 3 files modified
lib/lp/code/interfaces/gitapi.py (+3/-2) lib/lp/code/xmlrpc/git.py (+40/-45) lib/lp/code/xmlrpc/tests/test_git.py (+147/-635) |
||||
To merge this branch: | bzr merge lp:~twom/launchpad/rework-git-permissions-for-shadowing | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Colin Watson (community) | Approve | ||
Review via email: mp+357091@code.launchpad.net |
Commit message
Rework git branch permissions to improve shadowing for multiple grants
Description of the change
To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) : | # |
review:
Needs Fixing
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'lib/lp/code/interfaces/gitapi.py' |
2 | --- lib/lp/code/interfaces/gitapi.py 2018-09-28 13:53:41 +0000 |
3 | +++ lib/lp/code/interfaces/gitapi.py 2018-10-19 08:51:38 +0000 |
4 | @@ -68,8 +68,9 @@ |
5 | not yet supported. |
6 | """ |
7 | |
8 | - def listRefRules(self, repository, user): |
9 | - """Return the list of ref rules for `user` in `repository` |
10 | + def checkRefPermissions(self, repository, ref_paths, user): |
11 | + """Return a list of ref rules for a `user` in a `repository` that |
12 | + match the input refs. |
13 | |
14 | :returns: A list of rules for the user in the specified repository |
15 | """ |
16 | |
17 | === modified file 'lib/lp/code/xmlrpc/git.py' |
18 | --- lib/lp/code/xmlrpc/git.py 2018-10-17 10:09:33 +0000 |
19 | +++ lib/lp/code/xmlrpc/git.py 2018-10-19 08:51:38 +0000 |
20 | @@ -8,6 +8,8 @@ |
21 | 'GitAPI', |
22 | ] |
23 | |
24 | +from collections import defaultdict |
25 | +from fnmatch import fnmatch |
26 | import sys |
27 | |
28 | from pymacaroons import Macaroon |
29 | @@ -24,7 +26,10 @@ |
30 | |
31 | from lp.app.errors import NameLookupFailed |
32 | from lp.app.validators import LaunchpadValidationError |
33 | -from lp.code.enums import GitRepositoryType |
34 | +from lp.code.enums import ( |
35 | + GitGranteeType, |
36 | + GitRepositoryType, |
37 | + ) |
38 | from lp.code.errors import ( |
39 | GitRepositoryCreationException, |
40 | GitRepositoryCreationFault, |
41 | @@ -350,67 +355,57 @@ |
42 | permissions.add('force_push') |
43 | return permissions |
44 | |
45 | - def _listRefRules(self, requester, translated_path): |
46 | + def _findMatchingRules(self, repository, ref_path): |
47 | + """Find all matching rules for a given ref path""" |
48 | + matching_rules = [] |
49 | + for rule in repository.rules: |
50 | + if fnmatch(ref_path, rule.ref_pattern): |
51 | + matching_rules.append(rule) |
52 | + return matching_rules |
53 | + |
54 | + def _checkRefPermissions(self, requester, translated_path, ref_paths): |
55 | repository = removeSecurityProxy( |
56 | getUtility(IGitLookup).getByHostingPath(translated_path)) |
57 | is_owner = requester.inTeam(repository.owner) |
58 | + result = {} |
59 | + |
60 | + grants_for_user = defaultdict(list) |
61 | grants = repository.findRuleGrantsByGrantee(requester) |
62 | - # If the user is the owner, get the grants for REPOSITORY_OWNER |
63 | - # and add them to our available grants to match against |
64 | if is_owner: |
65 | owner_grants = repository.findRuleGrantsForRepositoryOwner() |
66 | grants = grants.union(owner_grants) |
67 | - result = [] |
68 | + for grant in grants: |
69 | + grants_for_user[grant.rule].append(grant) |
70 | |
71 | - for rule in repository.rules: |
72 | - # Do we have any grants for this rule, for this user? |
73 | - matching_grants = [x for x in grants if x.rule == rule] |
74 | - # If we don't have any grants, but the user is the owner, |
75 | - # they get a default grant to the ref specified by the rule. |
76 | - if is_owner and not matching_grants: |
77 | - result.append( |
78 | - {'ref_pattern': rule.ref_pattern, |
79 | - 'permissions': ['create', 'push'], |
80 | - }) |
81 | + for ref in ref_paths: |
82 | + matching_rules = self._findMatchingRules(repository, ref) |
83 | + if is_owner and not matching_rules: |
84 | + result[ref] = ['create', 'push', 'force_push'] |
85 | continue |
86 | - |
87 | - # Permissions are a union of all the applicable grants |
88 | + seen_grantees = set() |
89 | union_permissions = set() |
90 | - for grant in matching_grants: |
91 | - permissions = self._buildPermissions(grant) |
92 | - union_permissions.update(permissions) |
93 | + for rule in matching_rules: |
94 | + for grant in grants_for_user[rule]: |
95 | + if (grant.grantee, grant.grantee_type) in seen_grantees: |
96 | + continue |
97 | + permissions = self._buildPermissions(grant) |
98 | + union_permissions.update(permissions) |
99 | + seen_grantees.add((grant.grantee, grant.grantee_type)) |
100 | |
101 | - # If the user is the repository owner, they essentially have |
102 | - # the equivalent of a default team grant, but only |
103 | - # if there is no explicit grant to them otherwise specified |
104 | - if is_owner and not any(g for g in owner_grants if g.rule == rule): |
105 | + owner_type = (None, GitGranteeType.REPOSITORY_OWNER) |
106 | + if is_owner and owner_type not in seen_grantees: |
107 | union_permissions.update(['create', 'push']) |
108 | |
109 | - # Sort the permissions from the set for consistency |
110 | sorted_permissions = self._sortPermissions(union_permissions) |
111 | - result.append( |
112 | - {'ref_pattern': rule.ref_pattern, |
113 | - 'permissions': sorted_permissions, |
114 | - }) |
115 | - |
116 | - # The last rule is a default wildcard. The repository owner gets |
117 | - # permissions to everything, whereas other people get no permissions. |
118 | - if is_owner: |
119 | - default_permissions = ['create', 'push', 'force_push'] |
120 | - else: |
121 | - default_permissions = [] |
122 | - result.append( |
123 | - {'ref_pattern': '*', |
124 | - 'permissions': default_permissions, |
125 | - }) |
126 | - |
127 | + result[ref] = sorted_permissions |
128 | return result |
129 | |
130 | - def listRefRules(self, translated_path, auth_params): |
131 | - """See `IGitAPI`""" |
132 | + def checkRefPermissions(self, translated_path, ref_paths, auth_params): |
133 | + """ See `IGitAPI`""" |
134 | requester_id = auth_params.get("uid") |
135 | return run_with_login( |
136 | requester_id, |
137 | - self._listRefRules, |
138 | + self._checkRefPermissions, |
139 | translated_path, |
140 | - ) |
141 | + ref_paths |
142 | + ) |
143 | |
144 | === modified file 'lib/lp/code/xmlrpc/tests/test_git.py' |
145 | --- lib/lp/code/xmlrpc/tests/test_git.py 2018-10-17 10:09:33 +0000 |
146 | +++ lib/lp/code/xmlrpc/tests/test_git.py 2018-10-19 08:51:38 +0000 |
147 | @@ -266,410 +266,7 @@ |
148 | self.assertEqual( |
149 | initial_count, getUtility(IAllGitRepositories).count()) |
150 | |
151 | - def test_listRefRules_simple(self): |
152 | - # Test that correct ref rules are retrieved for a Person |
153 | - requester = self.factory.makePerson() |
154 | - repository = removeSecurityProxy( |
155 | - self.factory.makeGitRepository()) |
156 | - |
157 | - rule = self.factory.makeGitRule(repository) |
158 | - self.factory.makeGitRuleGrant( |
159 | - rule=rule, grantee=requester, can_push=True, can_create=True) |
160 | - |
161 | - results = self.git_api.listRefRules( |
162 | - repository.getInternalPath(), |
163 | - {'uid': requester.id}) |
164 | - self.assertThat(results, MatchesListwise([ |
165 | - MatchesDict({ |
166 | - 'ref_pattern': Equals('refs/heads/*'), |
167 | - 'permissions': Equals(['create', 'push']), |
168 | - }), |
169 | - MatchesDict({ |
170 | - 'ref_pattern': Equals('*'), |
171 | - 'permissions': Equals([]), |
172 | - }), |
173 | - ])) |
174 | - |
175 | - def test_listRefRules_with_other_grants(self): |
176 | - # Test that findRuleGrantsByGrantee only returns relevant rules |
177 | - requester = self.factory.makePerson() |
178 | - other_user = self.factory.makePerson() |
179 | - repository = removeSecurityProxy( |
180 | - self.factory.makeGitRepository()) |
181 | - |
182 | - rule = self.factory.makeGitRule(repository) |
183 | - self.factory.makeGitRuleGrant( |
184 | - rule=rule, grantee=requester, can_push=True) |
185 | - self.factory.makeGitRuleGrant( |
186 | - rule=rule, grantee=other_user, can_create=True) |
187 | - |
188 | - results = self.git_api.listRefRules( |
189 | - repository.getInternalPath(), |
190 | - {'uid': requester.id}) |
191 | - self.assertThat(results, MatchesListwise([ |
192 | - MatchesDict({ |
193 | - 'ref_pattern': Equals('refs/heads/*'), |
194 | - 'permissions': Equals(['push']), |
195 | - }), |
196 | - MatchesDict({ |
197 | - 'ref_pattern': Equals('*'), |
198 | - 'permissions': Equals([]), |
199 | - }), |
200 | - ])) |
201 | - |
202 | - def test_listRefRules_owner_has_default(self): |
203 | - owner = self.factory.makePerson() |
204 | - repository = removeSecurityProxy( |
205 | - self.factory.makeGitRepository(owner=owner)) |
206 | - |
207 | - results = self.git_api.listRefRules( |
208 | - repository.getInternalPath(), |
209 | - {'uid': owner.id}) |
210 | - |
211 | - self.assertThat(results, MatchesListwise([ |
212 | - MatchesDict({ |
213 | - 'ref_pattern': Equals('*'), |
214 | - 'permissions': Equals(['create', 'push', 'force_push']), |
215 | - }), |
216 | - ])) |
217 | - |
218 | - def test_listRefRules_owner_modifies_rules(self): |
219 | - owner = self.factory.makePerson() |
220 | - person = self.factory.makePerson() |
221 | - repository = removeSecurityProxy( |
222 | - self.factory.makeGitRepository(owner=owner)) |
223 | - |
224 | - rule = self.factory.makeGitRule( |
225 | - repository, ref_pattern=u'refs/heads/stable/*') |
226 | - self.factory.makeGitRuleGrant( |
227 | - rule=rule, grantee=person, can_push=True) |
228 | - |
229 | - results = self.git_api.listRefRules( |
230 | - repository.getInternalPath(), |
231 | - {'uid': owner.id}) |
232 | - |
233 | - self.assertThat(results, MatchesListwise([ |
234 | - MatchesDict({ |
235 | - 'ref_pattern': Equals('refs/heads/stable/*'), |
236 | - 'permissions': Equals(['create', 'push']), |
237 | - }), |
238 | - MatchesDict({ |
239 | - 'ref_pattern': Equals('*'), |
240 | - 'permissions': Equals(['create', 'push', 'force_push']), |
241 | - }), |
242 | - ])) |
243 | - |
244 | - def test_listRefRules_owner_no_default_with_explicit(self): |
245 | - owner = self.factory.makePerson() |
246 | - repository = removeSecurityProxy( |
247 | - self.factory.makeGitRepository(owner=owner)) |
248 | - |
249 | - rule = self.factory.makeGitRule( |
250 | - repository, ref_pattern=u'refs/heads/stable/*') |
251 | - self.factory.makeGitRuleGrant( |
252 | - rule=rule, grantee=GitGranteeType.REPOSITORY_OWNER, can_push=True) |
253 | - |
254 | - results = self.git_api.listRefRules( |
255 | - repository.getInternalPath(), |
256 | - {'uid': owner.id}) |
257 | - |
258 | - self.assertThat(results, MatchesListwise([ |
259 | - MatchesDict({ |
260 | - 'ref_pattern': Equals('refs/heads/stable/*'), |
261 | - 'permissions': Equals(['push']), |
262 | - }), |
263 | - MatchesDict({ |
264 | - 'ref_pattern': Equals('*'), |
265 | - 'permissions': Equals(['create', 'push', 'force_push']), |
266 | - }), |
267 | - ])) |
268 | - |
269 | - def test_listRefRules_owner_modifies_rules_multiple_grants(self): |
270 | - owner = self.factory.makePerson() |
271 | - person = self.factory.makePerson() |
272 | - repository = removeSecurityProxy( |
273 | - self.factory.makeGitRepository(owner=owner)) |
274 | - |
275 | - rule = self.factory.makeGitRule( |
276 | - repository, ref_pattern=u'refs/heads/stable/*') |
277 | - self.factory.makeGitRuleGrant( |
278 | - rule=rule, grantee=person, can_push=True, can_create=True) |
279 | - |
280 | - self.factory.makeGitRuleGrant( |
281 | - rule=rule, grantee=GitGranteeType.REPOSITORY_OWNER, can_push=True) |
282 | - |
283 | - results = self.git_api.listRefRules( |
284 | - repository.getInternalPath(), |
285 | - {'uid': owner.id}) |
286 | - |
287 | - self.assertThat(results, MatchesListwise([ |
288 | - MatchesDict({ |
289 | - 'ref_pattern': Equals('refs/heads/stable/*'), |
290 | - 'permissions': Equals(['push']), |
291 | - }), |
292 | - MatchesDict({ |
293 | - 'ref_pattern': Equals('*'), |
294 | - 'permissions': Equals(['create', 'push', 'force_push']), |
295 | - }), |
296 | - ])) |
297 | - |
298 | - def test_listRefRules_no_grants(self): |
299 | - # User that has no grants and is not the owner |
300 | - requester = self.factory.makePerson() |
301 | - owner = self.factory.makePerson() |
302 | - repository = removeSecurityProxy( |
303 | - self.factory.makeGitRepository(owner=owner)) |
304 | - |
305 | - rule = self.factory.makeGitRule(repository) |
306 | - self.factory.makeGitRuleGrant( |
307 | - rule=rule, grantee=owner, can_push=True, can_create=True) |
308 | - |
309 | - results = self.git_api.listRefRules( |
310 | - repository.getInternalPath(), |
311 | - {'uid': requester.id}) |
312 | - self.assertThat(results, MatchesListwise([ |
313 | - MatchesDict({ |
314 | - 'ref_pattern': Equals('refs/heads/*'), |
315 | - 'permissions': Equals([]), |
316 | - }), |
317 | - MatchesDict({ |
318 | - 'ref_pattern': Equals('*'), |
319 | - 'permissions': Equals([]), |
320 | - }), |
321 | - ])) |
322 | - |
323 | - def test_listRefRules_owner_has_default_with_other_grant(self): |
324 | - owner = self.factory.makePerson() |
325 | - repository = removeSecurityProxy( |
326 | - self.factory.makeGitRepository(owner=owner)) |
327 | - |
328 | - rule = self.factory.makeGitRule( |
329 | - repository=repository, ref_pattern=u'refs/heads/master') |
330 | - self.factory.makeGitRuleGrant( |
331 | - rule=rule, grantee=owner, can_push=True, can_create=True) |
332 | - |
333 | - results = self.git_api.listRefRules( |
334 | - repository.getInternalPath(), |
335 | - {'uid': owner.id}) |
336 | - # Default grant should be last in pattern |
337 | - self.assertThat(results, MatchesListwise([ |
338 | - MatchesDict({ |
339 | - 'ref_pattern': Equals('refs/heads/master'), |
340 | - 'permissions': Equals(['create', 'push']), |
341 | - }), |
342 | - MatchesDict({ |
343 | - 'ref_pattern': Equals('*'), |
344 | - 'permissions': Equals(['create', 'push', 'force_push']), |
345 | - }), |
346 | - ])) |
347 | - |
348 | - def test_listRefRules_owner_is_team(self): |
349 | - member = self.factory.makePerson() |
350 | - owner = self.factory.makeTeam(members=[member]) |
351 | - repository = removeSecurityProxy( |
352 | - self.factory.makeGitRepository( |
353 | - owner=owner, information_type=InformationType.USERDATA)) |
354 | - |
355 | - results = self.git_api.listRefRules( |
356 | - repository.getInternalPath(), |
357 | - {'uid': member.id}) |
358 | - |
359 | - # Should have default grant as member of owning team |
360 | - self.assertThat(results, MatchesListwise([ |
361 | - MatchesDict({ |
362 | - 'ref_pattern': Equals('*'), |
363 | - 'permissions': Equals(['create', 'push', 'force_push']), |
364 | - }), |
365 | - ])) |
366 | - |
367 | - def test_listRefRules_owner_is_team_with_grants(self): |
368 | - member = self.factory.makePerson() |
369 | - owner = self.factory.makeTeam(members=[member]) |
370 | - repository = removeSecurityProxy( |
371 | - self.factory.makeGitRepository( |
372 | - owner=owner, information_type=InformationType.USERDATA)) |
373 | - |
374 | - rule = self.factory.makeGitRule( |
375 | - repository=repository, ref_pattern=u'refs/heads/master') |
376 | - self.factory.makeGitRuleGrant( |
377 | - rule=rule, grantee=owner, can_push=True, can_create=True) |
378 | - |
379 | - results = self.git_api.listRefRules( |
380 | - repository.getInternalPath(), |
381 | - {'uid': member.id}) |
382 | - |
383 | - # Should have default grant as member of owning team |
384 | - self.assertThat(results, MatchesListwise([ |
385 | - MatchesDict({ |
386 | - 'ref_pattern': Equals('refs/heads/master'), |
387 | - 'permissions': Equals(['create', 'push']), |
388 | - }), |
389 | - MatchesDict({ |
390 | - 'ref_pattern': Equals('*'), |
391 | - 'permissions': Equals(['create', 'push', 'force_push']), |
392 | - }), |
393 | - ])) |
394 | - |
395 | - def test_listRefRules_owner_is_team_with_grants_to_person(self): |
396 | - member = self.factory.makePerson() |
397 | - other_member = self.factory.makePerson() |
398 | - owner = self.factory.makeTeam(members=[member, other_member]) |
399 | - repository = removeSecurityProxy( |
400 | - self.factory.makeGitRepository( |
401 | - owner=owner, information_type=InformationType.USERDATA)) |
402 | - |
403 | - rule = self.factory.makeGitRule( |
404 | - repository=repository, ref_pattern=u'refs/heads/master') |
405 | - self.factory.makeGitRuleGrant( |
406 | - rule=rule, grantee=GitGranteeType.REPOSITORY_OWNER, |
407 | - can_push=True, can_create=True) |
408 | - |
409 | - rule = self.factory.makeGitRule( |
410 | - repository=repository, ref_pattern=u'refs/heads/tags') |
411 | - self.factory.makeGitRuleGrant( |
412 | - rule=rule, grantee=member, can_create=True) |
413 | - |
414 | - # This should not appear |
415 | - self.factory.makeGitRuleGrant( |
416 | - rule=rule, grantee=other_member, can_push=True) |
417 | - |
418 | - results = self.git_api.listRefRules( |
419 | - repository.getInternalPath(), |
420 | - {'uid': member.id}) |
421 | - |
422 | - # Should have default grant as member of owning team |
423 | - self.assertThat(results, MatchesListwise([ |
424 | - MatchesDict({ |
425 | - 'ref_pattern': Equals('refs/heads/master'), |
426 | - 'permissions': Equals(['create', 'push']), |
427 | - }), |
428 | - MatchesDict({ |
429 | - 'ref_pattern': Equals('refs/heads/tags'), |
430 | - 'permissions': Equals(['create', 'push']), |
431 | - }), |
432 | - MatchesDict({ |
433 | - 'ref_pattern': Equals('*'), |
434 | - 'permissions': Equals(['create', 'push', 'force_push']), |
435 | - }), |
436 | - ])) |
437 | - |
438 | - def test_listRefRules_multiple_grants_to_same_ref_with_owner(self): |
439 | - member = self.factory.makePerson() |
440 | - owner = self.factory.makeTeam(members=[member]) |
441 | - repository = removeSecurityProxy( |
442 | - self.factory.makeGitRepository(owner=owner)) |
443 | - |
444 | - rule = self.factory.makeGitRule(repository=repository) |
445 | - self.factory.makeGitRuleGrant( |
446 | - rule=rule, grantee=member, can_create=True) |
447 | - self.factory.makeGitRuleGrant( |
448 | - rule=rule, grantee=owner, can_push=True) |
449 | - |
450 | - results = self.git_api.listRefRules( |
451 | - repository.getInternalPath(), |
452 | - {'uid': member.id}) |
453 | - |
454 | - self.assertThat(results, MatchesListwise([ |
455 | - MatchesDict({ |
456 | - 'ref_pattern': Equals('refs/heads/*'), |
457 | - 'permissions': Equals(['create', 'push']), |
458 | - }), |
459 | - MatchesDict({ |
460 | - 'ref_pattern': Equals('*'), |
461 | - 'permissions': Equals(['create', 'push', 'force_push']), |
462 | - }), |
463 | - ])) |
464 | - |
465 | - def test_listRefRules_multiple_grants_collapsing(self): |
466 | - member = self.factory.makePerson() |
467 | - second_member = self.factory.makePerson() |
468 | - third_member = self.factory.makePerson() |
469 | - owner = self.factory.makePerson() |
470 | - repository = removeSecurityProxy( |
471 | - self.factory.makeGitRepository(owner=owner)) |
472 | - |
473 | - rule = self.factory.makeGitRule(repository=repository) |
474 | - self.factory.makeGitRuleGrant( |
475 | - rule=rule, grantee=member, can_create=True) |
476 | - self.factory.makeGitRuleGrant( |
477 | - rule=rule, grantee=second_member, can_push=True) |
478 | - self.factory.makeGitRuleGrant( |
479 | - rule=rule, grantee=third_member, can_force_push=True) |
480 | - |
481 | - results = self.git_api.listRefRules( |
482 | - repository.getInternalPath(), |
483 | - {'uid': owner.id}) |
484 | - |
485 | - self.assertThat(results, MatchesListwise([ |
486 | - MatchesDict({ |
487 | - 'ref_pattern': Equals('refs/heads/*'), |
488 | - 'permissions': Equals(['create', 'push']), |
489 | - }), |
490 | - MatchesDict({ |
491 | - 'ref_pattern': Equals('*'), |
492 | - 'permissions': Equals(['create', 'push', 'force_push']), |
493 | - }), |
494 | - ])) |
495 | - |
496 | - def test_listRefRules_grantee_owner_type(self): |
497 | - owner = self.factory.makePerson() |
498 | - repository = removeSecurityProxy( |
499 | - self.factory.makeGitRepository(owner=owner)) |
500 | - rule = self.factory.makeGitRule(repository=repository) |
501 | - self.factory.makeGitRuleGrant( |
502 | - rule=rule, grantee=GitGranteeType.REPOSITORY_OWNER, |
503 | - can_create=True) |
504 | - |
505 | - results = self.git_api.listRefRules( |
506 | - repository.getInternalPath(), |
507 | - {'uid': owner.id}) |
508 | - |
509 | - self.assertThat(results, MatchesListwise([ |
510 | - MatchesDict({ |
511 | - 'ref_pattern': Equals('refs/heads/*'), |
512 | - 'permissions': Equals(['create']), |
513 | - }), |
514 | - MatchesDict({ |
515 | - 'ref_pattern': Equals('*'), |
516 | - 'permissions': Equals(['create', 'push', 'force_push']), |
517 | - }), |
518 | - ])) |
519 | - |
520 | - def test_listRefRules_grantee_owner_type_and_other_grants(self): |
521 | - owner = self.factory.makePerson() |
522 | - other_person = self.factory.makePerson() |
523 | - repository = removeSecurityProxy( |
524 | - self.factory.makeGitRepository(owner=owner)) |
525 | - rule = self.factory.makeGitRule(repository=repository) |
526 | - self.factory.makeGitRuleGrant( |
527 | - rule=rule, grantee=GitGranteeType.REPOSITORY_OWNER, |
528 | - can_create=True) |
529 | - |
530 | - rule = self.factory.makeGitRule( |
531 | - repository=repository, ref_pattern=u'refs/heads/other') |
532 | - self.factory.makeGitRuleGrant( |
533 | - rule=rule, grantee=other_person, can_push=True) |
534 | - |
535 | - results = self.git_api.listRefRules( |
536 | - repository.getInternalPath(), |
537 | - {'uid': owner.id}) |
538 | - |
539 | - self.assertThat(results, MatchesListwise([ |
540 | - MatchesDict({ |
541 | - 'ref_pattern': Equals('refs/heads/other'), |
542 | - 'permissions': Equals(['create', 'push']), |
543 | - }), |
544 | - MatchesDict({ |
545 | - 'ref_pattern': Equals('refs/heads/*'), |
546 | - 'permissions': Equals(['create']), |
547 | - }), |
548 | - MatchesDict({ |
549 | - 'ref_pattern': Equals('*'), |
550 | - 'permissions': Equals(['create', 'push', 'force_push']), |
551 | - }), |
552 | - ])) |
553 | - |
554 | - def test_listRefRules_grantee_example_one(self): |
555 | + def _make_scenario_one_repository(self): |
556 | user_a = self.factory.makePerson() |
557 | user_b = self.factory.makePerson() |
558 | user_c = self.factory.makePerson() |
559 | @@ -686,6 +283,10 @@ |
560 | can_force_push=True) |
561 | |
562 | rule = self.factory.makeGitRule( |
563 | + repository, ref_pattern=u'refs/heads/stable/protected') |
564 | + self.factory.makeGitRuleGrant(rule=rule, grantee=stable_team) |
565 | + |
566 | + rule = self.factory.makeGitRule( |
567 | repository, ref_pattern=u'refs/heads/archived/*') |
568 | self.factory.makeGitRuleGrant( |
569 | rule=rule, grantee=GitGranteeType.REPOSITORY_OWNER) |
570 | @@ -710,247 +311,158 @@ |
571 | self.factory.makeGitRuleGrant( |
572 | rule=rule, grantee=stable_team, can_create=True) |
573 | |
574 | - results = self.git_api.listRefRules( |
575 | - repository.getInternalPath(), |
576 | + test_ref_paths = [ |
577 | + 'refs/heads/stable/next', 'refs/heads/stable/protected', |
578 | + 'refs/heads/stable/foo', 'refs/heads/archived/foo', |
579 | + 'refs/heads/foo/next', 'refs/heads/unprotected', |
580 | + 'refs/tags/1.0', |
581 | + ] |
582 | + |
583 | + return (user_a, user_b, user_c, stable_team, next_team, repository, |
584 | + test_ref_paths) |
585 | + |
586 | + def test_checkRefPermissions_scenario_one_user_a(self): |
587 | + user_a, _, _, _, _, repo, paths = self._make_scenario_one_repository() |
588 | + |
589 | + results = self.git_api.checkRefPermissions( |
590 | + repo.getInternalPath(), |
591 | + paths, |
592 | {'uid': user_a.id}) |
593 | |
594 | - self.assertThat(results, MatchesListwise([ |
595 | - MatchesDict( |
596 | - {'ref_pattern': Equals('refs/heads/stable/next'), |
597 | - 'permissions': Equals(['push', 'force_push']), |
598 | - }), |
599 | - MatchesDict( |
600 | - {'ref_pattern': Equals('refs/heads/archived/*'), |
601 | - 'permissions': Equals([]), |
602 | - }), |
603 | - MatchesDict( |
604 | - {'ref_pattern': Equals('refs/heads/stable/*'), |
605 | - 'permissions': Equals(['create', 'push']), |
606 | - }), |
607 | - MatchesDict( |
608 | - {'ref_pattern': Equals('refs/heads/*/next'), |
609 | - 'permissions': Equals(['create', 'push']), |
610 | - }), |
611 | - MatchesDict( |
612 | - {'ref_pattern': Equals('refs/tags/*'), |
613 | - 'permissions': Equals(['create']), |
614 | - }), |
615 | - MatchesDict( |
616 | - {'ref_pattern': Equals('*'), |
617 | - 'permissions': Equals(['create', 'push', 'force_push']), |
618 | - }), |
619 | - ])) |
620 | - |
621 | - def test_listRefRules_grantee_example_two(self): |
622 | - user_a = self.factory.makePerson() |
623 | - user_b = self.factory.makePerson() |
624 | - user_c = self.factory.makePerson() |
625 | - stable_team = self.factory.makeTeam(members=[user_a, user_b]) |
626 | - next_team = self.factory.makeTeam(members=[user_b, user_c]) |
627 | - |
628 | - repository = removeSecurityProxy( |
629 | - self.factory.makeGitRepository(owner=user_a)) |
630 | - |
631 | - rule = self.factory.makeGitRule( |
632 | - repository, ref_pattern=u'refs/heads/stable/next') |
633 | - self.factory.makeGitRuleGrant( |
634 | - rule=rule, grantee=user_a, can_force_push=True) |
635 | - |
636 | - rule = self.factory.makeGitRule( |
637 | - repository, ref_pattern=u'refs/heads/archived/*') |
638 | - self.factory.makeGitRuleGrant( |
639 | - rule=rule, grantee=user_a) |
640 | - self.factory.makeGitRuleGrant( |
641 | - rule=rule, grantee=user_b, can_create=True) |
642 | - |
643 | - rule = self.factory.makeGitRule( |
644 | - repository, ref_pattern=u'refs/heads/stable/*') |
645 | - self.factory.makeGitRuleGrant( |
646 | - rule=rule, grantee=stable_team, can_push=True) |
647 | - |
648 | - rule = self.factory.makeGitRule( |
649 | - repository, ref_pattern=u'refs/heads/*/next') |
650 | - self.factory.makeGitRuleGrant( |
651 | - rule=rule, grantee=next_team, can_force_push=True) |
652 | - |
653 | - rule = self.factory.makeGitRule( |
654 | - repository, ref_pattern=u'refs/tags/*') |
655 | - self.factory.makeGitRuleGrant( |
656 | - rule=rule, grantee=user_a, can_create=True) |
657 | - self.factory.makeGitRuleGrant( |
658 | - rule=rule, grantee=stable_team, can_create=True) |
659 | - |
660 | - results = self.git_api.listRefRules( |
661 | - repository.getInternalPath(), |
662 | + self.assertThat(results, MatchesDict({ |
663 | + 'refs/heads/stable/next': Equals(['push', 'force_push']), |
664 | + 'refs/heads/stable/protected': Equals(['create', 'push']), |
665 | + 'refs/heads/stable/foo': Equals(['create', 'push']), |
666 | + 'refs/heads/archived/foo': Equals([]), |
667 | + 'refs/heads/foo/next': Equals(['create', 'push']), |
668 | + 'refs/heads/unprotected': Equals(['create', 'push', 'force_push']), |
669 | + 'refs/tags/1.0': Equals(['create']), |
670 | + })) |
671 | + |
672 | + def test_checkRefPermissions_scenario_one_user_b(self): |
673 | + _, user_b, _, _, _, repo, paths = self._make_scenario_one_repository() |
674 | + |
675 | + results = self.git_api.checkRefPermissions( |
676 | + repo.getInternalPath(), |
677 | + paths, |
678 | {'uid': user_b.id}) |
679 | |
680 | - self.assertThat(results, MatchesListwise([ |
681 | - MatchesDict( |
682 | - {'ref_pattern': Equals('refs/heads/stable/next'), |
683 | - 'permissions': Equals([]), |
684 | - }), |
685 | - MatchesDict( |
686 | - {'ref_pattern': Equals('refs/heads/archived/*'), |
687 | - 'permissions': Equals(['create']), |
688 | - }), |
689 | - MatchesDict( |
690 | - {'ref_pattern': Equals('refs/heads/stable/*'), |
691 | - 'permissions': Equals(['push']), |
692 | - }), |
693 | - MatchesDict( |
694 | - {'ref_pattern': Equals('refs/heads/*/next'), |
695 | - 'permissions': Equals(['push', 'force_push']), |
696 | - }), |
697 | - MatchesDict( |
698 | - {'ref_pattern': Equals('refs/tags/*'), |
699 | - 'permissions': Equals(['create']), |
700 | - }), |
701 | - MatchesDict( |
702 | - {'ref_pattern': Equals('*'), |
703 | - 'permissions': Equals([]), |
704 | - }), |
705 | - ])) |
706 | - |
707 | - def test_listRefRules_grantee_example_three(self): |
708 | - user_a = self.factory.makePerson() |
709 | - user_b = self.factory.makePerson() |
710 | - user_c = self.factory.makePerson() |
711 | - stable_team = self.factory.makeTeam(members=[user_a, user_b]) |
712 | - next_team = self.factory.makeTeam(members=[user_b, user_c]) |
713 | - |
714 | - repository = removeSecurityProxy( |
715 | - self.factory.makeGitRepository(owner=user_a)) |
716 | - |
717 | - rule = self.factory.makeGitRule( |
718 | - repository, ref_pattern=u'refs/heads/stable/next') |
719 | - self.factory.makeGitRuleGrant( |
720 | - rule=rule, grantee=user_a, can_force_push=True) |
721 | - |
722 | - rule = self.factory.makeGitRule( |
723 | - repository, ref_pattern=u'refs/heads/archived/*') |
724 | - self.factory.makeGitRuleGrant( |
725 | - rule=rule, grantee=user_a) |
726 | - self.factory.makeGitRuleGrant( |
727 | - rule=rule, grantee=user_b, can_create=True) |
728 | - |
729 | - rule = self.factory.makeGitRule( |
730 | - repository, ref_pattern=u'refs/heads/stable/*') |
731 | - self.factory.makeGitRuleGrant( |
732 | - rule=rule, grantee=stable_team, can_push=True) |
733 | - |
734 | - rule = self.factory.makeGitRule( |
735 | - repository, ref_pattern=u'refs/heads/*/next') |
736 | - self.factory.makeGitRuleGrant( |
737 | - rule=rule, grantee=next_team, can_force_push=True) |
738 | - |
739 | - rule = self.factory.makeGitRule( |
740 | - repository, ref_pattern=u'refs/tags/*') |
741 | - self.factory.makeGitRuleGrant( |
742 | - rule=rule, grantee=user_a, can_create=True) |
743 | - self.factory.makeGitRuleGrant( |
744 | - rule=rule, grantee=stable_team, can_create=True) |
745 | - |
746 | - results = self.git_api.listRefRules( |
747 | - repository.getInternalPath(), |
748 | + self.assertThat(results, MatchesDict({ |
749 | + 'refs/heads/stable/next': Equals(['push', 'force_push']), |
750 | + 'refs/heads/stable/protected': Equals([]), |
751 | + 'refs/heads/stable/foo': Equals(['push']), |
752 | + 'refs/heads/archived/foo': Equals(['create']), |
753 | + 'refs/heads/foo/next': Equals(['push', 'force_push']), |
754 | + 'refs/heads/unprotected': Equals([]), |
755 | + 'refs/tags/1.0': Equals(['create']), |
756 | + })) |
757 | + |
758 | + def test_checkRefPermissions_scenario_one_user_c(self): |
759 | + _, _, user_c, _, _, repo, paths = self._make_scenario_one_repository() |
760 | + |
761 | + results = self.git_api.checkRefPermissions( |
762 | + repo.getInternalPath(), |
763 | + paths, |
764 | {'uid': user_c.id}) |
765 | |
766 | - self.assertThat(results, MatchesListwise([ |
767 | - MatchesDict( |
768 | - {'ref_pattern': Equals('refs/heads/stable/next'), |
769 | - 'permissions': Equals([]), |
770 | - }), |
771 | - MatchesDict( |
772 | - {'ref_pattern': Equals('refs/heads/archived/*'), |
773 | - 'permissions': Equals([]), |
774 | - }), |
775 | - MatchesDict( |
776 | - {'ref_pattern': Equals('refs/heads/stable/*'), |
777 | - 'permissions': Equals([]), |
778 | - }), |
779 | - MatchesDict( |
780 | - {'ref_pattern': Equals('refs/heads/*/next'), |
781 | - 'permissions': Equals(['push', 'force_push']), |
782 | - }), |
783 | - MatchesDict( |
784 | - {'ref_pattern': Equals('refs/tags/*'), |
785 | - 'permissions': Equals([]), |
786 | - }), |
787 | - MatchesDict( |
788 | - {'ref_pattern': Equals('*'), |
789 | - 'permissions': Equals([]), |
790 | - }), |
791 | - ])) |
792 | + self.assertThat(results, MatchesDict({ |
793 | + 'refs/heads/stable/next': Equals(['push', 'force_push']), |
794 | + 'refs/heads/stable/protected': Equals([]), |
795 | + 'refs/heads/stable/foo': Equals([]), |
796 | + 'refs/heads/archived/foo': Equals([]), |
797 | + 'refs/heads/foo/next': Equals(['push', 'force_push']), |
798 | + 'refs/heads/unprotected': Equals([]), |
799 | + 'refs/tags/1.0': Equals([]), |
800 | + })) |
801 | |
802 | - def test_listRefRules_grantee_example_four(self): |
803 | - user_a = self.factory.makePerson() |
804 | - user_b = self.factory.makePerson() |
805 | - user_c = self.factory.makePerson() |
806 | + def test_checkRefPermissions_scenario_one_user_d(self): |
807 | user_d = self.factory.makePerson() |
808 | - stable_team = self.factory.makeTeam(members=[user_a, user_b]) |
809 | - next_team = self.factory.makeTeam(members=[user_b, user_c]) |
810 | - |
811 | - repository = removeSecurityProxy( |
812 | - self.factory.makeGitRepository(owner=user_a)) |
813 | - |
814 | - rule = self.factory.makeGitRule( |
815 | - repository, ref_pattern=u'refs/heads/stable/next') |
816 | - self.factory.makeGitRuleGrant( |
817 | - rule=rule, grantee=user_a, can_force_push=True) |
818 | - |
819 | - rule = self.factory.makeGitRule( |
820 | - repository, ref_pattern=u'refs/heads/archived/*') |
821 | - self.factory.makeGitRuleGrant( |
822 | - rule=rule, grantee=user_a) |
823 | - self.factory.makeGitRuleGrant( |
824 | - rule=rule, grantee=user_b, can_create=True) |
825 | - |
826 | - rule = self.factory.makeGitRule( |
827 | - repository, ref_pattern=u'refs/heads/stable/*') |
828 | - self.factory.makeGitRuleGrant( |
829 | - rule=rule, grantee=stable_team, can_push=True) |
830 | - |
831 | - rule = self.factory.makeGitRule( |
832 | - repository, ref_pattern=u'refs/heads/*/next') |
833 | - self.factory.makeGitRuleGrant( |
834 | - rule=rule, grantee=next_team, can_force_push=True) |
835 | - |
836 | - rule = self.factory.makeGitRule( |
837 | - repository, ref_pattern=u'refs/tags/*') |
838 | - self.factory.makeGitRuleGrant( |
839 | - rule=rule, grantee=user_a, can_create=True) |
840 | - self.factory.makeGitRuleGrant( |
841 | - rule=rule, grantee=stable_team, can_create=True) |
842 | - |
843 | - results = self.git_api.listRefRules( |
844 | - repository.getInternalPath(), |
845 | + _, _, user_c, _, _, repo, paths = self._make_scenario_one_repository() |
846 | + |
847 | + results = self.git_api.checkRefPermissions( |
848 | + repo.getInternalPath(), |
849 | + paths, |
850 | {'uid': user_d.id}) |
851 | |
852 | - self.assertThat(results, MatchesListwise([ |
853 | - MatchesDict( |
854 | - {'ref_pattern': Equals('refs/heads/stable/next'), |
855 | - 'permissions': Equals([]), |
856 | - }), |
857 | - MatchesDict( |
858 | - {'ref_pattern': Equals('refs/heads/archived/*'), |
859 | - 'permissions': Equals([]), |
860 | - }), |
861 | - MatchesDict( |
862 | - {'ref_pattern': Equals('refs/heads/stable/*'), |
863 | - 'permissions': Equals([]), |
864 | - }), |
865 | - MatchesDict( |
866 | - {'ref_pattern': Equals('refs/heads/*/next'), |
867 | - 'permissions': Equals([]), |
868 | - }), |
869 | - MatchesDict( |
870 | - {'ref_pattern': Equals('refs/tags/*'), |
871 | - 'permissions': Equals([]), |
872 | - }), |
873 | - MatchesDict( |
874 | - {'ref_pattern': Equals('*'), |
875 | - 'permissions': Equals([]), |
876 | - }), |
877 | - ])) |
878 | + self.assertThat(results, MatchesDict({ |
879 | + 'refs/heads/stable/next': Equals([]), |
880 | + 'refs/heads/stable/protected': Equals([]), |
881 | + 'refs/heads/stable/foo': Equals([]), |
882 | + 'refs/heads/archived/foo': Equals([]), |
883 | + 'refs/heads/foo/next': Equals([]), |
884 | + 'refs/heads/unprotected': Equals([]), |
885 | + 'refs/tags/1.0': Equals([]), |
886 | + })) |
887 | + |
888 | + def _make_scenario_two_repository(self): |
889 | + user_a = self.factory.makePerson() |
890 | + user_b = self.factory.makePerson() |
891 | + |
892 | + repository = removeSecurityProxy( |
893 | + self.factory.makeGitRepository(owner=user_a)) |
894 | + |
895 | + rule = self.factory.makeGitRule( |
896 | + repository, ref_pattern=u'refs/heads/master') |
897 | + self.factory.makeGitRuleGrant( |
898 | + rule=rule, grantee=user_b, can_push=True) |
899 | + |
900 | + rule = self.factory.makeGitRule( |
901 | + repository, ref_pattern=u'refs/heads/*') |
902 | + self.factory.makeGitRuleGrant( |
903 | + rule=rule, grantee=GitGranteeType.REPOSITORY_OWNER, |
904 | + can_create=True, can_push=True, can_force_push=True) |
905 | + |
906 | + rule = self.factory.makeGitRule( |
907 | + repository, ref_pattern=u'refs/tags/*') |
908 | + self.factory.makeGitRuleGrant( |
909 | + rule=rule, grantee=user_b, can_push=True) |
910 | + |
911 | + test_ref_paths = ['refs/heads/master', 'refs/heads/foo', |
912 | + 'refs/tags/1.0', 'refs/other'] |
913 | + return user_a, user_b, repository, test_ref_paths |
914 | + |
915 | + def test_checkRefPermissions_scenario_two_user_a(self): |
916 | + user_a, _, repo, paths = self._make_scenario_two_repository() |
917 | + results = self.git_api.checkRefPermissions( |
918 | + repo.getInternalPath(), |
919 | + paths, |
920 | + {'uid': user_a.id}) |
921 | + |
922 | + self.assertThat(results, MatchesDict({ |
923 | + 'refs/heads/master': Equals(['create', 'push', 'force_push']), |
924 | + 'refs/heads/foo': Equals(['create', 'push', 'force_push']), |
925 | + 'refs/tags/1.0': Equals(['create', 'push']), |
926 | + 'refs/other': Equals(['create', 'push', 'force_push']), |
927 | + })) |
928 | + |
929 | + def test_checkRefPermissions_scenario_two_user_b(self): |
930 | + _, user_b, repo, paths = self._make_scenario_two_repository() |
931 | + results = self.git_api.checkRefPermissions( |
932 | + repo.getInternalPath(), |
933 | + paths, |
934 | + {'uid': user_b.id}) |
935 | + |
936 | + self.assertThat(results, MatchesDict({ |
937 | + 'refs/heads/master': Equals(['push']), |
938 | + 'refs/heads/foo': Equals([]), |
939 | + 'refs/tags/1.0': Equals(['push']), |
940 | + 'refs/other': Equals([]), |
941 | + })) |
942 | + |
943 | + def test_checkRefPermissions_scenario_two_user_c(self): |
944 | + _, _, repo, paths = self._make_scenario_two_repository() |
945 | + user_c = self.factory.makePerson() |
946 | + results = self.git_api.checkRefPermissions( |
947 | + repo.getInternalPath(), |
948 | + paths, |
949 | + {'uid': user_c.id}) |
950 | + |
951 | + self.assertThat(results, MatchesDict({ |
952 | + 'refs/heads/master': Equals([]), |
953 | + 'refs/heads/foo': Equals([]), |
954 | + 'refs/tags/1.0': Equals([]), |
955 | + 'refs/other': Equals([]), |
956 | + })) |
957 | |
958 | |
959 | class TestGitAPI(TestGitAPIMixin, TestCaseWithFactory): |
Just a few minor tweaks, but otherwise this looks good and we can land it.