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
=== modified file 'lib/lp/code/interfaces/gitapi.py'
--- lib/lp/code/interfaces/gitapi.py 2018-09-28 13:53:41 +0000
+++ lib/lp/code/interfaces/gitapi.py 2018-10-19 08:51:38 +0000
@@ -68,8 +68,9 @@
68 not yet supported.68 not yet supported.
69 """69 """
7070
71 def listRefRules(self, repository, user):71 def checkRefPermissions(self, repository, ref_paths, user):
72 """Return the list of ref rules for `user` in `repository`72 """Return a list of ref rules for a `user` in a `repository` that
73 match the input refs.
7374
74 :returns: A list of rules for the user in the specified repository75 :returns: A list of rules for the user in the specified repository
75 """76 """
7677
=== modified file 'lib/lp/code/xmlrpc/git.py'
--- lib/lp/code/xmlrpc/git.py 2018-10-17 10:09:33 +0000
+++ lib/lp/code/xmlrpc/git.py 2018-10-19 08:51:38 +0000
@@ -8,6 +8,8 @@
8 'GitAPI',8 'GitAPI',
9 ]9 ]
1010
11from collections import defaultdict
12from fnmatch import fnmatch
11import sys13import sys
1214
13from pymacaroons import Macaroon15from pymacaroons import Macaroon
@@ -24,7 +26,10 @@
2426
25from lp.app.errors import NameLookupFailed27from lp.app.errors import NameLookupFailed
26from lp.app.validators import LaunchpadValidationError28from lp.app.validators import LaunchpadValidationError
27from lp.code.enums import GitRepositoryType29from lp.code.enums import (
30 GitGranteeType,
31 GitRepositoryType,
32 )
28from lp.code.errors import (33from lp.code.errors import (
29 GitRepositoryCreationException,34 GitRepositoryCreationException,
30 GitRepositoryCreationFault,35 GitRepositoryCreationFault,
@@ -350,67 +355,57 @@
350 permissions.add('force_push')355 permissions.add('force_push')
351 return permissions356 return permissions
352357
353 def _listRefRules(self, requester, translated_path):358 def _findMatchingRules(self, repository, ref_path):
359 """Find all matching rules for a given ref path"""
360 matching_rules = []
361 for rule in repository.rules:
362 if fnmatch(ref_path, rule.ref_pattern):
363 matching_rules.append(rule)
364 return matching_rules
365
366 def _checkRefPermissions(self, requester, translated_path, ref_paths):
354 repository = removeSecurityProxy(367 repository = removeSecurityProxy(
355 getUtility(IGitLookup).getByHostingPath(translated_path))368 getUtility(IGitLookup).getByHostingPath(translated_path))
356 is_owner = requester.inTeam(repository.owner)369 is_owner = requester.inTeam(repository.owner)
370 result = {}
371
372 grants_for_user = defaultdict(list)
357 grants = repository.findRuleGrantsByGrantee(requester)373 grants = repository.findRuleGrantsByGrantee(requester)
358 # If the user is the owner, get the grants for REPOSITORY_OWNER
359 # and add them to our available grants to match against
360 if is_owner:374 if is_owner:
361 owner_grants = repository.findRuleGrantsForRepositoryOwner()375 owner_grants = repository.findRuleGrantsForRepositoryOwner()
362 grants = grants.union(owner_grants)376 grants = grants.union(owner_grants)
363 result = []377 for grant in grants:
378 grants_for_user[grant.rule].append(grant)
364379
365 for rule in repository.rules:380 for ref in ref_paths:
366 # Do we have any grants for this rule, for this user?381 matching_rules = self._findMatchingRules(repository, ref)
367 matching_grants = [x for x in grants if x.rule == rule]382 if is_owner and not matching_rules:
368 # If we don't have any grants, but the user is the owner,383 result[ref] = ['create', 'push', 'force_push']
369 # they get a default grant to the ref specified by the rule.
370 if is_owner and not matching_grants:
371 result.append(
372 {'ref_pattern': rule.ref_pattern,
373 'permissions': ['create', 'push'],
374 })
375 continue384 continue
376385 seen_grantees = set()
377 # Permissions are a union of all the applicable grants
378 union_permissions = set()386 union_permissions = set()
379 for grant in matching_grants:387 for rule in matching_rules:
380 permissions = self._buildPermissions(grant)388 for grant in grants_for_user[rule]:
381 union_permissions.update(permissions)389 if (grant.grantee, grant.grantee_type) in seen_grantees:
390 continue
391 permissions = self._buildPermissions(grant)
392 union_permissions.update(permissions)
393 seen_grantees.add((grant.grantee, grant.grantee_type))
382394
383 # If the user is the repository owner, they essentially have395 owner_type = (None, GitGranteeType.REPOSITORY_OWNER)
384 # the equivalent of a default team grant, but only396 if is_owner and owner_type not in seen_grantees:
385 # if there is no explicit grant to them otherwise specified
386 if is_owner and not any(g for g in owner_grants if g.rule == rule):
387 union_permissions.update(['create', 'push'])397 union_permissions.update(['create', 'push'])
388398
389 # Sort the permissions from the set for consistency
390 sorted_permissions = self._sortPermissions(union_permissions)399 sorted_permissions = self._sortPermissions(union_permissions)
391 result.append(400 result[ref] = sorted_permissions
392 {'ref_pattern': rule.ref_pattern,
393 'permissions': sorted_permissions,
394 })
395
396 # The last rule is a default wildcard. The repository owner gets
397 # permissions to everything, whereas other people get no permissions.
398 if is_owner:
399 default_permissions = ['create', 'push', 'force_push']
400 else:
401 default_permissions = []
402 result.append(
403 {'ref_pattern': '*',
404 'permissions': default_permissions,
405 })
406
407 return result401 return result
408402
409 def listRefRules(self, translated_path, auth_params):403 def checkRefPermissions(self, translated_path, ref_paths, auth_params):
410 """See `IGitAPI`"""404 """ See `IGitAPI`"""
411 requester_id = auth_params.get("uid")405 requester_id = auth_params.get("uid")
412 return run_with_login(406 return run_with_login(
413 requester_id,407 requester_id,
414 self._listRefRules,408 self._checkRefPermissions,
415 translated_path,409 translated_path,
416 )410 ref_paths
411 )
417412
=== modified file 'lib/lp/code/xmlrpc/tests/test_git.py'
--- lib/lp/code/xmlrpc/tests/test_git.py 2018-10-17 10:09:33 +0000
+++ lib/lp/code/xmlrpc/tests/test_git.py 2018-10-19 08:51:38 +0000
@@ -266,410 +266,7 @@
266 self.assertEqual(266 self.assertEqual(
267 initial_count, getUtility(IAllGitRepositories).count())267 initial_count, getUtility(IAllGitRepositories).count())
268268
269 def test_listRefRules_simple(self):269 def _make_scenario_one_repository(self):
270 # Test that correct ref rules are retrieved for a Person
271 requester = self.factory.makePerson()
272 repository = removeSecurityProxy(
273 self.factory.makeGitRepository())
274
275 rule = self.factory.makeGitRule(repository)
276 self.factory.makeGitRuleGrant(
277 rule=rule, grantee=requester, can_push=True, can_create=True)
278
279 results = self.git_api.listRefRules(
280 repository.getInternalPath(),
281 {'uid': requester.id})
282 self.assertThat(results, MatchesListwise([
283 MatchesDict({
284 'ref_pattern': Equals('refs/heads/*'),
285 'permissions': Equals(['create', 'push']),
286 }),
287 MatchesDict({
288 'ref_pattern': Equals('*'),
289 'permissions': Equals([]),
290 }),
291 ]))
292
293 def test_listRefRules_with_other_grants(self):
294 # Test that findRuleGrantsByGrantee only returns relevant rules
295 requester = self.factory.makePerson()
296 other_user = self.factory.makePerson()
297 repository = removeSecurityProxy(
298 self.factory.makeGitRepository())
299
300 rule = self.factory.makeGitRule(repository)
301 self.factory.makeGitRuleGrant(
302 rule=rule, grantee=requester, can_push=True)
303 self.factory.makeGitRuleGrant(
304 rule=rule, grantee=other_user, can_create=True)
305
306 results = self.git_api.listRefRules(
307 repository.getInternalPath(),
308 {'uid': requester.id})
309 self.assertThat(results, MatchesListwise([
310 MatchesDict({
311 'ref_pattern': Equals('refs/heads/*'),
312 'permissions': Equals(['push']),
313 }),
314 MatchesDict({
315 'ref_pattern': Equals('*'),
316 'permissions': Equals([]),
317 }),
318 ]))
319
320 def test_listRefRules_owner_has_default(self):
321 owner = self.factory.makePerson()
322 repository = removeSecurityProxy(
323 self.factory.makeGitRepository(owner=owner))
324
325 results = self.git_api.listRefRules(
326 repository.getInternalPath(),
327 {'uid': owner.id})
328
329 self.assertThat(results, MatchesListwise([
330 MatchesDict({
331 'ref_pattern': Equals('*'),
332 'permissions': Equals(['create', 'push', 'force_push']),
333 }),
334 ]))
335
336 def test_listRefRules_owner_modifies_rules(self):
337 owner = self.factory.makePerson()
338 person = self.factory.makePerson()
339 repository = removeSecurityProxy(
340 self.factory.makeGitRepository(owner=owner))
341
342 rule = self.factory.makeGitRule(
343 repository, ref_pattern=u'refs/heads/stable/*')
344 self.factory.makeGitRuleGrant(
345 rule=rule, grantee=person, can_push=True)
346
347 results = self.git_api.listRefRules(
348 repository.getInternalPath(),
349 {'uid': owner.id})
350
351 self.assertThat(results, MatchesListwise([
352 MatchesDict({
353 'ref_pattern': Equals('refs/heads/stable/*'),
354 'permissions': Equals(['create', 'push']),
355 }),
356 MatchesDict({
357 'ref_pattern': Equals('*'),
358 'permissions': Equals(['create', 'push', 'force_push']),
359 }),
360 ]))
361
362 def test_listRefRules_owner_no_default_with_explicit(self):
363 owner = self.factory.makePerson()
364 repository = removeSecurityProxy(
365 self.factory.makeGitRepository(owner=owner))
366
367 rule = self.factory.makeGitRule(
368 repository, ref_pattern=u'refs/heads/stable/*')
369 self.factory.makeGitRuleGrant(
370 rule=rule, grantee=GitGranteeType.REPOSITORY_OWNER, can_push=True)
371
372 results = self.git_api.listRefRules(
373 repository.getInternalPath(),
374 {'uid': owner.id})
375
376 self.assertThat(results, MatchesListwise([
377 MatchesDict({
378 'ref_pattern': Equals('refs/heads/stable/*'),
379 'permissions': Equals(['push']),
380 }),
381 MatchesDict({
382 'ref_pattern': Equals('*'),
383 'permissions': Equals(['create', 'push', 'force_push']),
384 }),
385 ]))
386
387 def test_listRefRules_owner_modifies_rules_multiple_grants(self):
388 owner = self.factory.makePerson()
389 person = self.factory.makePerson()
390 repository = removeSecurityProxy(
391 self.factory.makeGitRepository(owner=owner))
392
393 rule = self.factory.makeGitRule(
394 repository, ref_pattern=u'refs/heads/stable/*')
395 self.factory.makeGitRuleGrant(
396 rule=rule, grantee=person, can_push=True, can_create=True)
397
398 self.factory.makeGitRuleGrant(
399 rule=rule, grantee=GitGranteeType.REPOSITORY_OWNER, can_push=True)
400
401 results = self.git_api.listRefRules(
402 repository.getInternalPath(),
403 {'uid': owner.id})
404
405 self.assertThat(results, MatchesListwise([
406 MatchesDict({
407 'ref_pattern': Equals('refs/heads/stable/*'),
408 'permissions': Equals(['push']),
409 }),
410 MatchesDict({
411 'ref_pattern': Equals('*'),
412 'permissions': Equals(['create', 'push', 'force_push']),
413 }),
414 ]))
415
416 def test_listRefRules_no_grants(self):
417 # User that has no grants and is not the owner
418 requester = self.factory.makePerson()
419 owner = self.factory.makePerson()
420 repository = removeSecurityProxy(
421 self.factory.makeGitRepository(owner=owner))
422
423 rule = self.factory.makeGitRule(repository)
424 self.factory.makeGitRuleGrant(
425 rule=rule, grantee=owner, can_push=True, can_create=True)
426
427 results = self.git_api.listRefRules(
428 repository.getInternalPath(),
429 {'uid': requester.id})
430 self.assertThat(results, MatchesListwise([
431 MatchesDict({
432 'ref_pattern': Equals('refs/heads/*'),
433 'permissions': Equals([]),
434 }),
435 MatchesDict({
436 'ref_pattern': Equals('*'),
437 'permissions': Equals([]),
438 }),
439 ]))
440
441 def test_listRefRules_owner_has_default_with_other_grant(self):
442 owner = self.factory.makePerson()
443 repository = removeSecurityProxy(
444 self.factory.makeGitRepository(owner=owner))
445
446 rule = self.factory.makeGitRule(
447 repository=repository, ref_pattern=u'refs/heads/master')
448 self.factory.makeGitRuleGrant(
449 rule=rule, grantee=owner, can_push=True, can_create=True)
450
451 results = self.git_api.listRefRules(
452 repository.getInternalPath(),
453 {'uid': owner.id})
454 # Default grant should be last in pattern
455 self.assertThat(results, MatchesListwise([
456 MatchesDict({
457 'ref_pattern': Equals('refs/heads/master'),
458 'permissions': Equals(['create', 'push']),
459 }),
460 MatchesDict({
461 'ref_pattern': Equals('*'),
462 'permissions': Equals(['create', 'push', 'force_push']),
463 }),
464 ]))
465
466 def test_listRefRules_owner_is_team(self):
467 member = self.factory.makePerson()
468 owner = self.factory.makeTeam(members=[member])
469 repository = removeSecurityProxy(
470 self.factory.makeGitRepository(
471 owner=owner, information_type=InformationType.USERDATA))
472
473 results = self.git_api.listRefRules(
474 repository.getInternalPath(),
475 {'uid': member.id})
476
477 # Should have default grant as member of owning team
478 self.assertThat(results, MatchesListwise([
479 MatchesDict({
480 'ref_pattern': Equals('*'),
481 'permissions': Equals(['create', 'push', 'force_push']),
482 }),
483 ]))
484
485 def test_listRefRules_owner_is_team_with_grants(self):
486 member = self.factory.makePerson()
487 owner = self.factory.makeTeam(members=[member])
488 repository = removeSecurityProxy(
489 self.factory.makeGitRepository(
490 owner=owner, information_type=InformationType.USERDATA))
491
492 rule = self.factory.makeGitRule(
493 repository=repository, ref_pattern=u'refs/heads/master')
494 self.factory.makeGitRuleGrant(
495 rule=rule, grantee=owner, can_push=True, can_create=True)
496
497 results = self.git_api.listRefRules(
498 repository.getInternalPath(),
499 {'uid': member.id})
500
501 # Should have default grant as member of owning team
502 self.assertThat(results, MatchesListwise([
503 MatchesDict({
504 'ref_pattern': Equals('refs/heads/master'),
505 'permissions': Equals(['create', 'push']),
506 }),
507 MatchesDict({
508 'ref_pattern': Equals('*'),
509 'permissions': Equals(['create', 'push', 'force_push']),
510 }),
511 ]))
512
513 def test_listRefRules_owner_is_team_with_grants_to_person(self):
514 member = self.factory.makePerson()
515 other_member = self.factory.makePerson()
516 owner = self.factory.makeTeam(members=[member, other_member])
517 repository = removeSecurityProxy(
518 self.factory.makeGitRepository(
519 owner=owner, information_type=InformationType.USERDATA))
520
521 rule = self.factory.makeGitRule(
522 repository=repository, ref_pattern=u'refs/heads/master')
523 self.factory.makeGitRuleGrant(
524 rule=rule, grantee=GitGranteeType.REPOSITORY_OWNER,
525 can_push=True, can_create=True)
526
527 rule = self.factory.makeGitRule(
528 repository=repository, ref_pattern=u'refs/heads/tags')
529 self.factory.makeGitRuleGrant(
530 rule=rule, grantee=member, can_create=True)
531
532 # This should not appear
533 self.factory.makeGitRuleGrant(
534 rule=rule, grantee=other_member, can_push=True)
535
536 results = self.git_api.listRefRules(
537 repository.getInternalPath(),
538 {'uid': member.id})
539
540 # Should have default grant as member of owning team
541 self.assertThat(results, MatchesListwise([
542 MatchesDict({
543 'ref_pattern': Equals('refs/heads/master'),
544 'permissions': Equals(['create', 'push']),
545 }),
546 MatchesDict({
547 'ref_pattern': Equals('refs/heads/tags'),
548 'permissions': Equals(['create', 'push']),
549 }),
550 MatchesDict({
551 'ref_pattern': Equals('*'),
552 'permissions': Equals(['create', 'push', 'force_push']),
553 }),
554 ]))
555
556 def test_listRefRules_multiple_grants_to_same_ref_with_owner(self):
557 member = self.factory.makePerson()
558 owner = self.factory.makeTeam(members=[member])
559 repository = removeSecurityProxy(
560 self.factory.makeGitRepository(owner=owner))
561
562 rule = self.factory.makeGitRule(repository=repository)
563 self.factory.makeGitRuleGrant(
564 rule=rule, grantee=member, can_create=True)
565 self.factory.makeGitRuleGrant(
566 rule=rule, grantee=owner, can_push=True)
567
568 results = self.git_api.listRefRules(
569 repository.getInternalPath(),
570 {'uid': member.id})
571
572 self.assertThat(results, MatchesListwise([
573 MatchesDict({
574 'ref_pattern': Equals('refs/heads/*'),
575 'permissions': Equals(['create', 'push']),
576 }),
577 MatchesDict({
578 'ref_pattern': Equals('*'),
579 'permissions': Equals(['create', 'push', 'force_push']),
580 }),
581 ]))
582
583 def test_listRefRules_multiple_grants_collapsing(self):
584 member = self.factory.makePerson()
585 second_member = self.factory.makePerson()
586 third_member = self.factory.makePerson()
587 owner = self.factory.makePerson()
588 repository = removeSecurityProxy(
589 self.factory.makeGitRepository(owner=owner))
590
591 rule = self.factory.makeGitRule(repository=repository)
592 self.factory.makeGitRuleGrant(
593 rule=rule, grantee=member, can_create=True)
594 self.factory.makeGitRuleGrant(
595 rule=rule, grantee=second_member, can_push=True)
596 self.factory.makeGitRuleGrant(
597 rule=rule, grantee=third_member, can_force_push=True)
598
599 results = self.git_api.listRefRules(
600 repository.getInternalPath(),
601 {'uid': owner.id})
602
603 self.assertThat(results, MatchesListwise([
604 MatchesDict({
605 'ref_pattern': Equals('refs/heads/*'),
606 'permissions': Equals(['create', 'push']),
607 }),
608 MatchesDict({
609 'ref_pattern': Equals('*'),
610 'permissions': Equals(['create', 'push', 'force_push']),
611 }),
612 ]))
613
614 def test_listRefRules_grantee_owner_type(self):
615 owner = self.factory.makePerson()
616 repository = removeSecurityProxy(
617 self.factory.makeGitRepository(owner=owner))
618 rule = self.factory.makeGitRule(repository=repository)
619 self.factory.makeGitRuleGrant(
620 rule=rule, grantee=GitGranteeType.REPOSITORY_OWNER,
621 can_create=True)
622
623 results = self.git_api.listRefRules(
624 repository.getInternalPath(),
625 {'uid': owner.id})
626
627 self.assertThat(results, MatchesListwise([
628 MatchesDict({
629 'ref_pattern': Equals('refs/heads/*'),
630 'permissions': Equals(['create']),
631 }),
632 MatchesDict({
633 'ref_pattern': Equals('*'),
634 'permissions': Equals(['create', 'push', 'force_push']),
635 }),
636 ]))
637
638 def test_listRefRules_grantee_owner_type_and_other_grants(self):
639 owner = self.factory.makePerson()
640 other_person = self.factory.makePerson()
641 repository = removeSecurityProxy(
642 self.factory.makeGitRepository(owner=owner))
643 rule = self.factory.makeGitRule(repository=repository)
644 self.factory.makeGitRuleGrant(
645 rule=rule, grantee=GitGranteeType.REPOSITORY_OWNER,
646 can_create=True)
647
648 rule = self.factory.makeGitRule(
649 repository=repository, ref_pattern=u'refs/heads/other')
650 self.factory.makeGitRuleGrant(
651 rule=rule, grantee=other_person, can_push=True)
652
653 results = self.git_api.listRefRules(
654 repository.getInternalPath(),
655 {'uid': owner.id})
656
657 self.assertThat(results, MatchesListwise([
658 MatchesDict({
659 'ref_pattern': Equals('refs/heads/other'),
660 'permissions': Equals(['create', 'push']),
661 }),
662 MatchesDict({
663 'ref_pattern': Equals('refs/heads/*'),
664 'permissions': Equals(['create']),
665 }),
666 MatchesDict({
667 'ref_pattern': Equals('*'),
668 'permissions': Equals(['create', 'push', 'force_push']),
669 }),
670 ]))
671
672 def test_listRefRules_grantee_example_one(self):
673 user_a = self.factory.makePerson()270 user_a = self.factory.makePerson()
674 user_b = self.factory.makePerson()271 user_b = self.factory.makePerson()
675 user_c = self.factory.makePerson()272 user_c = self.factory.makePerson()
@@ -686,6 +283,10 @@
686 can_force_push=True)283 can_force_push=True)
687284
688 rule = self.factory.makeGitRule(285 rule = self.factory.makeGitRule(
286 repository, ref_pattern=u'refs/heads/stable/protected')
287 self.factory.makeGitRuleGrant(rule=rule, grantee=stable_team)
288
289 rule = self.factory.makeGitRule(
689 repository, ref_pattern=u'refs/heads/archived/*')290 repository, ref_pattern=u'refs/heads/archived/*')
690 self.factory.makeGitRuleGrant(291 self.factory.makeGitRuleGrant(
691 rule=rule, grantee=GitGranteeType.REPOSITORY_OWNER)292 rule=rule, grantee=GitGranteeType.REPOSITORY_OWNER)
@@ -710,247 +311,158 @@
710 self.factory.makeGitRuleGrant(311 self.factory.makeGitRuleGrant(
711 rule=rule, grantee=stable_team, can_create=True)312 rule=rule, grantee=stable_team, can_create=True)
712313
713 results = self.git_api.listRefRules(314 test_ref_paths = [
714 repository.getInternalPath(),315 'refs/heads/stable/next', 'refs/heads/stable/protected',
316 'refs/heads/stable/foo', 'refs/heads/archived/foo',
317 'refs/heads/foo/next', 'refs/heads/unprotected',
318 'refs/tags/1.0',
319 ]
320
321 return (user_a, user_b, user_c, stable_team, next_team, repository,
322 test_ref_paths)
323
324 def test_checkRefPermissions_scenario_one_user_a(self):
325 user_a, _, _, _, _, repo, paths = self._make_scenario_one_repository()
326
327 results = self.git_api.checkRefPermissions(
328 repo.getInternalPath(),
329 paths,
715 {'uid': user_a.id})330 {'uid': user_a.id})
716331
717 self.assertThat(results, MatchesListwise([332 self.assertThat(results, MatchesDict({
718 MatchesDict(333 'refs/heads/stable/next': Equals(['push', 'force_push']),
719 {'ref_pattern': Equals('refs/heads/stable/next'),334 'refs/heads/stable/protected': Equals(['create', 'push']),
720 'permissions': Equals(['push', 'force_push']),335 'refs/heads/stable/foo': Equals(['create', 'push']),
721 }),336 'refs/heads/archived/foo': Equals([]),
722 MatchesDict(337 'refs/heads/foo/next': Equals(['create', 'push']),
723 {'ref_pattern': Equals('refs/heads/archived/*'),338 'refs/heads/unprotected': Equals(['create', 'push', 'force_push']),
724 'permissions': Equals([]),339 'refs/tags/1.0': Equals(['create']),
725 }),340 }))
726 MatchesDict(341
727 {'ref_pattern': Equals('refs/heads/stable/*'),342 def test_checkRefPermissions_scenario_one_user_b(self):
728 'permissions': Equals(['create', 'push']),343 _, user_b, _, _, _, repo, paths = self._make_scenario_one_repository()
729 }),344
730 MatchesDict(345 results = self.git_api.checkRefPermissions(
731 {'ref_pattern': Equals('refs/heads/*/next'),346 repo.getInternalPath(),
732 'permissions': Equals(['create', 'push']),347 paths,
733 }),
734 MatchesDict(
735 {'ref_pattern': Equals('refs/tags/*'),
736 'permissions': Equals(['create']),
737 }),
738 MatchesDict(
739 {'ref_pattern': Equals('*'),
740 'permissions': Equals(['create', 'push', 'force_push']),
741 }),
742 ]))
743
744 def test_listRefRules_grantee_example_two(self):
745 user_a = self.factory.makePerson()
746 user_b = self.factory.makePerson()
747 user_c = self.factory.makePerson()
748 stable_team = self.factory.makeTeam(members=[user_a, user_b])
749 next_team = self.factory.makeTeam(members=[user_b, user_c])
750
751 repository = removeSecurityProxy(
752 self.factory.makeGitRepository(owner=user_a))
753
754 rule = self.factory.makeGitRule(
755 repository, ref_pattern=u'refs/heads/stable/next')
756 self.factory.makeGitRuleGrant(
757 rule=rule, grantee=user_a, can_force_push=True)
758
759 rule = self.factory.makeGitRule(
760 repository, ref_pattern=u'refs/heads/archived/*')
761 self.factory.makeGitRuleGrant(
762 rule=rule, grantee=user_a)
763 self.factory.makeGitRuleGrant(
764 rule=rule, grantee=user_b, can_create=True)
765
766 rule = self.factory.makeGitRule(
767 repository, ref_pattern=u'refs/heads/stable/*')
768 self.factory.makeGitRuleGrant(
769 rule=rule, grantee=stable_team, can_push=True)
770
771 rule = self.factory.makeGitRule(
772 repository, ref_pattern=u'refs/heads/*/next')
773 self.factory.makeGitRuleGrant(
774 rule=rule, grantee=next_team, can_force_push=True)
775
776 rule = self.factory.makeGitRule(
777 repository, ref_pattern=u'refs/tags/*')
778 self.factory.makeGitRuleGrant(
779 rule=rule, grantee=user_a, can_create=True)
780 self.factory.makeGitRuleGrant(
781 rule=rule, grantee=stable_team, can_create=True)
782
783 results = self.git_api.listRefRules(
784 repository.getInternalPath(),
785 {'uid': user_b.id})348 {'uid': user_b.id})
786349
787 self.assertThat(results, MatchesListwise([350 self.assertThat(results, MatchesDict({
788 MatchesDict(351 'refs/heads/stable/next': Equals(['push', 'force_push']),
789 {'ref_pattern': Equals('refs/heads/stable/next'),352 'refs/heads/stable/protected': Equals([]),
790 'permissions': Equals([]),353 'refs/heads/stable/foo': Equals(['push']),
791 }),354 'refs/heads/archived/foo': Equals(['create']),
792 MatchesDict(355 'refs/heads/foo/next': Equals(['push', 'force_push']),
793 {'ref_pattern': Equals('refs/heads/archived/*'),356 'refs/heads/unprotected': Equals([]),
794 'permissions': Equals(['create']),357 'refs/tags/1.0': Equals(['create']),
795 }),358 }))
796 MatchesDict(359
797 {'ref_pattern': Equals('refs/heads/stable/*'),360 def test_checkRefPermissions_scenario_one_user_c(self):
798 'permissions': Equals(['push']),361 _, _, user_c, _, _, repo, paths = self._make_scenario_one_repository()
799 }),362
800 MatchesDict(363 results = self.git_api.checkRefPermissions(
801 {'ref_pattern': Equals('refs/heads/*/next'),364 repo.getInternalPath(),
802 'permissions': Equals(['push', 'force_push']),365 paths,
803 }),
804 MatchesDict(
805 {'ref_pattern': Equals('refs/tags/*'),
806 'permissions': Equals(['create']),
807 }),
808 MatchesDict(
809 {'ref_pattern': Equals('*'),
810 'permissions': Equals([]),
811 }),
812 ]))
813
814 def test_listRefRules_grantee_example_three(self):
815 user_a = self.factory.makePerson()
816 user_b = self.factory.makePerson()
817 user_c = self.factory.makePerson()
818 stable_team = self.factory.makeTeam(members=[user_a, user_b])
819 next_team = self.factory.makeTeam(members=[user_b, user_c])
820
821 repository = removeSecurityProxy(
822 self.factory.makeGitRepository(owner=user_a))
823
824 rule = self.factory.makeGitRule(
825 repository, ref_pattern=u'refs/heads/stable/next')
826 self.factory.makeGitRuleGrant(
827 rule=rule, grantee=user_a, can_force_push=True)
828
829 rule = self.factory.makeGitRule(
830 repository, ref_pattern=u'refs/heads/archived/*')
831 self.factory.makeGitRuleGrant(
832 rule=rule, grantee=user_a)
833 self.factory.makeGitRuleGrant(
834 rule=rule, grantee=user_b, can_create=True)
835
836 rule = self.factory.makeGitRule(
837 repository, ref_pattern=u'refs/heads/stable/*')
838 self.factory.makeGitRuleGrant(
839 rule=rule, grantee=stable_team, can_push=True)
840
841 rule = self.factory.makeGitRule(
842 repository, ref_pattern=u'refs/heads/*/next')
843 self.factory.makeGitRuleGrant(
844 rule=rule, grantee=next_team, can_force_push=True)
845
846 rule = self.factory.makeGitRule(
847 repository, ref_pattern=u'refs/tags/*')
848 self.factory.makeGitRuleGrant(
849 rule=rule, grantee=user_a, can_create=True)
850 self.factory.makeGitRuleGrant(
851 rule=rule, grantee=stable_team, can_create=True)
852
853 results = self.git_api.listRefRules(
854 repository.getInternalPath(),
855 {'uid': user_c.id})366 {'uid': user_c.id})
856367
857 self.assertThat(results, MatchesListwise([368 self.assertThat(results, MatchesDict({
858 MatchesDict(369 'refs/heads/stable/next': Equals(['push', 'force_push']),
859 {'ref_pattern': Equals('refs/heads/stable/next'),370 'refs/heads/stable/protected': Equals([]),
860 'permissions': Equals([]),371 'refs/heads/stable/foo': Equals([]),
861 }),372 'refs/heads/archived/foo': Equals([]),
862 MatchesDict(373 'refs/heads/foo/next': Equals(['push', 'force_push']),
863 {'ref_pattern': Equals('refs/heads/archived/*'),374 'refs/heads/unprotected': Equals([]),
864 'permissions': Equals([]),375 'refs/tags/1.0': Equals([]),
865 }),376 }))
866 MatchesDict(
867 {'ref_pattern': Equals('refs/heads/stable/*'),
868 'permissions': Equals([]),
869 }),
870 MatchesDict(
871 {'ref_pattern': Equals('refs/heads/*/next'),
872 'permissions': Equals(['push', 'force_push']),
873 }),
874 MatchesDict(
875 {'ref_pattern': Equals('refs/tags/*'),
876 'permissions': Equals([]),
877 }),
878 MatchesDict(
879 {'ref_pattern': Equals('*'),
880 'permissions': Equals([]),
881 }),
882 ]))
883377
884 def test_listRefRules_grantee_example_four(self):378 def test_checkRefPermissions_scenario_one_user_d(self):
885 user_a = self.factory.makePerson()
886 user_b = self.factory.makePerson()
887 user_c = self.factory.makePerson()
888 user_d = self.factory.makePerson()379 user_d = self.factory.makePerson()
889 stable_team = self.factory.makeTeam(members=[user_a, user_b])380 _, _, user_c, _, _, repo, paths = self._make_scenario_one_repository()
890 next_team = self.factory.makeTeam(members=[user_b, user_c])381
891382 results = self.git_api.checkRefPermissions(
892 repository = removeSecurityProxy(383 repo.getInternalPath(),
893 self.factory.makeGitRepository(owner=user_a))384 paths,
894
895 rule = self.factory.makeGitRule(
896 repository, ref_pattern=u'refs/heads/stable/next')
897 self.factory.makeGitRuleGrant(
898 rule=rule, grantee=user_a, can_force_push=True)
899
900 rule = self.factory.makeGitRule(
901 repository, ref_pattern=u'refs/heads/archived/*')
902 self.factory.makeGitRuleGrant(
903 rule=rule, grantee=user_a)
904 self.factory.makeGitRuleGrant(
905 rule=rule, grantee=user_b, can_create=True)
906
907 rule = self.factory.makeGitRule(
908 repository, ref_pattern=u'refs/heads/stable/*')
909 self.factory.makeGitRuleGrant(
910 rule=rule, grantee=stable_team, can_push=True)
911
912 rule = self.factory.makeGitRule(
913 repository, ref_pattern=u'refs/heads/*/next')
914 self.factory.makeGitRuleGrant(
915 rule=rule, grantee=next_team, can_force_push=True)
916
917 rule = self.factory.makeGitRule(
918 repository, ref_pattern=u'refs/tags/*')
919 self.factory.makeGitRuleGrant(
920 rule=rule, grantee=user_a, can_create=True)
921 self.factory.makeGitRuleGrant(
922 rule=rule, grantee=stable_team, can_create=True)
923
924 results = self.git_api.listRefRules(
925 repository.getInternalPath(),
926 {'uid': user_d.id})385 {'uid': user_d.id})
927386
928 self.assertThat(results, MatchesListwise([387 self.assertThat(results, MatchesDict({
929 MatchesDict(388 'refs/heads/stable/next': Equals([]),
930 {'ref_pattern': Equals('refs/heads/stable/next'),389 'refs/heads/stable/protected': Equals([]),
931 'permissions': Equals([]),390 'refs/heads/stable/foo': Equals([]),
932 }),391 'refs/heads/archived/foo': Equals([]),
933 MatchesDict(392 'refs/heads/foo/next': Equals([]),
934 {'ref_pattern': Equals('refs/heads/archived/*'),393 'refs/heads/unprotected': Equals([]),
935 'permissions': Equals([]),394 'refs/tags/1.0': Equals([]),
936 }),395 }))
937 MatchesDict(396
938 {'ref_pattern': Equals('refs/heads/stable/*'),397 def _make_scenario_two_repository(self):
939 'permissions': Equals([]),398 user_a = self.factory.makePerson()
940 }),399 user_b = self.factory.makePerson()
941 MatchesDict(400
942 {'ref_pattern': Equals('refs/heads/*/next'),401 repository = removeSecurityProxy(
943 'permissions': Equals([]),402 self.factory.makeGitRepository(owner=user_a))
944 }),403
945 MatchesDict(404 rule = self.factory.makeGitRule(
946 {'ref_pattern': Equals('refs/tags/*'),405 repository, ref_pattern=u'refs/heads/master')
947 'permissions': Equals([]),406 self.factory.makeGitRuleGrant(
948 }),407 rule=rule, grantee=user_b, can_push=True)
949 MatchesDict(408
950 {'ref_pattern': Equals('*'),409 rule = self.factory.makeGitRule(
951 'permissions': Equals([]),410 repository, ref_pattern=u'refs/heads/*')
952 }),411 self.factory.makeGitRuleGrant(
953 ]))412 rule=rule, grantee=GitGranteeType.REPOSITORY_OWNER,
413 can_create=True, can_push=True, can_force_push=True)
414
415 rule = self.factory.makeGitRule(
416 repository, ref_pattern=u'refs/tags/*')
417 self.factory.makeGitRuleGrant(
418 rule=rule, grantee=user_b, can_push=True)
419
420 test_ref_paths = ['refs/heads/master', 'refs/heads/foo',
421 'refs/tags/1.0', 'refs/other']
422 return user_a, user_b, repository, test_ref_paths
423
424 def test_checkRefPermissions_scenario_two_user_a(self):
425 user_a, _, repo, paths = self._make_scenario_two_repository()
426 results = self.git_api.checkRefPermissions(
427 repo.getInternalPath(),
428 paths,
429 {'uid': user_a.id})
430
431 self.assertThat(results, MatchesDict({
432 'refs/heads/master': Equals(['create', 'push', 'force_push']),
433 'refs/heads/foo': Equals(['create', 'push', 'force_push']),
434 'refs/tags/1.0': Equals(['create', 'push']),
435 'refs/other': Equals(['create', 'push', 'force_push']),
436 }))
437
438 def test_checkRefPermissions_scenario_two_user_b(self):
439 _, user_b, repo, paths = self._make_scenario_two_repository()
440 results = self.git_api.checkRefPermissions(
441 repo.getInternalPath(),
442 paths,
443 {'uid': user_b.id})
444
445 self.assertThat(results, MatchesDict({
446 'refs/heads/master': Equals(['push']),
447 'refs/heads/foo': Equals([]),
448 'refs/tags/1.0': Equals(['push']),
449 'refs/other': Equals([]),
450 }))
451
452 def test_checkRefPermissions_scenario_two_user_c(self):
453 _, _, repo, paths = self._make_scenario_two_repository()
454 user_c = self.factory.makePerson()
455 results = self.git_api.checkRefPermissions(
456 repo.getInternalPath(),
457 paths,
458 {'uid': user_c.id})
459
460 self.assertThat(results, MatchesDict({
461 'refs/heads/master': Equals([]),
462 'refs/heads/foo': Equals([]),
463 'refs/tags/1.0': Equals([]),
464 'refs/other': Equals([]),
465 }))
954466
955467
956class TestGitAPI(TestGitAPIMixin, TestCaseWithFactory):468class TestGitAPI(TestGitAPIMixin, TestCaseWithFactory):