Merge lp:~cjwatson/launchpad/git-finish-sharing into lp:launchpad

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: no longer in the source branch.
Merged at revision: 17366
Proposed branch: lp:~cjwatson/launchpad/git-finish-sharing
Merge into: lp:launchpad
Prerequisite: lp:~cjwatson/launchpad/git-collection
Diff against target: 1160 lines (+429/-105)
8 files modified
lib/lp/code/model/gitrepository.py (+9/-4)
lib/lp/code/model/hasgitrepositories.py (+3/-3)
lib/lp/code/model/tests/test_gitrepository.py (+136/-1)
lib/lp/registry/services/sharingservice.py (+44/-37)
lib/lp/registry/services/tests/test_sharingservice.py (+163/-45)
lib/lp/registry/tests/test_accesspolicy.py (+8/-1)
lib/lp/registry/tests/test_sharingjob.py (+58/-13)
lib/lp/security.py (+8/-1)
To merge this branch: bzr merge lp:~cjwatson/launchpad/git-finish-sharing
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+250663@code.launchpad.net

Commit message

Use Git repository collections to implement visibility and sharing.

Description of the change

Use Git repository collections to implement visibility and sharing.

There are a few bits we can't do until there's some analogue of BranchSubscription, but otherwise this should be a reasonably complete privacy implementation now.

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/code/model/gitrepository.py'
--- lib/lp/code/model/gitrepository.py 2015-02-26 14:25:26 +0000
+++ lib/lp/code/model/gitrepository.py 2015-02-27 11:27:39 +0000
@@ -26,6 +26,7 @@
26 )26 )
27from zope.component import getUtility27from zope.component import getUtility
28from zope.interface import implements28from zope.interface import implements
29from zope.security.proxy import removeSecurityProxy
2930
30from lp.app.enums import (31from lp.app.enums import (
31 InformationType,32 InformationType,
@@ -39,6 +40,7 @@
39 GitDefaultConflict,40 GitDefaultConflict,
40 GitTargetError,41 GitTargetError,
41 )42 )
43from lp.code.interfaces.gitcollection import IAllGitRepositories
42from lp.code.interfaces.gitlookup import IGitLookup44from lp.code.interfaces.gitlookup import IGitLookup
43from lp.code.interfaces.gitnamespace import (45from lp.code.interfaces.gitnamespace import (
44 get_git_namespace,46 get_git_namespace,
@@ -291,9 +293,8 @@
291 elif user.id in self._known_viewers:293 elif user.id in self._known_viewers:
292 return True294 return True
293 else:295 else:
294 # XXX cjwatson 2015-02-06: Fill this in once IGitCollection is296 return not getUtility(IAllGitRepositories).withIds(
295 # in place.297 self.id).visibleByUser(user).is_empty()
296 return False
297298
298 def getAllowedInformationTypes(self, user):299 def getAllowedInformationTypes(self, user):
299 """See `IGitRepository`."""300 """See `IGitRepository`."""
@@ -359,7 +360,11 @@
359 def getByPath(self, user, path):360 def getByPath(self, user, path):
360 """See `IGitRepositorySet`."""361 """See `IGitRepositorySet`."""
361 repository = getUtility(IGitLookup).getByPath(path)362 repository = getUtility(IGitLookup).getByPath(path)
362 if repository is not None and repository.visibleByUser(user):363 if repository is None:
364 return None
365 # removeSecurityProxy is safe here since we're explicitly performing
366 # a permission check.
367 if removeSecurityProxy(repository).visibleByUser(user):
363 return repository368 return repository
364 return None369 return None
365370
366371
=== modified file 'lib/lp/code/model/hasgitrepositories.py'
--- lib/lp/code/model/hasgitrepositories.py 2015-02-10 01:03:48 +0000
+++ lib/lp/code/model/hasgitrepositories.py 2015-02-27 11:27:39 +0000
@@ -8,6 +8,7 @@
88
9from zope.component import getUtility9from zope.component import getUtility
1010
11from lp.code.interfaces.gitcollection import IGitCollection
11from lp.code.interfaces.gitrepository import IGitRepositorySet12from lp.code.interfaces.gitrepository import IGitRepositorySet
1213
1314
@@ -23,6 +24,5 @@
2324
24 def getGitRepositories(self, visible_by_user=None, eager_load=False):25 def getGitRepositories(self, visible_by_user=None, eager_load=False):
25 """See `IHasGitRepositories`."""26 """See `IHasGitRepositories`."""
26 # XXX cjwatson 2015-02-06: Fill this in once IGitCollection is in27 collection = IGitCollection(self).visibleByUser(visible_by_user)
27 # place.28 return collection.getRepositories(eager_load=eager_load)
28 raise NotImplementedError
2929
=== modified file 'lib/lp/code/model/tests/test_gitrepository.py'
--- lib/lp/code/model/tests/test_gitrepository.py 2015-02-26 11:43:12 +0000
+++ lib/lp/code/model/tests/test_gitrepository.py 2015-02-27 11:27:39 +0000
@@ -34,11 +34,21 @@
34 IGitRepository,34 IGitRepository,
35 IGitRepositorySet,35 IGitRepositorySet,
36 )36 )
37from lp.registry.enums import BranchSharingPolicy37from lp.registry.enums import (
38 BranchSharingPolicy,
39 PersonVisibility,
40 TeamMembershipPolicy,
41 )
42from lp.registry.interfaces.accesspolicy import (
43 IAccessArtifactSource,
44 IAccessPolicyArtifactSource,
45 IAccessPolicySource,
46 )
38from lp.registry.interfaces.persondistributionsourcepackage import (47from lp.registry.interfaces.persondistributionsourcepackage import (
39 IPersonDistributionSourcePackageFactory,48 IPersonDistributionSourcePackageFactory,
40 )49 )
41from lp.registry.interfaces.personproduct import IPersonProductFactory50from lp.registry.interfaces.personproduct import IPersonProductFactory
51from lp.registry.tests.test_accesspolicy import get_policies_for_artifact
42from lp.services.database.constants import UTC_NOW52from lp.services.database.constants import UTC_NOW
43from lp.services.webapp.authorization import check_permission53from lp.services.webapp.authorization import check_permission
44from lp.testing import (54from lp.testing import (
@@ -134,6 +144,15 @@
134 self.repository_set.setDefaultRepository(project, repository)144 self.repository_set.setDefaultRepository(project, repository)
135 self.assertGitIdentity(repository, project.name)145 self.assertGitIdentity(repository, project.name)
136146
147 def test_git_identity_private_default_for_project(self):
148 # Private repositories also have a short lp: URL.
149 project = self.factory.makeProduct()
150 repository = self.factory.makeGitRepository(
151 target=project, information_type=InformationType.USERDATA)
152 with admin_logged_in():
153 self.repository_set.setDefaultRepository(project, repository)
154 self.assertGitIdentity(repository, project.name)
155
137 def test_git_identity_default_for_package(self):156 def test_git_identity_default_for_package(self):
138 # If a repository is the default for a package, then its Git157 # If a repository is the default for a package, then its Git
139 # identity uses the path to that package.158 # identity uses the path to that package.
@@ -302,6 +321,57 @@
302 self.assertEqual(namespace, repository.namespace)321 self.assertEqual(namespace, repository.namespace)
303322
304323
324class TestGitRepositoryPrivacy(TestCaseWithFactory):
325 """Tests for Git repository privacy."""
326
327 layer = DatabaseFunctionalLayer
328
329 def setUp(self):
330 # Use an admin user as we aren't checking edit permissions here.
331 super(TestGitRepositoryPrivacy, self).setUp("admin@canonical.com")
332
333 def test_personal_repositories_for_private_teams_are_private(self):
334 team = self.factory.makeTeam(
335 membership_policy=TeamMembershipPolicy.MODERATED,
336 visibility=PersonVisibility.PRIVATE)
337 repository = self.factory.makeGitRepository(owner=team, target=team)
338 self.assertTrue(repository.private)
339 self.assertEqual(
340 InformationType.PROPRIETARY, repository.information_type)
341
342 def test__reconcileAccess_for_project_repository(self):
343 # _reconcileAccess uses a project policy for a project repository.
344 repository = self.factory.makeGitRepository(
345 information_type=InformationType.USERDATA)
346 [artifact] = getUtility(IAccessArtifactSource).ensure([repository])
347 getUtility(IAccessPolicyArtifactSource).deleteByArtifact([artifact])
348 removeSecurityProxy(repository)._reconcileAccess()
349 self.assertContentEqual(
350 getUtility(IAccessPolicySource).find(
351 [(repository.target, InformationType.USERDATA)]),
352 get_policies_for_artifact(repository))
353
354 def test__reconcileAccess_for_package_repository(self):
355 # Git repository privacy isn't yet supported for distributions, so
356 # no AccessPolicyArtifact is created for a package repository.
357 repository = self.factory.makeGitRepository(
358 target=self.factory.makeDistributionSourcePackage(),
359 information_type=InformationType.USERDATA)
360 removeSecurityProxy(repository)._reconcileAccess()
361 self.assertEqual([], get_policies_for_artifact(repository))
362
363 def test__reconcileAccess_for_personal_repository(self):
364 # _reconcileAccess uses a person policy for a personal repository.
365 team_owner = self.factory.makeTeam()
366 repository = self.factory.makeGitRepository(
367 owner=team_owner, target=team_owner,
368 information_type=InformationType.USERDATA)
369 removeSecurityProxy(repository)._reconcileAccess()
370 self.assertContentEqual(
371 getUtility(IAccessPolicySource).findByTeam([team_owner]),
372 get_policies_for_artifact(repository))
373
374
305class TestGitRepositoryGetAllowedInformationTypes(TestCaseWithFactory):375class TestGitRepositoryGetAllowedInformationTypes(TestCaseWithFactory):
306 """Test `IGitRepository.getAllowedInformationTypes`."""376 """Test `IGitRepository.getAllowedInformationTypes`."""
307377
@@ -354,6 +424,17 @@
354 self.assertFalse(424 self.assertFalse(
355 check_permission("launchpad.Moderate", repository))425 check_permission("launchpad.Moderate", repository))
356426
427 def test_methods_smoketest(self):
428 # Users with launchpad.Moderate can call transitionToInformationType.
429 project = self.factory.makeProduct()
430 repository = self.factory.makeGitRepository(target=project)
431 with person_logged_in(project.owner):
432 project.setBranchSharingPolicy(BranchSharingPolicy.PUBLIC)
433 repository.transitionToInformationType(
434 InformationType.PRIVATESECURITY, project.owner)
435 self.assertEqual(
436 InformationType.PRIVATESECURITY, repository.information_type)
437
357 def test_attribute_smoketest(self):438 def test_attribute_smoketest(self):
358 # Users with launchpad.Moderate can set attributes.439 # Users with launchpad.Moderate can set attributes.
359 project = self.factory.makeProduct()440 project = self.factory.makeProduct()
@@ -486,6 +567,47 @@
486 repository.setTarget(target=owner, user=owner)567 repository.setTarget(target=owner, user=owner)
487 self.assertEqual(owner, repository.target)568 self.assertEqual(owner, repository.target)
488569
570 def test_private_personal_forbidden_for_public_teams(self):
571 # Only private teams can have private personal repositories.
572 owner = self.factory.makeTeam()
573 repository = self.factory.makeGitRepository(
574 owner=owner, information_type=InformationType.USERDATA)
575 with admin_logged_in():
576 self.assertRaises(
577 GitTargetError, repository.setTarget, target=owner, user=owner)
578
579 def test_private_personal_allowed_for_private_teams(self):
580 # Only private teams can have private personal repositories.
581 owner = self.factory.makeTeam(visibility=PersonVisibility.PRIVATE)
582 with person_logged_in(owner):
583 repository = self.factory.makeGitRepository(
584 owner=owner, information_type=InformationType.USERDATA)
585 repository.setTarget(target=owner, user=owner)
586 self.assertEqual(owner, repository.target)
587
588 def test_reconciles_access(self):
589 # setTarget calls _reconcileAccess to make the sharing schema
590 # match the new target.
591 repository = self.factory.makeGitRepository(
592 information_type=InformationType.USERDATA)
593 new_project = self.factory.makeProduct()
594 with admin_logged_in():
595 repository.setTarget(target=new_project, user=repository.owner)
596 self.assertEqual(
597 new_project, get_policies_for_artifact(repository)[0].pillar)
598
599 def test_reconciles_access_personal(self):
600 # setTarget calls _reconcileAccess to make the sharing schema
601 # correct for a private personal repository.
602 owner = self.factory.makeTeam(visibility=PersonVisibility.PRIVATE)
603 with person_logged_in(owner):
604 repository = self.factory.makeGitRepository(
605 owner=owner, target=owner,
606 information_type=InformationType.USERDATA)
607 repository.setTarget(target=owner, user=owner)
608 self.assertEqual(
609 owner, get_policies_for_artifact(repository)[0].person)
610
489 def test_public_to_proprietary_only_project(self):611 def test_public_to_proprietary_only_project(self):
490 # A repository cannot be moved to a target where the sharing policy612 # A repository cannot be moved to a target where the sharing policy
491 # does not allow it.613 # does not allow it.
@@ -525,6 +647,19 @@
525 person = self.factory.makePerson()647 person = self.factory.makePerson()
526 self.assertIsNone(self.repository_set.getByPath(person, "nonexistent"))648 self.assertIsNone(self.repository_set.getByPath(person, "nonexistent"))
527649
650 def test_getByPath_inaccessible(self):
651 # If the given user cannot view the matched repository, then
652 # getByPath returns None.
653 owner = self.factory.makePerson()
654 repository = self.factory.makeGitRepository(
655 owner=owner, information_type=InformationType.USERDATA)
656 with person_logged_in(owner):
657 path = repository.shortened_path
658 self.assertEqual(
659 repository, self.repository_set.getByPath(owner, path))
660 self.assertIsNone(
661 self.repository_set.getByPath(self.factory.makePerson(), path))
662
528 def test_setDefaultRepository_refuses_person(self):663 def test_setDefaultRepository_refuses_person(self):
529 # setDefaultRepository refuses if the target is a person.664 # setDefaultRepository refuses if the target is a person.
530 person = self.factory.makePerson()665 person = self.factory.makePerson()
531666
=== modified file 'lib/lp/registry/services/sharingservice.py'
--- lib/lp/registry/services/sharingservice.py 2015-02-16 13:01:34 +0000
+++ lib/lp/registry/services/sharingservice.py 2015-02-27 11:27:39 +0000
@@ -9,6 +9,7 @@
9 ]9 ]
1010
11from itertools import product11from itertools import product
12from operator import attrgetter
1213
13from lazr.restful.interfaces import IWebBrowserOriginatingRequest14from lazr.restful.interfaces import IWebBrowserOriginatingRequest
14from lazr.restful.utils import get_current_web_service_request15from lazr.restful.utils import get_current_web_service_request
@@ -36,6 +37,7 @@
36from lp.bugs.interfaces.bugtask import IBugTaskSet37from lp.bugs.interfaces.bugtask import IBugTaskSet
37from lp.bugs.interfaces.bugtasksearch import BugTaskSearchParams38from lp.bugs.interfaces.bugtasksearch import BugTaskSearchParams
38from lp.code.interfaces.branchcollection import IAllBranches39from lp.code.interfaces.branchcollection import IAllBranches
40from lp.code.interfaces.gitcollection import IAllGitRepositories
39from lp.registry.enums import (41from lp.registry.enums import (
40 BranchSharingPolicy,42 BranchSharingPolicy,
41 BugSharingPolicy,43 BugSharingPolicy,
@@ -227,7 +229,11 @@
227 branches = list(wanted_branches.getBranches())229 branches = list(wanted_branches.getBranches())
228 # Load the Git repositories.230 # Load the Git repositories.
229 gitrepositories = []231 gitrepositories = []
230 # XXX cjwatson 2015-02-16: Fill in once IGitCollection is in place.232 if gitrepository_ids:
233 all_gitrepositories = getUtility(IAllGitRepositories)
234 wanted_gitrepositories = all_gitrepositories.visibleByUser(
235 user).withIds(*gitrepository_ids)
236 gitrepositories = list(wanted_gitrepositories.getRepositories())
231 specifications = []237 specifications = []
232 if specification_ids:238 if specification_ids:
233 specifications = load(Specification, specification_ids)239 specifications = load(Specification, specification_ids)
@@ -319,48 +325,52 @@
319 gitrepositories=None, specifications=None,325 gitrepositories=None, specifications=None,
320 ignore_permissions=False):326 ignore_permissions=False):
321 """See `ISharingService`."""327 """See `ISharingService`."""
322 bugs_by_id = {}328 bug_ids = []
323 branches_by_id = {}329 branch_ids = []
324 gitrepositories_by_id = {}330 gitrepository_ids = []
325 for bug in bugs or []:331 for bug in bugs or []:
326 if (not ignore_permissions332 if (not ignore_permissions
327 and not check_permission('launchpad.View', bug)):333 and not check_permission('launchpad.View', bug)):
328 raise Unauthorized334 raise Unauthorized
329 bugs_by_id[bug.id] = bug335 bug_ids.append(bug.id)
330 for branch in branches or []:336 for branch in branches or []:
331 if (not ignore_permissions337 if (not ignore_permissions
332 and not check_permission('launchpad.View', branch)):338 and not check_permission('launchpad.View', branch)):
333 raise Unauthorized339 raise Unauthorized
334 branches_by_id[branch.id] = branch340 branch_ids.append(branch.id)
335 for gitrepository in gitrepositories or []:341 for gitrepository in gitrepositories or []:
336 if (not ignore_permissions342 if (not ignore_permissions
337 and not check_permission('launchpad.View', gitrepository)):343 and not check_permission('launchpad.View', gitrepository)):
338 raise Unauthorized344 raise Unauthorized
339 gitrepositories_by_id[gitrepository.id] = gitrepository345 gitrepository_ids.append(gitrepository.id)
340 for spec in specifications or []:346 for spec in specifications or []:
341 if (not ignore_permissions347 if (not ignore_permissions
342 and not check_permission('launchpad.View', spec)):348 and not check_permission('launchpad.View', spec)):
343 raise Unauthorized349 raise Unauthorized
344350
345 # Load the bugs.351 # Load the bugs.
346 visible_bug_ids = []352 visible_bugs = []
347 if bugs_by_id:353 if bug_ids:
348 param = BugTaskSearchParams(354 param = BugTaskSearchParams(user=person, bug=any(*bug_ids))
349 user=person, bug=any(*bugs_by_id.keys()))
350 visible_bug_ids = set(getUtility(IBugTaskSet).searchBugIds(param))355 visible_bug_ids = set(getUtility(IBugTaskSet).searchBugIds(param))
351 visible_bugs = [bugs_by_id[bug_id] for bug_id in visible_bug_ids]356 visible_bugs = [bug for bug in bugs if bug.id in visible_bug_ids]
352357
353 # Load the branches.358 # Load the branches.
354 visible_branches = []359 visible_branches = []
355 if branches_by_id:360 if branch_ids:
356 all_branches = getUtility(IAllBranches)361 all_branches = getUtility(IAllBranches)
357 wanted_branches = all_branches.visibleByUser(person).withIds(362 wanted_branches = all_branches.visibleByUser(person).withIds(
358 *branches_by_id.keys())363 *branch_ids)
359 visible_branches = list(wanted_branches.getBranches())364 visible_branches = list(wanted_branches.getBranches())
360365
361 # Load the Git repositories.366 # Load the Git repositories.
362 visible_gitrepositories = []367 visible_gitrepositories = []
363 # XXX cjwatson 2015-02-16: Fill in once IGitCollection is in place.368 if gitrepository_ids:
369 all_gitrepositories = getUtility(IAllGitRepositories)
370 wanted_gitrepositories = all_gitrepositories.visibleByUser(
371 person).withIds(*gitrepository_ids)
372 visible_gitrepositories = list(
373 wanted_gitrepositories.getRepositories())
364374
365 # Load the specifications.375 # Load the specifications.
366 visible_specs = []376 visible_specs = []
@@ -378,40 +388,37 @@
378 def getInvisibleArtifacts(self, person, bugs=None, branches=None,388 def getInvisibleArtifacts(self, person, bugs=None, branches=None,
379 gitrepositories=None):389 gitrepositories=None):
380 """See `ISharingService`."""390 """See `ISharingService`."""
381 bugs_by_id = {}391 bug_ids = list(map(attrgetter("id"), bugs or []))
382 branches_by_id = {}392 branch_ids = list(map(attrgetter("id"), branches or []))
383 gitrepositories_by_id = {}393 gitrepository_ids = list(map(attrgetter("id"), gitrepositories or []))
384 for bug in bugs or []:
385 bugs_by_id[bug.id] = bug
386 for branch in branches or []:
387 branches_by_id[branch.id] = branch
388 for gitrepository in gitrepositories or []:
389 gitrepositories_by_id[gitrepository.id] = gitrepository
390394
391 # Load the bugs.395 # Load the bugs.
392 visible_bug_ids = set()396 invisible_bugs = []
393 if bugs_by_id:397 if bug_ids:
394 param = BugTaskSearchParams(398 param = BugTaskSearchParams(user=person, bug=any(*bug_ids))
395 user=person, bug=any(*bugs_by_id.keys()))
396 visible_bug_ids = set(getUtility(IBugTaskSet).searchBugIds(param))399 visible_bug_ids = set(getUtility(IBugTaskSet).searchBugIds(param))
397 invisible_bug_ids = set(bugs_by_id.keys()).difference(visible_bug_ids)400 invisible_bugs = [
398 invisible_bugs = [bugs_by_id[bug_id] for bug_id in invisible_bug_ids]401 bug for bug in bugs if bug.id not in visible_bug_ids]
399402
400 # Load the branches.403 # Load the branches.
401 invisible_branches = []404 invisible_branches = []
402 if branches_by_id:405 if branch_ids:
403 all_branches = getUtility(IAllBranches)406 all_branches = getUtility(IAllBranches)
404 visible_branch_ids = all_branches.visibleByUser(person).withIds(407 visible_branch_ids = all_branches.visibleByUser(person).withIds(
405 *branches_by_id.keys()).getBranchIds()408 *branch_ids).getBranchIds()
406 invisible_branch_ids = (
407 set(branches_by_id.keys()).difference(visible_branch_ids))
408 invisible_branches = [409 invisible_branches = [
409 branches_by_id[branch_id]410 branch for branch in branches
410 for branch_id in invisible_branch_ids]411 if branch.id not in visible_branch_ids]
411412
412 # Load the Git repositories.413 # Load the Git repositories.
413 invisible_gitrepositories = []414 invisible_gitrepositories = []
414 # XXX cjwatson 2015-02-16: Fill in once IGitCollection is in place.415 if gitrepository_ids:
416 all_gitrepositories = getUtility(IAllGitRepositories)
417 visible_gitrepository_ids = all_gitrepositories.visibleByUser(
418 person).withIds(*gitrepository_ids).getRepositoryIds()
419 invisible_gitrepositories = [
420 gitrepository for gitrepository in gitrepositories
421 if gitrepository.id not in visible_gitrepository_ids]
415422
416 return invisible_bugs, invisible_branches, invisible_gitrepositories423 return invisible_bugs, invisible_branches, invisible_gitrepositories
417424
418425
=== modified file 'lib/lp/registry/services/tests/test_sharingservice.py'
--- lib/lp/registry/services/tests/test_sharingservice.py 2015-02-16 13:01:34 +0000
+++ lib/lp/registry/services/tests/test_sharingservice.py 2015-02-27 11:27:39 +0000
@@ -23,6 +23,7 @@
23 CodeReviewNotificationLevel,23 CodeReviewNotificationLevel,
24 )24 )
25from lp.code.interfaces.branch import IBranch25from lp.code.interfaces.branch import IBranch
26from lp.code.interfaces.gitrepository import IGitRepository
26from lp.registry.enums import (27from lp.registry.enums import (
27 BranchSharingPolicy,28 BranchSharingPolicy,
28 BugSharingPolicy,29 BugSharingPolicy,
@@ -928,12 +929,14 @@
928 [InformationType.USERDATA])929 [InformationType.USERDATA])
929930
930 def _assert_revokeAccessGrants(self, pillar, bugs, branches,931 def _assert_revokeAccessGrants(self, pillar, bugs, branches,
931 specifications):932 gitrepositories, specifications):
932 artifacts = []933 artifacts = []
933 if bugs:934 if bugs:
934 artifacts.extend(bugs)935 artifacts.extend(bugs)
935 if branches:936 if branches:
936 artifacts.extend(branches)937 artifacts.extend(branches)
938 if gitrepositories:
939 artifacts.extend(gitrepositories)
937 if specifications:940 if specifications:
938 artifacts.extend(specifications)941 artifacts.extend(specifications)
939 policy = self.factory.makeAccessPolicy(pillar=pillar,942 policy = self.factory.makeAccessPolicy(pillar=pillar,
@@ -961,6 +964,8 @@
961 branch.subscribe(person,964 branch.subscribe(person,
962 BranchSubscriptionNotificationLevel.NOEMAIL, None,965 BranchSubscriptionNotificationLevel.NOEMAIL, None,
963 CodeReviewNotificationLevel.NOEMAIL, pillar.owner)966 CodeReviewNotificationLevel.NOEMAIL, pillar.owner)
967 # XXX cjwatson 2015-02-05: subscribe to Git repositories when
968 # implemented
964 for spec in specifications or []:969 for spec in specifications or []:
965 spec.subscribe(person)970 spec.subscribe(person)
966971
@@ -973,7 +978,7 @@
973978
974 self.service.revokeAccessGrants(979 self.service.revokeAccessGrants(
975 pillar, grantee, pillar.owner, bugs=bugs, branches=branches,980 pillar, grantee, pillar.owner, bugs=bugs, branches=branches,
976 specifications=specifications)981 gitrepositories=gitrepositories, specifications=specifications)
977 with block_on_job(self):982 with block_on_job(self):
978 transaction.commit()983 transaction.commit()
979984
@@ -987,18 +992,22 @@
987 self.assertNotIn(grantee, bug.getDirectSubscribers())992 self.assertNotIn(grantee, bug.getDirectSubscribers())
988 for branch in branches or []:993 for branch in branches or []:
989 self.assertNotIn(grantee, branch.subscribers)994 self.assertNotIn(grantee, branch.subscribers)
995 # XXX cjwatson 2015-02-05: check revocation of subscription to Git
996 # repositories when implemented
990 for spec in specifications or []:997 for spec in specifications or []:
991 self.assertNotIn(grantee, spec.subscribers)998 self.assertNotIn(grantee, spec.subscribers)
992999
993 # Someone else still has access to the bugs and branches.1000 # Someone else still has access to the artifacts.
994 grants = accessartifact_grant_source.findByArtifact(1001 grants = accessartifact_grant_source.findByArtifact(
995 access_artifacts, [someone])1002 access_artifacts, [someone])
996 self.assertEqual(1, grants.count())1003 self.assertEqual(1, grants.count())
997 # Someone else still has subscriptions to the bugs and branches.1004 # Someone else still has subscriptions to the artifacts.
998 for bug in bugs or []:1005 for bug in bugs or []:
999 self.assertIn(someone, bug.getDirectSubscribers())1006 self.assertIn(someone, bug.getDirectSubscribers())
1000 for branch in branches or []:1007 for branch in branches or []:
1001 self.assertIn(someone, branch.subscribers)1008 self.assertIn(someone, branch.subscribers)
1009 # XXX cjwatson 2015-02-05: check subscription to Git repositories
1010 # when implemented
1002 for spec in specifications or []:1011 for spec in specifications or []:
1003 self.assertIn(someone, spec.subscribers)1012 self.assertIn(someone, spec.subscribers)
10041013
@@ -1010,7 +1019,7 @@
1010 bug = self.factory.makeBug(1019 bug = self.factory.makeBug(
1011 target=distro, owner=owner,1020 target=distro, owner=owner,
1012 information_type=InformationType.USERDATA)1021 information_type=InformationType.USERDATA)
1013 self._assert_revokeAccessGrants(distro, [bug], None, None)1022 self._assert_revokeAccessGrants(distro, [bug], None, None, None)
10141023
1015 def test_revokeAccessGrantsBranches(self):1024 def test_revokeAccessGrantsBranches(self):
1016 owner = self.factory.makePerson()1025 owner = self.factory.makePerson()
@@ -1019,7 +1028,17 @@
1019 branch = self.factory.makeBranch(1028 branch = self.factory.makeBranch(
1020 product=product, owner=owner,1029 product=product, owner=owner,
1021 information_type=InformationType.USERDATA)1030 information_type=InformationType.USERDATA)
1022 self._assert_revokeAccessGrants(product, None, [branch], None)1031 self._assert_revokeAccessGrants(product, None, [branch], None, None)
1032
1033 def test_revokeAccessGrantsGitRepositories(self):
1034 owner = self.factory.makePerson()
1035 product = self.factory.makeProduct(owner=owner)
1036 login_person(owner)
1037 gitrepository = self.factory.makeGitRepository(
1038 target=product, owner=owner,
1039 information_type=InformationType.USERDATA)
1040 self._assert_revokeAccessGrants(
1041 product, None, None, [gitrepository], None)
10231042
1024 def test_revokeAccessGrantsSpecifications(self):1043 def test_revokeAccessGrantsSpecifications(self):
1025 owner = self.factory.makePerson()1044 owner = self.factory.makePerson()
@@ -1030,15 +1049,18 @@
1030 specification = self.factory.makeSpecification(1049 specification = self.factory.makeSpecification(
1031 product=product, owner=owner,1050 product=product, owner=owner,
1032 information_type=InformationType.EMBARGOED)1051 information_type=InformationType.EMBARGOED)
1033 self._assert_revokeAccessGrants(product, None, None, [specification])1052 self._assert_revokeAccessGrants(
1053 product, None, None, None, [specification])
10341054
1035 def _assert_revokeTeamAccessGrants(self, pillar, bugs, branches,1055 def _assert_revokeTeamAccessGrants(self, pillar, bugs, branches,
1036 specifications):1056 gitrepositories, specifications):
1037 artifacts = []1057 artifacts = []
1038 if bugs:1058 if bugs:
1039 artifacts.extend(bugs)1059 artifacts.extend(bugs)
1040 if branches:1060 if branches:
1041 artifacts.extend(branches)1061 artifacts.extend(branches)
1062 if gitrepositories:
1063 artifacts.extend(gitrepositories)
1042 if specifications:1064 if specifications:
1043 artifacts.extend(specifications)1065 artifacts.extend(specifications)
1044 policy = self.factory.makeAccessPolicy(pillar=pillar,1066 policy = self.factory.makeAccessPolicy(pillar=pillar,
@@ -1065,6 +1087,8 @@
1065 branch.subscribe(1087 branch.subscribe(
1066 person, BranchSubscriptionNotificationLevel.NOEMAIL,1088 person, BranchSubscriptionNotificationLevel.NOEMAIL,
1067 None, CodeReviewNotificationLevel.NOEMAIL, pillar.owner)1089 None, CodeReviewNotificationLevel.NOEMAIL, pillar.owner)
1090 # XXX cjwatson 2015-02-05: subscribe to Git repositories when
1091 # implemented
1068 # Subscribing somebody to a specification does not yet imply1092 # Subscribing somebody to a specification does not yet imply
1069 # granting access to this person.1093 # granting access to this person.
1070 if specifications:1094 if specifications:
@@ -1075,12 +1099,16 @@
10751099
1076 # Check that grantees have expected access grants and subscriptions.1100 # Check that grantees have expected access grants and subscriptions.
1077 for person in [team_grantee, person_grantee]:1101 for person in [team_grantee, person_grantee]:
1078 visible_bugs, visible_branches, _, visible_specs = (1102 (visible_bugs, visible_branches, visible_gitrepositories,
1103 visible_specs) = (
1079 self.service.getVisibleArtifacts(1104 self.service.getVisibleArtifacts(
1080 person, bugs=bugs, branches=branches,1105 person, bugs=bugs, branches=branches,
1106 gitrepositories=gitrepositories,
1081 specifications=specifications))1107 specifications=specifications))
1082 self.assertContentEqual(bugs or [], visible_bugs)1108 self.assertContentEqual(bugs or [], visible_bugs)
1083 self.assertContentEqual(branches or [], visible_branches)1109 self.assertContentEqual(branches or [], visible_branches)
1110 # XXX cjwatson 2015-02-05: check Git repositories when
1111 # subscription is implemented
1084 self.assertContentEqual(specifications or [], visible_specs)1112 self.assertContentEqual(specifications or [], visible_specs)
1085 for person in [team_grantee, person_grantee]:1113 for person in [team_grantee, person_grantee]:
1086 for bug in bugs or []:1114 for bug in bugs or []:
@@ -1088,7 +1116,7 @@
10881116
1089 self.service.revokeAccessGrants(1117 self.service.revokeAccessGrants(
1090 pillar, team_grantee, pillar.owner, bugs=bugs, branches=branches,1118 pillar, team_grantee, pillar.owner, bugs=bugs, branches=branches,
1091 specifications=specifications)1119 gitrepositories=gitrepositories, specifications=specifications)
1092 with block_on_job(self):1120 with block_on_job(self):
1093 transaction.commit()1121 transaction.commit()
10941122
@@ -1103,11 +1131,14 @@
1103 for person in [team_grantee, person_grantee]:1131 for person in [team_grantee, person_grantee]:
1104 for bug in bugs or []:1132 for bug in bugs or []:
1105 self.assertNotIn(person, bug.getDirectSubscribers())1133 self.assertNotIn(person, bug.getDirectSubscribers())
1106 visible_bugs, visible_branches, _, visible_specs = (1134 (visible_bugs, visible_branches, visible_gitrepositories,
1135 visible_specs) = (
1107 self.service.getVisibleArtifacts(1136 self.service.getVisibleArtifacts(
1108 person, bugs=bugs, branches=branches))1137 person, bugs=bugs, branches=branches,
1138 gitrepositories=gitrepositories))
1109 self.assertContentEqual([], visible_bugs)1139 self.assertContentEqual([], visible_bugs)
1110 self.assertContentEqual([], visible_branches)1140 self.assertContentEqual([], visible_branches)
1141 self.assertContentEqual([], visible_gitrepositories)
1111 self.assertContentEqual([], visible_specs)1142 self.assertContentEqual([], visible_specs)
11121143
1113 def test_revokeTeamAccessGrantsBugs(self):1144 def test_revokeTeamAccessGrantsBugs(self):
@@ -1118,7 +1149,7 @@
1118 bug = self.factory.makeBug(1149 bug = self.factory.makeBug(
1119 target=distro, owner=owner,1150 target=distro, owner=owner,
1120 information_type=InformationType.USERDATA)1151 information_type=InformationType.USERDATA)
1121 self._assert_revokeTeamAccessGrants(distro, [bug], None, None)1152 self._assert_revokeTeamAccessGrants(distro, [bug], None, None, None)
11221153
1123 def test_revokeTeamAccessGrantsBranches(self):1154 def test_revokeTeamAccessGrantsBranches(self):
1124 # Users with launchpad.Edit can delete all access for a grantee.1155 # Users with launchpad.Edit can delete all access for a grantee.
@@ -1127,7 +1158,20 @@
1127 login_person(owner)1158 login_person(owner)
1128 branch = self.factory.makeBranch(1159 branch = self.factory.makeBranch(
1129 owner=owner, information_type=InformationType.USERDATA)1160 owner=owner, information_type=InformationType.USERDATA)
1130 self._assert_revokeTeamAccessGrants(product, None, [branch], None)1161 self._assert_revokeTeamAccessGrants(
1162 product, None, [branch], None, None)
1163
1164 # XXX cjwatson 2015-02-05: Enable this once GitRepositorySubscription is
1165 # implemented.
1166 def disabled_test_revokeTeamAccessGrantsGitRepositories(self):
1167 # Users with launchpad.Edit can delete all access for a grantee.
1168 owner = self.factory.makePerson()
1169 product = self.factory.makeProduct(owner=owner)
1170 login_person(owner)
1171 gitrepository = self.factory.makeGitRepository(
1172 owner=owner, information_type=InformationType.USERDATA)
1173 self._assert_revokeTeamAccessGrants(
1174 product, None, None, [gitrepository], None)
11311175
1132 def test_revokeTeamAccessGrantsSpecifications(self):1176 def test_revokeTeamAccessGrantsSpecifications(self):
1133 # Users with launchpad.Edit can delete all access for a grantee.1177 # Users with launchpad.Edit can delete all access for a grantee.
@@ -1140,7 +1184,7 @@
1140 product=product, owner=owner,1184 product=product, owner=owner,
1141 information_type=InformationType.EMBARGOED)1185 information_type=InformationType.EMBARGOED)
1142 self._assert_revokeTeamAccessGrants(1186 self._assert_revokeTeamAccessGrants(
1143 product, None, None, [specification])1187 product, None, None, None, [specification])
11441188
1145 def _assert_revokeAccessGrantsUnauthorized(self):1189 def _assert_revokeAccessGrantsUnauthorized(self):
1146 # revokeAccessGrants raises an Unauthorized exception if the user1190 # revokeAccessGrants raises an Unauthorized exception if the user
@@ -1163,9 +1207,9 @@
1163 login_person(self.factory.makePerson())1207 login_person(self.factory.makePerson())
1164 self._assert_revokeAccessGrantsUnauthorized()1208 self._assert_revokeAccessGrantsUnauthorized()
11651209
1166 def test_revokeAccessGrants_without_bugs_or_branches(self):1210 def test_revokeAccessGrants_without_artifacts(self):
1167 # The revokeAccessGrants method raises a ValueError if called without1211 # The revokeAccessGrants method raises a ValueError if called without
1168 # specifying either bugs or branches.1212 # specifying any artifacts.
1169 owner = self.factory.makePerson()1213 owner = self.factory.makePerson()
1170 product = self.factory.makeProduct(owner=owner)1214 product = self.factory.makeProduct(owner=owner)
1171 grantee = self.factory.makePerson()1215 grantee = self.factory.makePerson()
@@ -1174,24 +1218,27 @@
1174 ValueError, self.service.revokeAccessGrants,1218 ValueError, self.service.revokeAccessGrants,
1175 product, grantee, product.owner)1219 product, grantee, product.owner)
11761220
1177 def _assert_ensureAccessGrants(self, user, bugs, branches, specifications,1221 def _assert_ensureAccessGrants(self, user, bugs, branches, gitrepositories,
1178 grantee=None):1222 specifications, grantee=None):
1179 # Creating access grants works as expected.1223 # Creating access grants works as expected.
1180 if not grantee:1224 if not grantee:
1181 grantee = self.factory.makePerson()1225 grantee = self.factory.makePerson()
1182 self.service.ensureAccessGrants(1226 self.service.ensureAccessGrants(
1183 [grantee], user, bugs=bugs, branches=branches,1227 [grantee], user, bugs=bugs, branches=branches,
1184 specifications=specifications)1228 gitrepositories=gitrepositories, specifications=specifications)
11851229
1186 # Check that grantee has expected access grants.1230 # Check that grantee has expected access grants.
1187 shared_bugs = []1231 shared_bugs = []
1188 shared_branches = []1232 shared_branches = []
1233 shared_gitrepositories = []
1189 shared_specifications = []1234 shared_specifications = []
1190 all_pillars = []1235 all_pillars = []
1191 for bug in bugs or []:1236 for bug in bugs or []:
1192 all_pillars.extend(bug.affected_pillars)1237 all_pillars.extend(bug.affected_pillars)
1193 for branch in branches or []:1238 for branch in branches or []:
1194 all_pillars.append(branch.target.context)1239 all_pillars.append(branch.target.context)
1240 for gitrepository in gitrepositories or []:
1241 all_pillars.append(gitrepository.target)
1195 for specification in specifications or []:1242 for specification in specifications or []:
1196 all_pillars.append(specification.target)1243 all_pillars.append(specification.target)
1197 policies = getUtility(IAccessPolicySource).findByPillar(all_pillars)1244 policies = getUtility(IAccessPolicySource).findByPillar(all_pillars)
@@ -1203,10 +1250,13 @@
1203 shared_bugs.append(a.concrete_artifact)1250 shared_bugs.append(a.concrete_artifact)
1204 elif IBranch.providedBy(a.concrete_artifact):1251 elif IBranch.providedBy(a.concrete_artifact):
1205 shared_branches.append(a.concrete_artifact)1252 shared_branches.append(a.concrete_artifact)
1253 elif IGitRepository.providedBy(a.concrete_artifact):
1254 shared_gitrepositories.append(a.concrete_artifact)
1206 elif ISpecification.providedBy(a.concrete_artifact):1255 elif ISpecification.providedBy(a.concrete_artifact):
1207 shared_specifications.append(a.concrete_artifact)1256 shared_specifications.append(a.concrete_artifact)
1208 self.assertContentEqual(bugs or [], shared_bugs)1257 self.assertContentEqual(bugs or [], shared_bugs)
1209 self.assertContentEqual(branches or [], shared_branches)1258 self.assertContentEqual(branches or [], shared_branches)
1259 self.assertContentEqual(gitrepositories or [], shared_gitrepositories)
1210 self.assertContentEqual(specifications or [], shared_specifications)1260 self.assertContentEqual(specifications or [], shared_specifications)
12111261
1212 def test_ensureAccessGrantsBugs(self):1262 def test_ensureAccessGrantsBugs(self):
@@ -1217,7 +1267,7 @@
1217 bug = self.factory.makeBug(1267 bug = self.factory.makeBug(
1218 target=distro, owner=owner,1268 target=distro, owner=owner,
1219 information_type=InformationType.USERDATA)1269 information_type=InformationType.USERDATA)
1220 self._assert_ensureAccessGrants(owner, [bug], None, None)1270 self._assert_ensureAccessGrants(owner, [bug], None, None, None)
12211271
1222 def test_ensureAccessGrantsBranches(self):1272 def test_ensureAccessGrantsBranches(self):
1223 # Access grants can be created for branches.1273 # Access grants can be created for branches.
@@ -1227,7 +1277,18 @@
1227 branch = self.factory.makeBranch(1277 branch = self.factory.makeBranch(
1228 product=product, owner=owner,1278 product=product, owner=owner,
1229 information_type=InformationType.USERDATA)1279 information_type=InformationType.USERDATA)
1230 self._assert_ensureAccessGrants(owner, None, [branch], None)1280 self._assert_ensureAccessGrants(owner, None, [branch], None, None)
1281
1282 def test_ensureAccessGrantsGitRepositories(self):
1283 # Access grants can be created for Git repositories.
1284 owner = self.factory.makePerson()
1285 product = self.factory.makeProduct(owner=owner)
1286 login_person(owner)
1287 gitrepository = self.factory.makeGitRepository(
1288 target=product, owner=owner,
1289 information_type=InformationType.USERDATA)
1290 self._assert_ensureAccessGrants(
1291 owner, None, None, [gitrepository], None)
12311292
1232 def test_ensureAccessGrantsSpecifications(self):1293 def test_ensureAccessGrantsSpecifications(self):
1233 # Access grants can be created for branches.1294 # Access grants can be created for branches.
@@ -1244,7 +1305,8 @@
1244 with person_logged_in(owner):1305 with person_logged_in(owner):
1245 specification.transitionToInformationType(1306 specification.transitionToInformationType(
1246 InformationType.PROPRIETARY, owner)1307 InformationType.PROPRIETARY, owner)
1247 self._assert_ensureAccessGrants(owner, None, None, [specification])1308 self._assert_ensureAccessGrants(
1309 owner, None, None, None, [specification])
12481310
1249 def test_ensureAccessGrantsExisting(self):1311 def test_ensureAccessGrantsExisting(self):
1250 # Any existing access grants are retained and new ones created.1312 # Any existing access grants are retained and new ones created.
@@ -1263,7 +1325,7 @@
1263 # Test with a new bug as well as the one for which access is already1325 # Test with a new bug as well as the one for which access is already
1264 # granted.1326 # granted.
1265 self._assert_ensureAccessGrants(1327 self._assert_ensureAccessGrants(
1266 owner, [bug, bug2], None, None, grantee)1328 owner, [bug, bug2], None, None, None, grantee)
12671329
1268 def _assert_ensureAccessGrantsUnauthorized(self, user):1330 def _assert_ensureAccessGrantsUnauthorized(self, user):
1269 # ensureAccessGrants raises an Unauthorized exception if the user1331 # ensureAccessGrants raises an Unauthorized exception if the user
@@ -1331,7 +1393,8 @@
1331 self._assert_updatePillarSharingPoliciesUnauthorized(anyone)1393 self._assert_updatePillarSharingPoliciesUnauthorized(anyone)
13321394
1333 def create_shared_artifacts(self, product, grantee, user):1395 def create_shared_artifacts(self, product, grantee, user):
1334 # Create some shared bugs and branches.1396 # Create some shared bugs, branches, Git repositories, and
1397 # specifications.
1335 bugs = []1398 bugs = []
1336 bug_tasks = []1399 bug_tasks = []
1337 for x in range(0, 10):1400 for x in range(0, 10):
@@ -1346,6 +1409,12 @@
1346 product=product, owner=product.owner,1409 product=product, owner=product.owner,
1347 information_type=InformationType.USERDATA)1410 information_type=InformationType.USERDATA)
1348 branches.append(branch)1411 branches.append(branch)
1412 gitrepositories = []
1413 for x in range(0, 10):
1414 gitrepository = self.factory.makeGitRepository(
1415 target=product, owner=product.owner,
1416 information_type=InformationType.USERDATA)
1417 gitrepositories.append(gitrepository)
1349 specs = []1418 specs = []
1350 for x in range(0, 10):1419 for x in range(0, 10):
1351 spec = self.factory.makeSpecification(1420 spec = self.factory.makeSpecification(
@@ -1371,9 +1440,11 @@
1371 grant_access(bug, i == 9)1440 grant_access(bug, i == 9)
1372 for i, branch in enumerate(branches):1441 for i, branch in enumerate(branches):
1373 grant_access(branch, i == 9)1442 grant_access(branch, i == 9)
1443 for i, gitrepository in enumerate(gitrepositories):
1444 grant_access(gitrepository, i == 9)
1374 getUtility(IService, 'sharing').ensureAccessGrants(1445 getUtility(IService, 'sharing').ensureAccessGrants(
1375 [grantee], product.owner, specifications=specs[:9])1446 [grantee], product.owner, specifications=specs[:9])
1376 return bug_tasks, branches, specs1447 return bug_tasks, branches, gitrepositories, specs
13771448
1378 def test_getSharedArtifacts(self):1449 def test_getSharedArtifacts(self):
1379 # Test the getSharedArtifacts method.1450 # Test the getSharedArtifacts method.
@@ -1384,14 +1455,16 @@
1384 login_person(owner)1455 login_person(owner)
1385 grantee = self.factory.makePerson()1456 grantee = self.factory.makePerson()
1386 user = self.factory.makePerson()1457 user = self.factory.makePerson()
1387 bug_tasks, branches, specs = self.create_shared_artifacts(1458 bug_tasks, branches, gitrepositories, specs = (
1388 product, grantee, user)1459 self.create_shared_artifacts(product, grantee, user))
13891460
1390 # Check the results.1461 # Check the results.
1391 shared_bugtasks, shared_branches, _, shared_specs = (1462 (shared_bugtasks, shared_branches, shared_gitrepositories,
1463 shared_specs) = (
1392 self.service.getSharedArtifacts(product, grantee, user))1464 self.service.getSharedArtifacts(product, grantee, user))
1393 self.assertContentEqual(bug_tasks[:9], shared_bugtasks)1465 self.assertContentEqual(bug_tasks[:9], shared_bugtasks)
1394 self.assertContentEqual(branches[:9], shared_branches)1466 self.assertContentEqual(branches[:9], shared_branches)
1467 self.assertContentEqual(gitrepositories[:9], shared_gitrepositories)
1395 self.assertContentEqual(specs[:9], shared_specs)1468 self.assertContentEqual(specs[:9], shared_specs)
13961469
1397 def _assert_getSharedProjects(self, product, who=None):1470 def _assert_getSharedProjects(self, product, who=None):
@@ -1531,7 +1604,7 @@
1531 login_person(owner)1604 login_person(owner)
1532 grantee = self.factory.makePerson()1605 grantee = self.factory.makePerson()
1533 user = self.factory.makePerson()1606 user = self.factory.makePerson()
1534 bug_tasks, ignored, ignored = self.create_shared_artifacts(1607 bug_tasks, _, _, _ = self.create_shared_artifacts(
1535 product, grantee, user)1608 product, grantee, user)
15361609
1537 # Check the results.1610 # Check the results.
@@ -1547,7 +1620,7 @@
1547 login_person(owner)1620 login_person(owner)
1548 grantee = self.factory.makePerson()1621 grantee = self.factory.makePerson()
1549 user = self.factory.makePerson()1622 user = self.factory.makePerson()
1550 ignored, branches, ignored = self.create_shared_artifacts(1623 _, branches, _, _ = self.create_shared_artifacts(
1551 product, grantee, user)1624 product, grantee, user)
15521625
1553 # Check the results.1626 # Check the results.
@@ -1555,6 +1628,23 @@
1555 product, grantee, user)1628 product, grantee, user)
1556 self.assertContentEqual(branches[:9], shared_branches)1629 self.assertContentEqual(branches[:9], shared_branches)
15571630
1631 def test_getSharedGitRepositories(self):
1632 # Test the getSharedGitRepositories method.
1633 owner = self.factory.makePerson()
1634 product = self.factory.makeProduct(
1635 owner=owner, specification_sharing_policy=(
1636 SpecificationSharingPolicy.PUBLIC_OR_PROPRIETARY))
1637 login_person(owner)
1638 grantee = self.factory.makePerson()
1639 user = self.factory.makePerson()
1640 _, _, gitrepositories, _ = self.create_shared_artifacts(
1641 product, grantee, user)
1642
1643 # Check the results.
1644 shared_gitrepositories = self.service.getSharedGitRepositories(
1645 product, grantee, user)
1646 self.assertContentEqual(gitrepositories[:9], shared_gitrepositories)
1647
1558 def test_getSharedSpecifications(self):1648 def test_getSharedSpecifications(self):
1559 # Test the getSharedSpecifications method.1649 # Test the getSharedSpecifications method.
1560 owner = self.factory.makePerson()1650 owner = self.factory.makePerson()
@@ -1564,7 +1654,7 @@
1564 login_person(owner)1654 login_person(owner)
1565 grantee = self.factory.makePerson()1655 grantee = self.factory.makePerson()
1566 user = self.factory.makePerson()1656 user = self.factory.makePerson()
1567 ignored, ignored, specifications = self.create_shared_artifacts(1657 _, _, _, specifications = self.create_shared_artifacts(
1568 product, grantee, user)1658 product, grantee, user)
15691659
1570 # Check the results.1660 # Check the results.
@@ -1592,6 +1682,16 @@
1592 login_person(owner)1682 login_person(owner)
1593 self._assert_getPeopleWithoutAccess(product, branch)1683 self._assert_getPeopleWithoutAccess(product, branch)
15941684
1685 def test_getPeopleWithAccessGitRepositories(self):
1686 # Test the getPeopleWithoutAccess method with Git repositories.
1687 owner = self.factory.makePerson()
1688 product = self.factory.makeProduct(owner=owner)
1689 gitrepository = self.factory.makeGitRepository(
1690 target=product, owner=owner,
1691 information_type=InformationType.USERDATA)
1692 login_person(owner)
1693 self._assert_getPeopleWithoutAccess(product, gitrepository)
1694
1595 def _assert_getPeopleWithoutAccess(self, product, artifact):1695 def _assert_getPeopleWithoutAccess(self, product, artifact):
1596 access_artifact = self.factory.makeAccessArtifact(concrete=artifact)1696 access_artifact = self.factory.makeAccessArtifact(concrete=artifact)
1597 # Make some people to check. people[:5] will not have access.1697 # Make some people to check. people[:5] will not have access.
@@ -1647,6 +1747,12 @@
1647 product=product, owner=owner,1747 product=product, owner=owner,
1648 information_type=InformationType.USERDATA)1748 information_type=InformationType.USERDATA)
1649 branches.append(branch)1749 branches.append(branch)
1750 gitrepositories = []
1751 for x in range(0, 10):
1752 gitrepository = self.factory.makeGitRepository(
1753 target=product, owner=owner,
1754 information_type=InformationType.USERDATA)
1755 gitrepositories.append(gitrepository)
16501756
1651 specifications = []1757 specifications = []
1652 for x in range(0, 10):1758 for x in range(0, 10):
@@ -1662,46 +1768,58 @@
1662 artifact=access_artifact, grantee=grantee, grantor=owner)1768 artifact=access_artifact, grantee=grantee, grantor=owner)
1663 return access_artifact1769 return access_artifact
16641770
1665 # Grant access to some of the bugs and branches.1771 # Grant access to some of the artifacts.
1666 for bug in bugs[:5]:1772 for bug in bugs[:5]:
1667 grant_access(bug)1773 grant_access(bug)
1668 for branch in branches[:5]:1774 for branch in branches[:5]:
1669 grant_access(branch)1775 grant_access(branch)
1776 for gitrepository in gitrepositories[:5]:
1777 grant_access(gitrepository)
1670 for spec in specifications[:5]:1778 for spec in specifications[:5]:
1671 grant_access(spec)1779 grant_access(spec)
1672 return grantee, owner, branches, bugs, specifications1780 return grantee, owner, bugs, branches, gitrepositories, specifications
16731781
1674 def test_getVisibleArtifacts(self):1782 def test_getVisibleArtifacts(self):
1675 # Test the getVisibleArtifacts method.1783 # Test the getVisibleArtifacts method.
1676 grantee, ignore, branches, bugs, specs = self._make_Artifacts()1784 grantee, ignore, bugs, branches, gitrepositories, specs = (
1785 self._make_Artifacts())
1677 # Check the results.1786 # Check the results.
1678 shared_bugs, shared_branches, _, shared_specs = (1787 shared_bugs, shared_branches, shared_gitrepositories, shared_specs = (
1679 self.service.getVisibleArtifacts(1788 self.service.getVisibleArtifacts(
1680 grantee, bugs=bugs, branches=branches, specifications=specs))1789 grantee, bugs=bugs, branches=branches,
1790 gitrepositories=gitrepositories, specifications=specs))
1681 self.assertContentEqual(bugs[:5], shared_bugs)1791 self.assertContentEqual(bugs[:5], shared_bugs)
1682 self.assertContentEqual(branches[:5], shared_branches)1792 self.assertContentEqual(branches[:5], shared_branches)
1793 self.assertContentEqual(gitrepositories[:5], shared_gitrepositories)
1683 self.assertContentEqual(specs[:5], shared_specs)1794 self.assertContentEqual(specs[:5], shared_specs)
16841795
1685 def test_getVisibleArtifacts_grant_on_pillar(self):1796 def test_getVisibleArtifacts_grant_on_pillar(self):
1686 # getVisibleArtifacts() returns private specifications if1797 # getVisibleArtifacts() returns private specifications if
1687 # user has a policy grant for the pillar of the specification.1798 # user has a policy grant for the pillar of the specification.
1688 ignore, owner, branches, bugs, specs = self._make_Artifacts()1799 _, owner, bugs, branches, gitrepositories, specs = (
1689 shared_bugs, shared_branches, _, shared_specs = (1800 self._make_Artifacts())
1801 shared_bugs, shared_branches, shared_gitrepositories, shared_specs = (
1690 self.service.getVisibleArtifacts(1802 self.service.getVisibleArtifacts(
1691 owner, bugs=bugs, branches=branches, specifications=specs))1803 owner, bugs=bugs, branches=branches,
1804 gitrepositories=gitrepositories, specifications=specs))
1692 self.assertContentEqual(bugs, shared_bugs)1805 self.assertContentEqual(bugs, shared_bugs)
1693 self.assertContentEqual(branches, shared_branches)1806 self.assertContentEqual(branches, shared_branches)
1807 self.assertContentEqual(gitrepositories, shared_gitrepositories)
1694 self.assertContentEqual(specs, shared_specs)1808 self.assertContentEqual(specs, shared_specs)
16951809
1696 def test_getInvisibleArtifacts(self):1810 def test_getInvisibleArtifacts(self):
1697 # Test the getInvisibleArtifacts method.1811 # Test the getInvisibleArtifacts method.
1698 grantee, ignore, branches, bugs, specs = self._make_Artifacts()1812 grantee, ignore, bugs, branches, gitrepositories, specs = (
1813 self._make_Artifacts())
1699 # Check the results.1814 # Check the results.
1700 not_shared_bugs, not_shared_branches, _ = (1815 not_shared_bugs, not_shared_branches, not_shared_gitrepositories = (
1701 self.service.getInvisibleArtifacts(1816 self.service.getInvisibleArtifacts(
1702 grantee, bugs=bugs, branches=branches))1817 grantee, bugs=bugs, branches=branches,
1818 gitrepositories=gitrepositories))
1703 self.assertContentEqual(bugs[5:], not_shared_bugs)1819 self.assertContentEqual(bugs[5:], not_shared_bugs)
1704 self.assertContentEqual(branches[5:], not_shared_branches)1820 self.assertContentEqual(branches[5:], not_shared_branches)
1821 self.assertContentEqual(
1822 gitrepositories[5:], not_shared_gitrepositories)
17051823
1706 def _assert_getVisibleArtifacts_bug_change(self, change_callback):1824 def _assert_getVisibleArtifacts_bug_change(self, change_callback):
1707 # Test the getVisibleArtifacts method excludes bugs after a change of1825 # Test the getVisibleArtifacts method excludes bugs after a change of
@@ -1723,7 +1841,7 @@
1723 information_type=InformationType.USERDATA)1841 information_type=InformationType.USERDATA)
1724 bugs.append(bug)1842 bugs.append(bug)
17251843
1726 shared_bugs, shared_branches, _, shared_specs = (1844 shared_bugs, shared_branches, shared_gitrepositories, shared_specs = (
1727 self.service.getVisibleArtifacts(grantee, bugs=bugs))1845 self.service.getVisibleArtifacts(grantee, bugs=bugs))
1728 self.assertContentEqual(bugs, shared_bugs)1846 self.assertContentEqual(bugs, shared_bugs)
17291847
@@ -1731,7 +1849,7 @@
1731 for x in range(0, 5):1849 for x in range(0, 5):
1732 change_callback(bugs[x], owner)1850 change_callback(bugs[x], owner)
1733 # Check the results.1851 # Check the results.
1734 shared_bugs, shared_branches, _, shared_specs = (1852 shared_bugs, shared_branches, shared_gitrepositories, shared_specs = (
1735 self.service.getVisibleArtifacts(grantee, bugs=bugs))1853 self.service.getVisibleArtifacts(grantee, bugs=bugs))
1736 self.assertContentEqual(bugs[5:], shared_bugs)1854 self.assertContentEqual(bugs[5:], shared_bugs)
17371855
17381856
=== modified file 'lib/lp/registry/tests/test_accesspolicy.py'
--- lib/lp/registry/tests/test_accesspolicy.py 2013-06-20 05:50:00 +0000
+++ lib/lp/registry/tests/test_accesspolicy.py 2015-02-27 11:27:39 +0000
@@ -1,4 +1,4 @@
1# Copyright 2011-2012 Canonical Ltd. This software is licensed under the1# Copyright 2011-2015 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4__metaclass__ = type4__metaclass__ = type
@@ -280,6 +280,13 @@
280 return self.factory.makeBranch()280 return self.factory.makeBranch()
281281
282282
283class TestAccessArtifactGitRepository(BaseAccessArtifactTests,
284 TestCaseWithFactory):
285
286 def getConcreteArtifact(self):
287 return self.factory.makeGitRepository()
288
289
283class TestAccessArtifactBug(BaseAccessArtifactTests,290class TestAccessArtifactBug(BaseAccessArtifactTests,
284 TestCaseWithFactory):291 TestCaseWithFactory):
285292
286293
=== modified file 'lib/lp/registry/tests/test_sharingjob.py'
--- lib/lp/registry/tests/test_sharingjob.py 2014-06-19 06:38:53 +0000
+++ lib/lp/registry/tests/test_sharingjob.py 2015-02-27 11:27:39 +0000
@@ -1,4 +1,4 @@
1# Copyright 2012 Canonical Ltd. This software is licensed under the1# Copyright 2012-2015 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Tests for SharingJobs."""4"""Tests for SharingJobs."""
@@ -118,6 +118,16 @@
118 'for branch_ids=[%d], requestor=%s>'118 'for branch_ids=[%d], requestor=%s>'
119 % (branch.id, requestor.name), repr(job))119 % (branch.id, requestor.name), repr(job))
120120
121 def test_repr_gitrepositories(self):
122 requestor = self.factory.makePerson()
123 gitrepository = self.factory.makeGitRepository()
124 job = getUtility(IRemoveArtifactSubscriptionsJobSource).create(
125 requestor, artifacts=[gitrepository])
126 self.assertEqual(
127 '<REMOVE_ARTIFACT_SUBSCRIPTIONS job reconciling subscriptions '
128 'for gitrepository_ids=[%d], requestor=%s>'
129 % (gitrepository.id, requestor.name), repr(job))
130
121 def test_repr_specifications(self):131 def test_repr_specifications(self):
122 requestor = self.factory.makePerson()132 requestor = self.factory.makePerson()
123 specification = self.factory.makeSpecification()133 specification = self.factory.makeSpecification()
@@ -303,6 +313,9 @@
303 branch = self.factory.makeBranch(313 branch = self.factory.makeBranch(
304 owner=owner, product=product,314 owner=owner, product=product,
305 information_type=InformationType.USERDATA)315 information_type=InformationType.USERDATA)
316 gitrepository = self.factory.makeGitRepository(
317 owner=owner, target=product,
318 information_type=InformationType.USERDATA)
306 specification = self.factory.makeSpecification(319 specification = self.factory.makeSpecification(
307 owner=owner, product=product,320 owner=owner, product=product,
308 information_type=InformationType.PROPRIETARY)321 information_type=InformationType.PROPRIETARY)
@@ -317,6 +330,8 @@
317 branch.subscribe(artifact_indirect_grantee,330 branch.subscribe(artifact_indirect_grantee,
318 BranchSubscriptionNotificationLevel.NOEMAIL, None,331 BranchSubscriptionNotificationLevel.NOEMAIL, None,
319 CodeReviewNotificationLevel.NOEMAIL, owner)332 CodeReviewNotificationLevel.NOEMAIL, owner)
333 # XXX cjwatson 2015-02-05: Fill this in once we have
334 # GitRepositorySubscription.
320 # Subscribing somebody to a specification does not automatically335 # Subscribing somebody to a specification does not automatically
321 # create an artifact grant.336 # create an artifact grant.
322 spec_artifact = self.factory.makeAccessArtifact(specification)337 spec_artifact = self.factory.makeAccessArtifact(specification)
@@ -325,10 +340,10 @@
325 with person_logged_in(product.owner):340 with person_logged_in(product.owner):
326 specification.subscribe(artifact_indirect_grantee, owner)341 specification.subscribe(artifact_indirect_grantee, owner)
327342
328 # pick one of the concrete artifacts (bug, branch or spec)343 # pick one of the concrete artifacts (bug, branch, Git repository,
329 # and subscribe the teams and persons.344 # or spec) and subscribe the teams and persons.
330 concrete_artifact, get_pillars, get_subscribers = configure_test(345 concrete_artifact, get_pillars, get_subscribers = configure_test(
331 bug, branch, specification, policy_team_grantee,346 bug, branch, gitrepository, specification, policy_team_grantee,
332 policy_indirect_grantee, artifact_team_grantee, owner)347 policy_indirect_grantee, artifact_team_grantee, owner)
333348
334 # Subscribing policy_team_grantee has created an artifact grant so we349 # Subscribing policy_team_grantee has created an artifact grant so we
@@ -361,6 +376,8 @@
361 self.assertIn(artifact_team_grantee, subscribers)376 self.assertIn(artifact_team_grantee, subscribers)
362 self.assertIn(artifact_indirect_grantee, bug.getDirectSubscribers())377 self.assertIn(artifact_indirect_grantee, bug.getDirectSubscribers())
363 self.assertIn(artifact_indirect_grantee, branch.subscribers)378 self.assertIn(artifact_indirect_grantee, branch.subscribers)
379 # XXX cjwatson 2015-02-05: Fill this in once we have
380 # GitRepositorySubscription.
364 self.assertIn(artifact_indirect_grantee,381 self.assertIn(artifact_indirect_grantee,
365 removeSecurityProxy(specification).subscribers)382 removeSecurityProxy(specification).subscribers)
366383
@@ -373,9 +390,9 @@
373 return removeSecurityProxy(390 return removeSecurityProxy(
374 concrete_artifact).getDirectSubscribers()391 concrete_artifact).getDirectSubscribers()
375392
376 def configure_test(bug, branch, specification, policy_team_grantee,393 def configure_test(bug, branch, gitrepository, specification,
377 policy_indirect_grantee, artifact_team_grantee,394 policy_team_grantee, policy_indirect_grantee,
378 owner):395 artifact_team_grantee, owner):
379 concrete_artifact = bug396 concrete_artifact = bug
380 bug.subscribe(policy_team_grantee, owner)397 bug.subscribe(policy_team_grantee, owner)
381 bug.subscribe(policy_indirect_grantee, owner)398 bug.subscribe(policy_indirect_grantee, owner)
@@ -393,9 +410,9 @@
393 def get_subscribers(concrete_artifact):410 def get_subscribers(concrete_artifact):
394 return concrete_artifact.subscribers411 return concrete_artifact.subscribers
395412
396 def configure_test(bug, branch, specification, policy_team_grantee,413 def configure_test(bug, branch, gitrepository, specification,
397 policy_indirect_grantee, artifact_team_grantee,414 policy_team_grantee, policy_indirect_grantee,
398 owner):415 artifact_team_grantee, owner):
399 concrete_artifact = branch416 concrete_artifact = branch
400 branch.subscribe(417 branch.subscribe(
401 policy_team_grantee,418 policy_team_grantee,
@@ -414,6 +431,27 @@
414 self._assert_artifact_change_unsubscribes(431 self._assert_artifact_change_unsubscribes(
415 change_callback, configure_test)432 change_callback, configure_test)
416433
434 def _assert_gitrepository_change_unsubscribes(self, change_callback):
435
436 def get_pillars(concrete_artifact):
437 return [concrete_artifact.product]
438
439 def get_subscribers(concrete_artifact):
440 return concrete_artifact.subscribers
441
442 def configure_test(bug, branch, gitrepository, specification,
443 policy_team_grantee, policy_indirect_grantee,
444 artifact_team_grantee, owner):
445 concrete_artifact = gitrepository
446 # XXX cjwatson 2015-02-05: Fill this in once we have
447 # GitRepositorySubscription.
448 return concrete_artifact, get_pillars, get_subscribers
449
450 # XXX cjwatson 2015-02-05: Uncomment once we have
451 # GitRepositorySubscription.
452 #self._assert_artifact_change_unsubscribes(
453 # change_callback, configure_test)
454
417 def _assert_specification_change_unsubscribes(self, change_callback):455 def _assert_specification_change_unsubscribes(self, change_callback):
418456
419 def get_pillars(concrete_artifact):457 def get_pillars(concrete_artifact):
@@ -422,9 +460,9 @@
422 def get_subscribers(concrete_artifact):460 def get_subscribers(concrete_artifact):
423 return concrete_artifact.subscribers461 return concrete_artifact.subscribers
424462
425 def configure_test(bug, branch, specification, policy_team_grantee,463 def configure_test(bug, branch, gitrepository, specification,
426 policy_indirect_grantee, artifact_team_grantee,464 policy_team_grantee, policy_indirect_grantee,
427 owner):465 artifact_team_grantee, owner):
428 naked_spec = removeSecurityProxy(specification)466 naked_spec = removeSecurityProxy(specification)
429 naked_spec.subscribe(policy_team_grantee, owner)467 naked_spec.subscribe(policy_team_grantee, owner)
430 naked_spec.subscribe(policy_indirect_grantee, owner)468 naked_spec.subscribe(policy_indirect_grantee, owner)
@@ -444,6 +482,13 @@
444482
445 self._assert_branch_change_unsubscribes(change_information_type)483 self._assert_branch_change_unsubscribes(change_information_type)
446484
485 def test_change_information_type_gitrepository(self):
486 def change_information_type(gitrepository):
487 removeSecurityProxy(gitrepository).information_type = (
488 InformationType.PRIVATESECURITY)
489
490 self._assert_gitrepository_change_unsubscribes(change_information_type)
491
447 def test_change_information_type_specification(self):492 def test_change_information_type_specification(self):
448 def change_information_type(specification):493 def change_information_type(specification):
449 removeSecurityProxy(specification).information_type = (494 removeSecurityProxy(specification).information_type = (
450495
=== modified file 'lib/lp/security.py'
--- lib/lp/security.py 2015-02-23 03:22:37 +0000
+++ lib/lp/security.py 2015-02-27 11:27:39 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009-2014 Canonical Ltd. This software is licensed under the1# Copyright 2009-2015 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Security policies for using content objects."""4"""Security policies for using content objects."""
@@ -83,6 +83,7 @@
83 )83 )
84from lp.code.interfaces.codereviewvote import ICodeReviewVoteReference84from lp.code.interfaces.codereviewvote import ICodeReviewVoteReference
85from lp.code.interfaces.diff import IPreviewDiff85from lp.code.interfaces.diff import IPreviewDiff
86from lp.code.interfaces.gitcollection import IGitCollection
86from lp.code.interfaces.gitrepository import (87from lp.code.interfaces.gitrepository import (
87 IGitRepository,88 IGitRepository,
88 user_has_special_git_repository_access,89 user_has_special_git_repository_access,
@@ -1020,6 +1021,12 @@
1020 if not mp.is_empty():1021 if not mp.is_empty():
1021 return True1022 return True
10221023
1024 # Grant visibility to people who can see Git repositories owned
1025 # by the private team.
1026 team_repositories = IGitCollection(self.obj)
1027 if not team_repositories.visibleByUser(user.person).is_empty():
1028 return True
1029
1023 # Grant visibility to users in a team that has the private team as1030 # Grant visibility to users in a team that has the private team as
1024 # a member, so that they can see the team properly in member1031 # a member, so that they can see the team properly in member
1025 # listings.1032 # listings.