Merge lp:~bac/launchpad/bug-828914-test into lp:launchpad

Proposed by Brad Crittenden
Status: Merged
Approved by: Brad Crittenden
Approved revision: no longer in the source branch.
Merged at revision: 13842
Proposed branch: lp:~bac/launchpad/bug-828914-test
Merge into: lp:launchpad
Diff against target: 211 lines (+58/-21)
4 files modified
lib/canonical/launchpad/security.py (+2/-1)
lib/lp/buildmaster/model/packagebuild.py (+3/-1)
lib/lp/code/browser/tests/test_sourcepackagerecipe.py (+11/-10)
lib/lp/code/model/tests/test_sourcepackagerecipe.py (+42/-9)
To merge this branch: bzr merge lp:~bac/launchpad/bug-828914-test
Reviewer Review Type Date Requested Status
Benji York (community) code Approve
Review via email: mp+73585@code.launchpad.net

Commit message

[r=benji][bug=828914] Use the proper security adapter when building a SourcePackageRecipe into a private PPA and the user has ArchivePermission to upload but isn't a member of the team that owns the PPA.

Description of the change

= Summary =

When a user is not a member of the team owning a private PPA, but does
have upload rights via an ArchivePermission, tries to request a recipe
build there is an error in the security adapter which causes an OOPS.
The problem is the wrong security adapter is being used.

== Proposed fix ==

Use getAdapter to select the proper adapter.

== Pre-implementation notes ==

Talks with Julian, Aaron, and William.

== Tests ==

bin/test -vvt test_sourcepackagerecipe

== Demo and Q/A ==

* Create a private PPA for a team you are a member.
* Using the API, create a new component uploader archive permission
  for your user.

  team = lp.people['team']
  p3a = team.ppas[0]
  p3a.newComponentUploader(person=lp.me, component_name="main")

* Leave the team.
* Now create a recipe for one of your branches and use the private
  team PPA as the build destination.
* Select 'Request build'.

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/canonical/launchpad/security.py
  lib/lp/buildmaster/model/packagebuild.py
  lib/lp/code/model/tests/test_sourcepackagerecipe.py
  lib/lp/code/browser/tests/test_sourcepackagerecipe.py

Will fix these lint issues later.

./lib/lp/code/model/tests/test_sourcepackagerecipe.py
     647: local variable 'build' is assigned to but never used
    1100: local variable 'build' is assigned to but never used
     105: E251 no spaces around keyword / parameter equals
     307: E225 missing whitespace around operator
     313: E225 missing whitespace around operator
     500: E225 missing whitespace around operator
     642: E225 missing whitespace around operator
    1072: E225 missing whitespace around operator
    1092: E225 missing whitespace around operator
./lib/lp/code/browser/tests/test_sourcepackagerecipe.py
     483: Line exceeds 78 characters.
    1155: Line exceeds 78 characters.
     483: E501 line too long (80 characters)
     595: E225 missing whitespace around operator
    1042: E225 missing whitespace around operator

To post a comment you must log in.
Revision history for this message
Benji York (benji) wrote :

Looks good.

review: Approve (code)
Revision history for this message
Francis J. Lacoste (flacoste) wrote :

If you look through the various security.py files, you'll see that we have a bunch of other places where the security policy implementation is being instantiated directly instead of using getAdapter(). These are all kind of places such problem may creep up in the future.

