Merge lp:~abentley/launchpad/recipe-security into lp:launchpad

Proposed by Aaron Bentley
Status: Merged
Approved by: Edwin Grubbs
Approved revision: no longer in the source branch.
Merged at revision: not available
Proposed branch: lp:~abentley/launchpad/recipe-security
Merge into: lp:launchpad
Prerequisite: lp:~abentley/launchpad/request-build
Diff against target: 432 lines (+116/-39)
11 files modified
configs/development/build-from-branch.zcml (+3/-3)
lib/canonical/launchpad/security.py (+26/-1)
lib/lp/code/browser/tests/test_sourcepackagerecipe.py (+3/-3)
lib/lp/code/interfaces/sourcepackagerecipe.py (+5/-4)
lib/lp/code/model/sourcepackagerecipe.py (+2/-4)
lib/lp/code/model/sourcepackagerecipedata.py (+5/-0)
lib/lp/code/model/tests/test_hasrecipes.py (+8/-10)
lib/lp/code/model/tests/test_sourcepackagerecipe.py (+38/-3)
lib/lp/code/tests/test_recipebuilder.py (+4/-1)
lib/lp/testing/__init__.py (+15/-4)
lib/lp/testing/factory.py (+7/-6)
To merge this branch: bzr merge lp:~abentley/launchpad/recipe-security
Reviewer Review Type Date Requested Status
Edwin Grubbs (community) Approve
Review via email: mp+23357@code.launchpad.net

Commit message

Add launchpad.View to SourcePackageRecipe

Description of the change

= Summary =
Provide launchpad.View on SourcePackageRecipe, and use it to protect index and build request pages

== Proposed fix ==

== Pre-implementation notes ==
discussed with rockstar and thumper

== Implementation details ==
launchpad.Edit is already defined, but build requests only need launchpad.View, because they write to the PPA, not the recipe.

launchpad.View was defined in a generic way which should be useful in similar cases.

LaunchpadObjectFactory.makeSourcepackageRecipe's branches parameter was converted to a normal parameter so that it wasn't necessary to specify every possible parameter in order to specify a branch.

LaunchpadObjectFactory.makeSourcePackageRecipe was changed to do Store.flush to ensure that the vital recipe_data member is populated.

== Tests ==
bin/test test_sourcepackagerecipe -t test_view -t test_edit

== Demo and Q/A ==

= Launchpad lint =

Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.

Linting changed files:
  lib/lp/code/model/sourcepackagerecipedata.py
  lib/lp/code/templates/sourcepackagerecipe-request-builds.pt
  lib/canonical/launchpad/security.py
  lib/lp/soyuz/browser/tests/build-views.txt
  configs/development/build-from-branch.zcml
  lib/lp/testing/factory.py
  lib/lp/testing/__init__.py
  lib/lp/code/templates/sourcepackagerecipe-index.pt
  lib/canonical/launchpad/icing/style-3-0.css.in
  lib/lp/soyuz/browser/configure.zcml
  lib/lp/code/browser/sourcepackagerecipe.py
  lib/lp/code/model/sourcepackagerecipe.py
  lib/lp/code/browser/tests/test_sourcepackagerecipe.py
  lib/lp/code/interfaces/sourcepackagerecipe.py
  lib/lp/code/tests/test_recipebuilder.py
  lib/canonical/launchpad/browser/__init__.py
  lib/lp/code/model/tests/test_hasrecipes.py
  lib/lp/code/model/tests/test_sourcepackagerecipe.py
  lib/lp/soyuz/browser/archive.py

== Pyflakes notices ==

lib/lp/code/model/tests/test_hasrecipes.py
    31: local variable 'recipe1' is assigned to but never used
    32: local variable 'recipe2' is assigned to but never used
    33: local variable 'recipe_ignored' is assigned to but never used
    45: local variable 'recipe1' is assigned to but never used
    46: local variable 'recipe2' is assigned to but never used
    47: local variable 'recipe_ignored' is assigned to but never used
    60: local variable 'recipe1' is assigned to but never used
    61: local variable 'recipe2' is assigned to but never used
    62: local variable 'recipe_ignored' is assigned to but never used

== Pylint notices ==

