Merge lp:~twom/launchpad/rework-git-permissions-for-shadowing into lp:launchpad

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
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

To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) :
review: Needs Fixing
Revision history for this message
Colin Watson (cjwatson) wrote :

Just a few minor tweaks, but otherwise this looks good and we can land it.

review: Approve

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):