Care to do a sweep as a separate branch?

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/canonical/launchpad/security.py'
--- lib/canonical/launchpad/security.py 2011-08-28 07:29:11 +0000
+++ lib/canonical/launchpad/security.py 2011-08-31 20:28:59 +0000
@@ -1649,7 +1649,8 @@
16491649
1650 def _checkBuildPermission(self, user=None):1650 def _checkBuildPermission(self, user=None):
1651 """Check access to `IPackageBuild` for this job."""1651 """Check access to `IPackageBuild` for this job."""
1652 permission = ViewBinaryPackageBuild(self.obj.build)1652 permission = getAdapter(
1653 self.obj.build, IAuthorization, self.permission)
1653 if user is None:1654 if user is None:
1654 return permission.checkUnauthenticated()1655 return permission.checkUnauthenticated()
1655 else:1656 else:
16561657
=== modified file 'lib/lp/buildmaster/model/packagebuild.py'
--- lib/lp/buildmaster/model/packagebuild.py 2011-05-16 23:41:48 +0000
+++ lib/lp/buildmaster/model/packagebuild.py 2011-08-31 20:28:59 +0000
@@ -280,10 +280,12 @@
280 specific_job.job.suspend()280 specific_job.job.suspend()
281281
282 duration_estimate = self.estimateDuration()282 duration_estimate = self.estimateDuration()
283 job = specific_job.job
284 processor = specific_job.processor
283 queue_entry = BuildQueue(285 queue_entry = BuildQueue(
284 estimated_duration=duration_estimate,286 estimated_duration=duration_estimate,
285 job_type=self.build_farm_job_type,287 job_type=self.build_farm_job_type,
286 job=specific_job.job, processor=specific_job.processor,288 job=job, processor=processor,
287 virtualized=specific_job.virtualized)289 virtualized=specific_job.virtualized)
288 Store.of(self).add(queue_entry)290 Store.of(self).add(queue_entry)
289 return queue_entry291 return queue_entry
290292
=== modified file 'lib/lp/code/browser/tests/test_sourcepackagerecipe.py'
--- lib/lp/code/browser/tests/test_sourcepackagerecipe.py 2011-08-31 12:23:13 +0000
+++ lib/lp/code/browser/tests/test_sourcepackagerecipe.py 2011-08-31 20:28:59 +0000
@@ -480,9 +480,9 @@
480480
481 with recipe_parser_newest_version(145.115):481 with recipe_parser_newest_version(145.115):
482 recipe = dedent(u'''\482 recipe = dedent(u'''\
483 # bzr-builder format 145.115 deb-version {debupstream}-0~{revno}483 # bzr-builder format 145.115 deb-version {debupstream}-0~{revno}
484 %s484 %s
485 ''') % branch.bzr_identity485 ''') % branch.bzr_identity
486 browser = self.createRecipe(recipe, branch)486 browser = self.createRecipe(recipe, branch)
487 self.assertEqual(487 self.assertEqual(
488 get_feedback_messages(browser.contents)[1],488 get_feedback_messages(browser.contents)[1],
@@ -592,7 +592,7 @@
592592
593 def test_new_sourcepackage_branch_recipe_with_related_branches(self):593 def test_new_sourcepackage_branch_recipe_with_related_branches(self):
594 # The related branches should be rendered correctly on the page.594 # The related branches should be rendered correctly on the page.
595 reference_branch= self.factory.makePackageBranch()595 reference_branch = self.factory.makePackageBranch()
596 (branch, ignore, related_package_branch_info) = (596 (branch, ignore, related_package_branch_info) = (
597 self.factory.makeRelatedBranches(reference_branch))597 self.factory.makeRelatedBranches(reference_branch))
598 browser = self.getUserBrowser(598 browser = self.getUserBrowser(
@@ -1039,7 +1039,7 @@
1039 def test_edit_sourcepackage_branch_recipe_with_related_branches(self):1039 def test_edit_sourcepackage_branch_recipe_with_related_branches(self):
1040 # The related branches should be rendered correctly on the page.1040 # The related branches should be rendered correctly on the page.
1041 with person_logged_in(self.chef):1041 with person_logged_in(self.chef):
1042 reference_branch= self.factory.makePackageBranch()1042 reference_branch = self.factory.makePackageBranch()
1043 recipe = self.factory.makeSourcePackageRecipe(1043 recipe = self.factory.makeSourcePackageRecipe(
1044 owner=self.chef, branches=[reference_branch])1044 owner=self.chef, branches=[reference_branch])
1045 (branch, ignore, related_package_branch_info) = (1045 (branch, ignore, related_package_branch_info) = (
@@ -1152,10 +1152,11 @@
1152 sourcepackagerelease=source_package_release, archive=self.ppa,1152 sourcepackagerelease=source_package_release, archive=self.ppa,
1153 distroseries=self.squirrel)1153 distroseries=self.squirrel)
1154 builder = self.factory.makeBuilder()1154 builder = self.factory.makeBuilder()
1155 binary_build = removeSecurityProxy(self.factory.makeBinaryPackageBuild(1155 binary_build = removeSecurityProxy(
1156 source_package_release=source_package_release,1156 self.factory.makeBinaryPackageBuild(
1157 distroarchseries=self.squirrel.nominatedarchindep,1157 source_package_release=source_package_release,
1158 processor=builder.processor))1158 distroarchseries=self.squirrel.nominatedarchindep,
1159 processor=builder.processor))
1159 binary_build.queueBuild()1160 binary_build.queueBuild()
1160 binary_build.status = BuildStatus.FULLYBUILT1161 binary_build.status = BuildStatus.FULLYBUILT
1161 binary_build.date_started = datetime(2010, 04, 16, tzinfo=UTC)1162 binary_build.date_started = datetime(2010, 04, 16, tzinfo=UTC)
@@ -1529,7 +1530,7 @@
15291530
15301531
1531class TestSourcePackageRecipeBuildView(BrowserTestCase):1532class TestSourcePackageRecipeBuildView(BrowserTestCase):
1532 """Test behaviour of SourcePackageReciptBuildView."""1533 """Test behaviour of SourcePackageRecipeBuildView."""
15331534
1534 layer = LaunchpadFunctionalLayer1535 layer = LaunchpadFunctionalLayer
15351536
15361537
=== modified file 'lib/lp/code/model/tests/test_sourcepackagerecipe.py'
--- lib/lp/code/model/tests/test_sourcepackagerecipe.py 2011-08-29 07:55:48 +0000
+++ lib/lp/code/model/tests/test_sourcepackagerecipe.py 2011-08-31 20:28:59 +0000
@@ -101,7 +101,7 @@
101 """101 """
102 registrant = self.factory.makePerson()102 registrant = self.factory.makePerson()
103 return dict(103 return dict(
104 registrant=registrant,104 registrant = registrant,
105 owner = self.factory.makeTeam(owner=registrant),105 owner = self.factory.makeTeam(owner=registrant),
106 distroseries = [self.factory.makeDistroSeries()],106 distroseries = [self.factory.makeDistroSeries()],
107 name = self.factory.getUniqueString(u'recipe-name'),107 name = self.factory.getUniqueString(u'recipe-name'),
@@ -304,13 +304,13 @@
304 store = Store.of(build)304 store = Store.of(build)
305 store.flush()305 store.flush()
306 build_job = store.find(SourcePackageRecipeBuildJob,306 build_job = store.find(SourcePackageRecipeBuildJob,
307 SourcePackageRecipeBuildJob.build_id==build.id).one()307 SourcePackageRecipeBuildJob.build_id == build.id).one()
308 self.assertProvides(build_job, ISourcePackageRecipeBuildJob)308 self.assertProvides(build_job, ISourcePackageRecipeBuildJob)
309 self.assertTrue(build_job.virtualized)309 self.assertTrue(build_job.virtualized)
310 job = build_job.job310 job = build_job.job
311 self.assertProvides(job, IJob)311 self.assertProvides(job, IJob)
312 self.assertEquals(job.status, JobStatus.WAITING)312 self.assertEquals(job.status, JobStatus.WAITING)
313 build_queue = store.find(BuildQueue, BuildQueue.job==job.id).one()313 build_queue = store.find(BuildQueue, BuildQueue.job == job.id).one()
314 self.assertProvides(build_queue, IBuildQueue)314 self.assertProvides(build_queue, IBuildQueue)
315 self.assertTrue(build_queue.virtualized)315 self.assertTrue(build_queue.virtualized)
316316
@@ -418,6 +418,39 @@
418 recipe.requestBuild(archive, recipe.owner, series,418 recipe.requestBuild(archive, recipe.owner, series,
419 PackagePublishingPocket.RELEASE)419 PackagePublishingPocket.RELEASE)
420420
421 def test_requestBuildPrivatePPAWithArchivePermission(self):
422 """User is not in PPA owner team but has ArchivePermission.
423
424 The case where the user is not in the PPA owner team but is allowed to
425 upload to the PPA via an explicit ArchivePermission takes a different
426 security path than if he were part of the team.
427 """
428
429 # Create a team private PPA.
430 team_owner = self.factory.makePerson()
431 team = self.factory.makeTeam(owner=team_owner)
432 team_p3a = self.factory.makeArchive(
433 owner=team, displayname='Private PPA', name='p3a',
434 private=True)
435
436 # Create a recipe with the team P3A as the build destination.
437 recipe = self.factory.makeSourcePackageRecipe()
438
439 # Add upload component rights for the non-team person.
440 with person_logged_in(team_owner):
441 team_p3a.newComponentUploader(
442 person=recipe.owner, component_name="main")
443 (distroseries,) = list(recipe.distroseries)
444
445 # Try to request a build. It should work.
446 with person_logged_in(recipe.owner):
447 build = recipe.requestBuild(
448 team_p3a, recipe.owner, distroseries,
449 PackagePublishingPocket.RELEASE)
450 self.assertEqual(build.archive, team_p3a)
451 self.assertEqual(build.distroseries, distroseries)
452 self.assertEqual(build.requester, recipe.owner)
453
421 def test_sourcepackagerecipe_description(self):454 def test_sourcepackagerecipe_description(self):
422 """Ensure that the SourcePackageRecipe has a proper description."""455 """Ensure that the SourcePackageRecipe has a proper description."""
423 description = u'The whoozits and whatzits.'456 description = u'The whoozits and whatzits.'
@@ -464,7 +497,7 @@
464 with person_logged_in(owner):497 with person_logged_in(owner):
465 recipe = self.factory.makeSourcePackageRecipe(branches=[branch])498 recipe = self.factory.makeSourcePackageRecipe(branches=[branch])
466 self.assertTrue(check_permission('launchpad.View', recipe))499 self.assertTrue(check_permission('launchpad.View', recipe))
467 removeSecurityProxy(branch).explicitly_private=True500 removeSecurityProxy(branch).explicitly_private = True
468 with person_logged_in(self.factory.makePerson()):501 with person_logged_in(self.factory.makePerson()):
469 self.assertFalse(check_permission('launchpad.View', recipe))502 self.assertFalse(check_permission('launchpad.View', recipe))
470 self.assertFalse(check_permission('launchpad.View', recipe))503 self.assertFalse(check_permission('launchpad.View', recipe))
@@ -606,12 +639,12 @@
606 """SourcePackageRecipe.getPendingBuildInfo() is as expected."""639 """SourcePackageRecipe.getPendingBuildInfo() is as expected."""
607 person = self.factory.makePerson()640 person = self.factory.makePerson()
608 archives = [self.factory.makeArchive(owner=person) for x in range(4)]641 archives = [self.factory.makeArchive(owner=person) for x in range(4)]
609 distroseries= self.factory.makeSourcePackageRecipeDistroseries()642 distroseries = self.factory.makeSourcePackageRecipeDistroseries()
610 recipe = self.factory.makeSourcePackageRecipe()643 recipe = self.factory.makeSourcePackageRecipe()
611644
612 build_info = []645 build_info = []
613 for archive in archives:646 for archive in archives:
614 build = recipe.requestBuild(archive, person, distroseries)647 recipe.requestBuild(archive, person, distroseries)
615 build_info.insert(0, {648 build_info.insert(0, {
616 "distroseries": distroseries.displayname,649 "distroseries": distroseries.displayname,
617 "archive": '%s/%s' %650 "archive": '%s/%s' %
@@ -1036,7 +1069,7 @@
1036 """SourcePackageRecipe.[pending_|completed_]builds is as expected."""1069 """SourcePackageRecipe.[pending_|completed_]builds is as expected."""
1037 person = self.factory.makePerson()1070 person = self.factory.makePerson()
1038 archives = [self.factory.makeArchive(owner=person) for x in range(4)]1071 archives = [self.factory.makeArchive(owner=person) for x in range(4)]
1039 distroseries= self.factory.makeSourcePackageRecipeDistroseries()1072 distroseries = self.factory.makeSourcePackageRecipeDistroseries()
10401073
1041 recipe, user, launchpad = self.makeRecipe(person)1074 recipe, user, launchpad = self.makeRecipe(person)
1042 distroseries = ws_object(launchpad, distroseries)1075 distroseries = ws_object(launchpad, distroseries)
@@ -1056,7 +1089,7 @@
1056 """SourcePackageRecipe.getPendingBuildInfo() is as expected."""1089 """SourcePackageRecipe.getPendingBuildInfo() is as expected."""
1057 person = self.factory.makePerson()1090 person = self.factory.makePerson()
1058 archives = [self.factory.makeArchive(owner=person) for x in range(4)]1091 archives = [self.factory.makeArchive(owner=person) for x in range(4)]
1059 distroseries= self.factory.makeSourcePackageRecipeDistroseries()1092 distroseries = self.factory.makeSourcePackageRecipeDistroseries()
10601093
1061 recipe, user, launchpad = self.makeRecipe(person)1094 recipe, user, launchpad = self.makeRecipe(person)
1062 ws_distroseries = ws_object(launchpad, distroseries)1095 ws_distroseries = ws_object(launchpad, distroseries)
@@ -1064,7 +1097,7 @@
1064 build_info = []1097 build_info = []
1065 for archive in archives:1098 for archive in archives:
1066 ws_archive = ws_object(launchpad, archive)1099 ws_archive = ws_object(launchpad, archive)
1067 build = recipe.requestBuild(1100 recipe.requestBuild(
1068 archive=ws_archive, distroseries=ws_distroseries,1101 archive=ws_archive, distroseries=ws_distroseries,
1069 pocket=PackagePublishingPocket.RELEASE.title)1102 pocket=PackagePublishingPocket.RELEASE.title)
1070 build_info.insert(0, {1103 build_info.insert(0, {