lib/lp/code/model/sourcepackagerecipedata.py
    54: [E1002, _SourcePackageRecipeDataInstruction.__init__] Use super on an old style class
    221: [E1002, SourcePackageRecipeData.__init__] Use super on an old style class

lib/lp/code/browser/sourcepackagerecipe.py
    11: [F0401] Unable to import 'zope.component'
    13: [F0401] Unable to import 'zope.schema'
    14: [F0401] Unable to import 'zope.schema.vocabulary'
    15: [F0401] Unable to import 'canonical.widgets.itemswidgets'
    17: [F0401] Unable to import 'canonical.launchpad.interfaces'
    18: [F0401] Unable to import 'canonical.launchpad.webapp'
    21: [F0401] Unable to import 'canonical.launchpad.webapp.authorization'
    22: [F0401] Unable to import 'lp.buildmaster.interfaces.buildbase'
    23: [F0401] Unable to import 'lp.code.interfaces.sourcepackagerecipe'
    24: [F0401] Unable to import 'lp.soyuz.browser.archive'
    25: [F0401] Unable to import 'lp.soyuz.interfaces.archive'
    27: [F0401] Unable to import 'lp.registry.interfaces.distroseries'
    28: [F0401] Unable to import 'lp.registry.interfaces.pocket'

lib/lp/code/model/sourcepackagerecipe.py
    100: [W1001, SourcePackageRecipe] Use of "property" on an old style class

lib/lp/code/browser/tests/test_sourcepackagerecipe.py
    29: [E1002, TestSourcePackageRecipeView.setUp] Use super on an old style class

lib/canonical/launchpad/browser/__init__.py
    15: [F0401] Unable to import 'lp.soyuz.browser.binarypackagerelease'
    16: [F0401] Unable to import 'lp.code.browser.branchmergeproposal'
    17: [F0401] Unable to import 'lp.code.browser.branchref'
    18: [F0401] Unable to import 'lp.code.browser.branchsubscription'
    19: [F0401] Unable to import 'lp.code.browser.branchvisibilitypolicy'
    20: [F0401] Unable to import 'lp.code.browser.codeimport'
    21: [F0401] Unable to import 'lp.code.browser.codeimportmachine'
    22: [F0401] Unable to import 'lp.registry.browser.codeofconduct'
    23: [F0401] Unable to import 'lp.code.browser.codereviewcomment'
    24: [F0401] Unable to import 'lp.registry.browser.distributionmirror'
    25: [F0401] Unable to import 'lp.soyuz.browser.distributionsourcepackagerelease'
    26: [F0401] Unable to import 'lp.soyuz.browser.distroarchseries'
    27: [F0401] Unable to import 'lp.soyuz.browser.distroarchseriesbinarypackage'
    28: [F0401] Unable to import 'lp.soyuz.browser.distroarchseriesbinarypackagerelease'
    29: [F0401] Unable to import 'lp.soyuz.browser.distroseriesbinarypackage'
    30: [F0401] Unable to import 'lp.soyuz.browser.distroseriessourcepackagerelease'
    31: [F0401] Unable to import 'lp.answers.browser.faq'
    32: [F0401] Unable to import 'lp.answers.browser.faqcollection'
    33: [F0401] Unable to import 'lp.answers.browser.faqtarget'
    34: [F0401] Unable to import 'lp.registry.browser.featuredproject'
    35: [F0401] Unable to import 'canonical.launchpad.browser.feeds'
    36: [F0401] Unable to import 'lp.hardwaredb.browser.hwdb'
    37: [F0401] Unable to import 'lp.registry.browser.karma'
    38: [F0401] Unable to import 'canonical.launchpad.browser.launchpad'
    39: [F0401] Unable to import 'canonical.launchpad.browser.launchpadstatistic'
    40: [F0401] Unable to import 'canonical.launchpad.browser.librarian'
    41: [F0401] Unable to import 'canonical.launchpad.browser.logintoken'
    42: [F0401] Unable to import 'lp.registry.browser.mailinglists'
    43: [F0401] Unable to import 'lp.registry.browser.mentoringoffer'
    44: [F0401] Unable to import 'canonical.launchpad.browser.oauth'
    45: [F0401] Unable to import 'lp.registry.browser.objectreassignment'
    46: [F0401] Unable to import 'canonical.launchpad.browser.packagerelationship'
    47: [F0401] Unable to import 'lp.registry.browser.peoplemerge'
    48: [F0401] Unable to import 'lp.registry.browser.poll'
    49: [F0401] Unable to import 'lp.soyuz.browser.publishedpackage'
    50: [F0401] Unable to import 'lp.soyuz.browser.publishing'
    51: [F0401] Unable to import 'lp.answers.browser.question'
    52: [F0401] Unable to import 'lp.answers.browser.questiontarget'
    53: [F0401] Unable to import 'lp.soyuz.browser.queue'
    54: [F0401] Unable to import 'lp.soyuz.browser.sourcepackagerelease'
    55: [F0401] Unable to import 'lp.blueprints.browser.specificationbranch'
    56: [F0401] Unable to import 'lp.blueprints.browser.specificationdependency'
    57: [F0401] Unable to import 'lp.blueprints.browser.specificationfeedback'
    58: [F0401] Unable to import 'lp.blueprints.browser.specificationgoal'
    59: [F0401] Unable to import 'lp.blueprints.browser.specificationsubscription'
    60: [F0401] Unable to import 'lp.blueprints.browser.specificationtarget'
    61: [F0401] Unable to import 'lp.blueprints.browser.sprint'
    62: [F0401] Unable to import 'lp.blueprints.browser.sprintattendance'
    63: [F0401] Unable to import 'lp.blueprints.browser.sprintspecification'
    64: [F0401] Unable to import 'lp.registry.browser.team'
    65: [F0401] Unable to import 'lp.registry.browser.teammembership'
    66: [F0401] Unable to import 'canonical.launchpad.browser.temporaryblobstorage'
    67: [F0401] Unable to import 'canonical.launchpad.browser.widgets'

