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
1=== modified file 'lib/canonical/launchpad/security.py'
2--- lib/canonical/launchpad/security.py 2011-08-28 07:29:11 +0000
3+++ lib/canonical/launchpad/security.py 2011-08-31 20:28:59 +0000
4@@ -1649,7 +1649,8 @@
5
6 def _checkBuildPermission(self, user=None):
7 """Check access to `IPackageBuild` for this job."""
8- permission = ViewBinaryPackageBuild(self.obj.build)
9+ permission = getAdapter(
10+ self.obj.build, IAuthorization, self.permission)
11 if user is None:
12 return permission.checkUnauthenticated()
13 else:
14
15=== modified file 'lib/lp/buildmaster/model/packagebuild.py'
16--- lib/lp/buildmaster/model/packagebuild.py 2011-05-16 23:41:48 +0000
17+++ lib/lp/buildmaster/model/packagebuild.py 2011-08-31 20:28:59 +0000
18@@ -280,10 +280,12 @@
19 specific_job.job.suspend()
20
21 duration_estimate = self.estimateDuration()
22+ job = specific_job.job
23+ processor = specific_job.processor
24 queue_entry = BuildQueue(
25 estimated_duration=duration_estimate,
26 job_type=self.build_farm_job_type,
27- job=specific_job.job, processor=specific_job.processor,
28+ job=job, processor=processor,
29 virtualized=specific_job.virtualized)
30 Store.of(self).add(queue_entry)
31 return queue_entry
32
33=== modified file 'lib/lp/code/browser/tests/test_sourcepackagerecipe.py'
34--- lib/lp/code/browser/tests/test_sourcepackagerecipe.py 2011-08-31 12:23:13 +0000
35+++ lib/lp/code/browser/tests/test_sourcepackagerecipe.py 2011-08-31 20:28:59 +0000
36@@ -480,9 +480,9 @@
37
38 with recipe_parser_newest_version(145.115):
39 recipe = dedent(u'''\
40- # bzr-builder format 145.115 deb-version {debupstream}-0~{revno}
41- %s
42- ''') % branch.bzr_identity
43+ # bzr-builder format 145.115 deb-version {debupstream}-0~{revno}
44+ %s
45+ ''') % branch.bzr_identity
46 browser = self.createRecipe(recipe, branch)
47 self.assertEqual(
48 get_feedback_messages(browser.contents)[1],
49@@ -592,7 +592,7 @@
50
51 def test_new_sourcepackage_branch_recipe_with_related_branches(self):
52 # The related branches should be rendered correctly on the page.
53- reference_branch= self.factory.makePackageBranch()
54+ reference_branch = self.factory.makePackageBranch()
55 (branch, ignore, related_package_branch_info) = (
56 self.factory.makeRelatedBranches(reference_branch))
57 browser = self.getUserBrowser(
58@@ -1039,7 +1039,7 @@
59 def test_edit_sourcepackage_branch_recipe_with_related_branches(self):
60 # The related branches should be rendered correctly on the page.
61 with person_logged_in(self.chef):
62- reference_branch= self.factory.makePackageBranch()
63+ reference_branch = self.factory.makePackageBranch()
64 recipe = self.factory.makeSourcePackageRecipe(
65 owner=self.chef, branches=[reference_branch])
66 (branch, ignore, related_package_branch_info) = (
67@@ -1152,10 +1152,11 @@
68 sourcepackagerelease=source_package_release, archive=self.ppa,
69 distroseries=self.squirrel)
70 builder = self.factory.makeBuilder()
71- binary_build = removeSecurityProxy(self.factory.makeBinaryPackageBuild(
72- source_package_release=source_package_release,
73- distroarchseries=self.squirrel.nominatedarchindep,
74- processor=builder.processor))
75+ binary_build = removeSecurityProxy(
76+ self.factory.makeBinaryPackageBuild(
77+ source_package_release=source_package_release,
78+ distroarchseries=self.squirrel.nominatedarchindep,
79+ processor=builder.processor))
80 binary_build.queueBuild()
81 binary_build.status = BuildStatus.FULLYBUILT
82 binary_build.date_started = datetime(2010, 04, 16, tzinfo=UTC)
83@@ -1529,7 +1530,7 @@
84
85
86 class TestSourcePackageRecipeBuildView(BrowserTestCase):
87- """Test behaviour of SourcePackageReciptBuildView."""
88+ """Test behaviour of SourcePackageRecipeBuildView."""
89
90 layer = LaunchpadFunctionalLayer
91
92
93=== modified file 'lib/lp/code/model/tests/test_sourcepackagerecipe.py'
94--- lib/lp/code/model/tests/test_sourcepackagerecipe.py 2011-08-29 07:55:48 +0000
95+++ lib/lp/code/model/tests/test_sourcepackagerecipe.py 2011-08-31 20:28:59 +0000
96@@ -101,7 +101,7 @@
97 """
98 registrant = self.factory.makePerson()
99 return dict(
100- registrant=registrant,
101+ registrant = registrant,
102 owner = self.factory.makeTeam(owner=registrant),
103 distroseries = [self.factory.makeDistroSeries()],
104 name = self.factory.getUniqueString(u'recipe-name'),
105@@ -304,13 +304,13 @@
106 store = Store.of(build)
107 store.flush()
108 build_job = store.find(SourcePackageRecipeBuildJob,
109- SourcePackageRecipeBuildJob.build_id==build.id).one()
110+ SourcePackageRecipeBuildJob.build_id == build.id).one()
111 self.assertProvides(build_job, ISourcePackageRecipeBuildJob)
112 self.assertTrue(build_job.virtualized)
113 job = build_job.job
114 self.assertProvides(job, IJob)
115 self.assertEquals(job.status, JobStatus.WAITING)
116- build_queue = store.find(BuildQueue, BuildQueue.job==job.id).one()
117+ build_queue = store.find(BuildQueue, BuildQueue.job == job.id).one()
118 self.assertProvides(build_queue, IBuildQueue)
119 self.assertTrue(build_queue.virtualized)
120
121@@ -418,6 +418,39 @@
122 recipe.requestBuild(archive, recipe.owner, series,
123 PackagePublishingPocket.RELEASE)
124
125+ def test_requestBuildPrivatePPAWithArchivePermission(self):
126+ """User is not in PPA owner team but has ArchivePermission.
127+
128+ The case where the user is not in the PPA owner team but is allowed to
129+ upload to the PPA via an explicit ArchivePermission takes a different
130+ security path than if he were part of the team.
131+ """
132+
133+ # Create a team private PPA.
134+ team_owner = self.factory.makePerson()
135+ team = self.factory.makeTeam(owner=team_owner)
136+ team_p3a = self.factory.makeArchive(
137+ owner=team, displayname='Private PPA', name='p3a',
138+ private=True)
139+
140+ # Create a recipe with the team P3A as the build destination.
141+ recipe = self.factory.makeSourcePackageRecipe()
142+
143+ # Add upload component rights for the non-team person.
144+ with person_logged_in(team_owner):
145+ team_p3a.newComponentUploader(
146+ person=recipe.owner, component_name="main")
147+ (distroseries,) = list(recipe.distroseries)
148+
149+ # Try to request a build. It should work.
150+ with person_logged_in(recipe.owner):
151+ build = recipe.requestBuild(
152+ team_p3a, recipe.owner, distroseries,
153+ PackagePublishingPocket.RELEASE)
154+ self.assertEqual(build.archive, team_p3a)
155+ self.assertEqual(build.distroseries, distroseries)
156+ self.assertEqual(build.requester, recipe.owner)
157+
158 def test_sourcepackagerecipe_description(self):
159 """Ensure that the SourcePackageRecipe has a proper description."""
160 description = u'The whoozits and whatzits.'
161@@ -464,7 +497,7 @@
162 with person_logged_in(owner):
163 recipe = self.factory.makeSourcePackageRecipe(branches=[branch])
164 self.assertTrue(check_permission('launchpad.View', recipe))
165- removeSecurityProxy(branch).explicitly_private=True
166+ removeSecurityProxy(branch).explicitly_private = True
167 with person_logged_in(self.factory.makePerson()):
168 self.assertFalse(check_permission('launchpad.View', recipe))
169 self.assertFalse(check_permission('launchpad.View', recipe))
170@@ -606,12 +639,12 @@
171 """SourcePackageRecipe.getPendingBuildInfo() is as expected."""
172 person = self.factory.makePerson()
173 archives = [self.factory.makeArchive(owner=person) for x in range(4)]
174- distroseries= self.factory.makeSourcePackageRecipeDistroseries()
175+ distroseries = self.factory.makeSourcePackageRecipeDistroseries()
176 recipe = self.factory.makeSourcePackageRecipe()
177
178 build_info = []
179 for archive in archives:
180- build = recipe.requestBuild(archive, person, distroseries)
181+ recipe.requestBuild(archive, person, distroseries)
182 build_info.insert(0, {
183 "distroseries": distroseries.displayname,
184 "archive": '%s/%s' %
185@@ -1036,7 +1069,7 @@
186 """SourcePackageRecipe.[pending_|completed_]builds is as expected."""
187 person = self.factory.makePerson()
188 archives = [self.factory.makeArchive(owner=person) for x in range(4)]
189- distroseries= self.factory.makeSourcePackageRecipeDistroseries()
190+ distroseries = self.factory.makeSourcePackageRecipeDistroseries()
191
192 recipe, user, launchpad = self.makeRecipe(person)
193 distroseries = ws_object(launchpad, distroseries)
194@@ -1056,7 +1089,7 @@
195 """SourcePackageRecipe.getPendingBuildInfo() is as expected."""
196 person = self.factory.makePerson()
197 archives = [self.factory.makeArchive(owner=person) for x in range(4)]
198- distroseries= self.factory.makeSourcePackageRecipeDistroseries()
199+ distroseries = self.factory.makeSourcePackageRecipeDistroseries()
200
201 recipe, user, launchpad = self.makeRecipe(person)
202 ws_distroseries = ws_object(launchpad, distroseries)
203@@ -1064,7 +1097,7 @@
204 build_info = []
205 for archive in archives:
206 ws_archive = ws_object(launchpad, archive)
207- build = recipe.requestBuild(
208+ recipe.requestBuild(
209 archive=ws_archive, distroseries=ws_distroseries,
210 pocket=PackagePublishingPocket.RELEASE.title)
211 build_info.insert(0, {