lib/lp/soyuz/browser/archive.py
    31: [F0401] Unable to import 'pytz'
    34: [F0401] Unable to import 'zope.app.form.browser'
    35: [F0401] Unable to import 'zope.component'
    36: [F0401] Unable to import 'zope.formlib'
    38: [F0401] Unable to import 'zope.security.proxy'
    39: [F0401] Unable to import 'zope.schema'
    40: [F0401] Unable to import 'zope.schema.interfaces'
    41: [F0401] Unable to import 'zope.schema.vocabulary'
    42: [F0401] Unable to import 'storm.zope.interfaces'
    46: [F0401] Unable to import 'canonical.cachedproperty'
    47: [F0401] Unable to import 'canonical.launchpad'
    48: [F0401] Unable to import 'canonical.launchpad.helpers'
    49: [F0401] Unable to import 'canonical.lazr.utils'
    50: [F0401] Unable to import 'lp.buildmaster.interfaces.buildbase'
    51: [F0401] Unable to import 'lp.services.browser_helpers'
    52: [F0401] Unable to import 'lp.services.worlddata.interfaces.country'
    53: [F0401] Unable to import 'lp.soyuz.browser.build'
    54: [F0401] Unable to import 'lp.soyuz.browser.sourceslist'
    56: [F0401] Unable to import 'canonical.launchpad.browser.librarian'
    57: [F0401] Unable to import 'lp.soyuz.adapters.archivedependencies'
    59: [F0401] Unable to import 'lp.soyuz.adapters.archivesourcepublication'
    61: [F0401] Unable to import 'lp.soyuz.interfaces.archive'
    64: [F0401] Unable to import 'lp.soyuz.interfaces.archivepermission'
    66: [F0401] Unable to import 'lp.soyuz.interfaces.archivesubscriber'
    67: [F0401] Unable to import 'lp.soyuz.interfaces.binarypackagename'
    68: [F0401] Unable to import 'lp.soyuz.interfaces.build'
    69: [F0401] Unable to import 'lp.soyuz.interfaces.buildrecords'
    70: [F0401] Unable to import 'lp.soyuz.interfaces.component'
    71: [F0401] Unable to import 'lp.registry.interfaces.series'
    72: [F0401] Unable to import 'canonical.launchpad.interfaces.launchpad'
    74: [F0401] Unable to import 'lp.soyuz.interfaces.packagecopyrequest'
    76: [F0401] Unable to import 'lp.soyuz.interfaces.packageset'
    77: [F0401] Unable to import 'lp.registry.interfaces.person'
    78: [F0401] Unable to import 'lp.registry.interfaces.pocket'
    79: [F0401] Unable to import 'lp.soyuz.interfaces.publishing'
    82: [F0401] Unable to import 'lp.registry.interfaces.sourcepackagename'
    84: [F0401] Unable to import 'canonical.launchpad.webapp'
    88: [F0401] Unable to import 'lp.soyuz.scripts.packagecopier'
    89: [F0401] Unable to import 'canonical.launchpad.webapp.authorization'
    90: [F0401] Unable to import 'canonical.launchpad.webapp.badge'
    91: [F0401] Unable to import 'canonical.launchpad.webapp.batching'
    92: [F0401] Unable to import 'canonical.launchpad.webapp.interfaces'
    93: [F0401] Unable to import 'canonical.launchpad.webapp.menu'
    94: [F0401] Unable to import 'canonical.launchpad.webapp.tales'
    95: [F0401] Unable to import 'canonical.widgets'
    97: [F0401] Unable to import 'canonical.widgets.itemswidgets'
    99: [F0401] Unable to import 'canonical.widgets.lazrjs'
    101: [F0401] Unable to import 'canonical.widgets.textwidgets'
    791: [E1002, ArchiveView.initialize] Use super on an old style class
    989: [E1002, ArchiveSourceSelectionFormView.setUpWidgets] Use super on an old style class

To post a comment you must log in.
Revision history for this message
Edwin Grubbs (edwin-grubbs) wrote :

Looks good.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'configs/development/build-from-branch.zcml'
2--- configs/development/build-from-branch.zcml 2010-04-08 16:04:56 +0000
3+++ configs/development/build-from-branch.zcml 2010-04-19 03:41:31 +0000
4@@ -19,13 +19,13 @@
5 class="lp.code.browser.sourcepackagerecipe.SourcePackageRecipeView"
6 name="+index"
7 template="../../lib/lp/code/templates/sourcepackagerecipe-index.pt"
8- permission="zope.Public"/>
9+ permission="launchpad.View"/>
10 <browser:page
11 for="lp.code.interfaces.sourcepackagerecipe.ISourcePackageRecipe"
12 class="lp.code.browser.sourcepackagerecipe.SourcePackageRecipeRequestBuildsView"
13 name="+request-builds"
14 template="../../lib/lp/code/templates/sourcepackagerecipe-request-builds.pt"
15- permission="zope.Public"/>
16+ permission="launchpad.View"/>
17 </facet>
18 <facet facet="branches">
19 <browser:defaultView
20@@ -37,7 +37,7 @@
21 class="lp.code.browser.sourcepackagerecipe.SourcePackageRecipeBuildView"
22 name="+index"
23 template="../../lib/lp/code/templates/sourcepackagerecipe-index.pt"
24- permission="zope.Public"/>
25+ permission="launchpad.View"/>
26 <browser:menus
27 classes="SourcePackageRecipeContextMenu"
28 module="lp.code.browser.sourcepackagerecipe"/>
29
30=== modified file 'lib/canonical/launchpad/security.py'
31--- lib/canonical/launchpad/security.py 2010-04-16 10:15:09 +0000
32+++ lib/canonical/launchpad/security.py 2010-04-19 03:41:31 +0000
33@@ -1,13 +1,15 @@
34 # Copyright 2009-2010 Canonical Ltd. This software is licensed under the
35 # GNU Affero General Public License version 3 (see the file LICENSE).
36
37+# pylint: disable-msg=F0401
38+
39 """Security policies for using content objects."""
40
41 __metaclass__ = type
42 __all__ = ['AuthorizationBase']
43
44 from zope.interface import implements, Interface
45-from zope.component import getUtility
46+from zope.component import getAdapter, getUtility
47
48 from canonical.launchpad.interfaces.account import IAccount
49 from lp.archiveuploader.permission import (
50@@ -26,6 +28,7 @@
51 IBranchMergeProposal)
52 from lp.code.interfaces.branchsubscription import (
53 IBranchSubscription)
54+from lp.code.interfaces.sourcepackagerecipe import ISourcePackageRecipe
55 from lp.bugs.interfaces.bug import IBug
56 from lp.bugs.interfaces.bugattachment import IBugAttachment
57 from lp.bugs.interfaces.bugbranch import IBugBranch
58@@ -2164,6 +2167,28 @@
59 return user.in_admin
60
61
62+class ViewSourcePackageRecipe(AuthorizationBase):
63+
64+ permission = "launchpad.View"
65+ usedfor = ISourcePackageRecipe
66+
67+ def iter_adapters(self):
68+ for branch in self.obj.getReferencedBranches():
69+ yield getAdapter(branch, IAuthorization, self.permission)
70+
71+ def checkAuthenticated(self, user):
72+ for adapter in self.iter_adapters():
73+ if not adapter.checkAuthenticated(user):
74+ return False
75+ return True
76+
77+ def checkUnauthenticated(self):
78+ for adapter in self.iter_adapters():
79+ if not adapter.checkUnauthenticated():
80+ return False
81+ return True
82+
83+
84 class ViewSourcePackagePublishingHistory(ViewArchive):
85 """Restrict viewing of source publications."""
86 permission = "launchpad.View"
87
88=== modified file 'lib/lp/code/browser/tests/test_sourcepackagerecipe.py'
89--- lib/lp/code/browser/tests/test_sourcepackagerecipe.py 2010-04-13 13:13:56 +0000
90+++ lib/lp/code/browser/tests/test_sourcepackagerecipe.py 2010-04-19 03:41:31 +0000
91@@ -42,9 +42,9 @@
92 cake_branch = self.factory.makeProductBranch(
93 owner=self.chef, name='cake', product=chocolate)
94 return self.factory.makeSourcePackageRecipe(
95- None, self.chef, self.squirrel, None, u'cake_recipe',
96- u'This recipe builds a foo for disto bar, with my Secret Squirrel'
97- ' changes.', cake_branch)
98+ owner=self.chef, distroseries=self.squirrel, name=u'cake_recipe',
99+ description=u'This recipe builds a foo for disto bar, with my'
100+ ' Secret Squirrel changes.', branches=[cake_branch])
101
102 def getRecipeBrowser(self, recipe, view_name=None):
103 """Return a browser for the specified recipe, opened as Chef."""
104
105=== modified file 'lib/lp/code/interfaces/sourcepackagerecipe.py'
106--- lib/lp/code/interfaces/sourcepackagerecipe.py 2010-04-06 20:17:04 +0000
107+++ lib/lp/code/interfaces/sourcepackagerecipe.py 2010-04-19 03:41:31 +0000
108@@ -1,7 +1,7 @@
109 # Copyright 2009 Canonical Ltd. This software is licensed under the
110 # GNU Affero General Public License version 3 (see the file LICENSE).
111
112-# pylint: disable-msg=E0211,E0213
113+# pylint: disable-msg=E0211,E0213,F0401
114
115 """Interface of the `SourcePackageRecipe` content type."""
116
117@@ -62,6 +62,10 @@
118 description = _(
119 'The template that will be used to generate a deb version.'),)
120
121+ def getReferencedBranches():
122+ """An iterator of the branches referenced by this recipe."""
123+
124+
125
126 class ISourcePackageRecipe(IHasOwner, ISourcePackageRecipeData):
127 """An ISourcePackageRecipe describes how to build a source package.
128@@ -105,9 +109,6 @@
129 IBranch, title=_("The base branch used by this recipe."),
130 required=True, readonly=True)
131
132- def getReferencedBranches():
133- """An iterator of the branches referenced by this recipe."""
134-
135 def requestBuild(archive, distroseries, requester, pocket):
136 """Request that the recipe be built in to the specified archive.
137
138
139=== modified file 'lib/lp/code/model/sourcepackagerecipe.py'
140--- lib/lp/code/model/sourcepackagerecipe.py 2010-04-06 20:17:04 +0000
141+++ lib/lp/code/model/sourcepackagerecipe.py 2010-04-19 03:41:31 +0000
142@@ -1,6 +1,8 @@
143 # Copyright 2009 Canonical Ltd. This software is licensed under the
144 # GNU Affero General Public License version 3 (see the file LICENSE).
145
146+# pylint: disable-msg=F0401
147+
148 """Implementation of the `SourcePackageRecipe` content type."""
149
150 __metaclass__ = type
151@@ -101,10 +103,6 @@
152 def base_branch(self):
153 return self._recipe_data.base_branch
154
155- def getReferencedBranches(self):
156- """See `ISourcePackageRecipe.getReferencedBranches`."""
157- return self._recipe_data.getReferencedBranches()
158-
159 @staticmethod
160 def new(registrant, owner, distroseries, sourcepackagename, name,
161 builder_recipe, description):
162
163=== modified file 'lib/lp/code/model/sourcepackagerecipedata.py'
164--- lib/lp/code/model/sourcepackagerecipedata.py 2010-04-06 20:17:04 +0000
165+++ lib/lp/code/model/sourcepackagerecipedata.py 2010-04-19 03:41:31 +0000
166@@ -1,6 +1,8 @@
167 # Copyright 2009-2010 Canonical Ltd. This software is licensed under the
168 # GNU Affero General Public License version 3 (see the file LICENSE).
169
170+# pylint: disable-msg=F0401
171+
172 """Implementation of the recipe storage.
173
174 This is purely an implementation detail of SourcePackageRecipe.recipe_data and
175@@ -24,6 +26,7 @@
176 from canonical.launchpad.interfaces.lpstorm import IStore
177
178 from lp.code.model.branch import Branch
179+from lp.code.interfaces.branch import NoSuchBranch
180 from lp.code.interfaces.branchlookup import IBranchLookup
181 from lp.code.interfaces.sourcepackagerecipe import (
182 ForbiddenInstruction, TooNewRecipeFormat)
183@@ -205,6 +208,8 @@
184 self.instructions.find().remove()
185 branch_lookup = getUtility(IBranchLookup)
186 base_branch = branch_lookup.getByUrl(builder_recipe.url)
187+ if base_branch is None:
188+ raise NoSuchBranch(builder_recipe.url)
189 if builder_recipe.revspec is not None:
190 self.revspec = unicode(builder_recipe.revspec)
191 self._recordInstructions(
192
193=== modified file 'lib/lp/code/model/tests/test_hasrecipes.py'
194--- lib/lp/code/model/tests/test_hasrecipes.py 2010-04-08 19:51:59 +0000
195+++ lib/lp/code/model/tests/test_hasrecipes.py 2010-04-19 03:41:31 +0000
196@@ -1,6 +1,8 @@
197 # Copyright 2010 Canonical Ltd. This software is licensed under the
198 # GNU Affero General Public License version 3 (see the file LICENSE).
199
200+# pylint: disable-msg=F0401
201+
202 """Tests for classes that implement IHasRecipes."""
203
204 __metaclass__ = type
205@@ -26,10 +28,8 @@
206 # IBranch.recipes should provide all the SourcePackageRecipes attached
207 # to that branch.
208 base_branch = self.factory.makeBranch()
209- recipe1 = self.factory.makeSourcePackageRecipe(
210- None, None, None, None, None, None, base_branch)
211- recipe2 = self.factory.makeSourcePackageRecipe(
212- None, None, None, None, None, None, base_branch)
213+ recipe1 = self.factory.makeSourcePackageRecipe(branches=[base_branch])
214+ recipe2 = self.factory.makeSourcePackageRecipe(branches=[base_branch])
215 recipe_ignored = self.factory.makeSourcePackageRecipe()
216 self.assertEqual(2, base_branch.getRecipes().count())
217
218@@ -53,14 +53,12 @@
219 self.assertProvides(product, IHasRecipes)
220
221 def test_product_getRecipes(self):
222- # IProduct.recipes should provide all the SourcePackageRecipes attached
223- # to that product's branches.
224+ # IProduct.recipes should provide all the SourcePackageRecipes
225+ # attached to that product's branches.
226 product = self.factory.makeProduct()
227 branch = self.factory.makeBranch(product=product)
228- recipe1 = self.factory.makeSourcePackageRecipe(
229- None, None, None, None, None, None, branch)
230- recipe2 = self.factory.makeSourcePackageRecipe(
231- None, None, None, None, None, None, branch)
232+ recipe1 = self.factory.makeSourcePackageRecipe(branches=[branch])
233+ recipe2 = self.factory.makeSourcePackageRecipe(branches=[branch])
234 recipe_ignored = self.factory.makeSourcePackageRecipe()
235 self.assertEqual(2, product.getRecipes().count())
236
237
238=== modified file 'lib/lp/code/model/tests/test_sourcepackagerecipe.py'
239--- lib/lp/code/model/tests/test_sourcepackagerecipe.py 2010-03-26 19:29:56 +0000
240+++ lib/lp/code/model/tests/test_sourcepackagerecipe.py 2010-04-19 03:41:31 +0000
241@@ -1,15 +1,19 @@
242 # Copyright 2010 Canonical Ltd. This software is licensed under the
243 # GNU Affero General Public License version 3 (see the file LICENSE).
244
245+# pylint: disable-msg=F0401
246+
247 """Tests for SourcePackageRecipes."""
248
249+from __future__ import with_statement
250+
251 __metaclass__ = type
252
253 import unittest
254
255-from canonical.testing import DatabaseFunctionalLayer, LaunchpadZopelessLayer
256-from lp.testing import (
257- login_person, run_with_login, TestCase, TestCaseWithFactory, time_counter)
258+from canonical.testing import DatabaseFunctionalLayer
259+from canonical.launchpad.webapp.authorization import check_permission
260+from lp.testing import (login_person, person_logged_in, TestCaseWithFactory)
261
262
263 class TestSourcePackageRecipe(TestCaseWithFactory):
264@@ -44,6 +48,37 @@
265 recipe.build_daily = True
266 self.assertTrue(recipe.build_daily)
267
268+ def test_view_public(self):
269+ """Anyone can view a recipe with public branches."""
270+ owner = self.factory.makePerson()
271+ branch = self.factory.makeAnyBranch(owner=owner)
272+ with person_logged_in(owner):
273+ recipe = self.factory.makeSourcePackageRecipe(branches=[branch])
274+ self.assertTrue(check_permission('launchpad.View', recipe))
275+ with person_logged_in(self.factory.makePerson()):
276+ self.assertTrue(check_permission('launchpad.View', recipe))
277+ self.assertTrue(check_permission('launchpad.View', recipe))
278+
279+ def test_view_private(self):
280+ """Recipes with private branches are restricted."""
281+ owner = self.factory.makePerson()
282+ branch = self.factory.makeAnyBranch(owner=owner, private=True)
283+ with person_logged_in(owner):
284+ recipe = self.factory.makeSourcePackageRecipe(branches=[branch])
285+ self.assertTrue(check_permission('launchpad.View', recipe))
286+ with person_logged_in(self.factory.makePerson()):
287+ self.assertFalse(check_permission('launchpad.View', recipe))
288+ self.assertFalse(check_permission('launchpad.View', recipe))
289+
290+ def test_edit(self):
291+ """Only the owner can edit a sourcepackagerecipe."""
292+ recipe = self.factory.makeSourcePackageRecipe()
293+ self.assertFalse(check_permission('launchpad.Edit', recipe))
294+ with person_logged_in(self.factory.makePerson()):
295+ self.assertFalse(check_permission('launchpad.Edit', recipe))
296+ with person_logged_in(recipe.owner):
297+ self.assertTrue(check_permission('launchpad.Edit', recipe))
298+
299
300 def test_suite():
301 return unittest.TestLoader().loadTestsFromName(__name__)
302
303=== modified file 'lib/lp/code/tests/test_recipebuilder.py'
304--- lib/lp/code/tests/test_recipebuilder.py 2010-04-12 05:52:01 +0000
305+++ lib/lp/code/tests/test_recipebuilder.py 2010-04-19 03:41:31 +0000
306@@ -3,6 +3,8 @@
307
308 """Test RecipeBuildBehavior."""
309
310+# pylint: disable-msg=F0401
311+
312 __metaclass__ = type
313
314 import transaction
315@@ -62,7 +64,8 @@
316 somebranch = self.factory.makeBranch(owner=requester, name="pkg",
317 product=self.factory.makeProduct("someapp"))
318 recipe = self.factory.makeSourcePackageRecipe(requester, requester,
319- distroseries, spn, u"recept", u"Recipe description", somebranch)
320+ distroseries, spn, u"recept", u"Recipe description",
321+ branches=[somebranch])
322 spb = self.factory.makeSourcePackageRecipeBuild(
323 sourcepackage=sourcepackage, recipe=recipe, requester=requester)
324 job = spb.makeJob()
325
326=== modified file 'lib/lp/testing/__init__.py'
327--- lib/lp/testing/__init__.py 2010-04-15 20:37:59 +0000
328+++ lib/lp/testing/__init__.py 2010-04-19 03:41:31 +0000
329@@ -1,7 +1,11 @@
330 # Copyright 2009, 2010 Canonical Ltd. This software is licensed under the
331 # GNU Affero General Public License version 3 (see the file LICENSE).
332
333-# pylint: disable-msg=W0401,C0301
334+# pylint: disable-msg=W0401,C0301,F0401
335+
336+
337+from __future__ import with_statement
338+
339
340 __metaclass__ = type
341 __all__ = [
342@@ -37,6 +41,7 @@
343 'ZopeTestInSubProcess',
344 ]
345
346+from contextlib import contextmanager
347 from datetime import datetime, timedelta
348 from inspect import getargspec, getmembers, getmro, isclass, ismethod
349 import os
350@@ -765,18 +770,24 @@
351 return mergeFunctionMetadata(function, wrapped)
352
353
354-def run_with_login(person, function, *args, **kwargs):
355- """Run 'function' with 'person' logged in."""
356+@contextmanager
357+def person_logged_in(person):
358 current_person = getUtility(ILaunchBag).user
359 logout()
360 login_person(person)
361 try:
362- return function(*args, **kwargs)
363+ yield
364 finally:
365 logout()
366 login_person(current_person)
367
368
369+def run_with_login(person, function, *args, **kwargs):
370+ """Run 'function' with 'person' logged in."""
371+ with person_logged_in(person):
372+ return function(*args, **kwargs)
373+
374+
375 def time_counter(origin=None, delta=timedelta(seconds=5)):
376 """A generator for yielding datetime values.
377
378
379=== modified file 'lib/lp/testing/factory.py'
380--- lib/lp/testing/factory.py 2010-04-17 11:58:39 +0000
381+++ lib/lp/testing/factory.py 2010-04-19 03:41:31 +0000
382@@ -1,6 +1,8 @@
383 # Copyright 2009 Canonical Ltd. This software is licensed under the
384 # GNU Affero General Public License version 3 (see the file LICENSE).
385
386+# pylint: disable-msg=F0401
387+
388 """Testing infrastructure for the Launchpad application.
389
390 This module should not have any actual tests.
391@@ -25,8 +27,6 @@
392 from textwrap import dedent
393
394 import pytz
395-from storm.store import Store
396-import transaction
397
398 from twisted.python.util import mergeFunctionMetadata
399
400@@ -39,9 +39,8 @@
401 from canonical.database.sqlbase import flush_database_updates
402
403 from canonical.launchpad.database.account import Account
404-from canonical.launchpad.database.emailaddress import EmailAddress
405 from canonical.launchpad.database.message import Message, MessageChunk
406-from canonical.launchpad.interfaces import IMasterStore
407+from canonical.launchpad.interfaces import IMasterStore, IStore
408 from canonical.launchpad.interfaces.account import (
409 AccountCreationRationale, AccountStatus, IAccountSet)
410 from canonical.launchpad.interfaces.emailaddress import (
411@@ -1723,7 +1722,7 @@
412
413 def makeSourcePackageRecipe(self, registrant=None, owner=None,
414 distroseries=None, sourcepackagename=None,
415- name=None, description=None, *branches):
416+ name=None, description=None, branches=()):
417 """Make a `SourcePackageRecipe`."""
418 if registrant is None:
419 registrant = self.makePerson()
420@@ -1741,9 +1740,11 @@
421 if description is None:
422 description = self.getUniqueString().decode('utf8')
423 recipe = self.makeRecipe(*branches)
424- return getUtility(ISourcePackageRecipeSource).new(
425+ source_package_recipe = getUtility(ISourcePackageRecipeSource).new(
426 registrant, owner, [distroseries], sourcepackagename, name,
427 recipe, description)
428+ IStore(source_package_recipe).flush()
429+ return source_package_recipe
430
431 def makeSourcePackageRecipeBuild(self, sourcepackage=None, recipe=None,
432 requester=None, archive=None,