Merge lp:~stevenk/launchpad/bugs-use-information_type-redux into lp:launchpad

Proposed by Steve Kowalik
Status: Merged
Approved by: Steve Kowalik
Approved revision: no longer in the source branch.
Merged at revision: 15009
Proposed branch: lp:~stevenk/launchpad/bugs-use-information_type-redux
Merge into: lp:launchpad
Diff against target: 399 lines (+96/-44)
9 files modified
lib/lp/bugs/adapters/bug.py (+16/-1)
lib/lp/bugs/browser/tests/test_bugsubscription_views.py (+1/-5)
lib/lp/bugs/mail/tests/test_handler.py (+2/-2)
lib/lp/bugs/model/bug.py (+32/-23)
lib/lp/bugs/model/tests/test_bug.py (+19/-0)
lib/lp/bugs/tests/test_bug_mirror_access_triggers.py (+3/-3)
lib/lp/registry/enums.py (+11/-0)
lib/lp/registry/model/person.py (+2/-2)
lib/lp/services/feeds/stories/xx-security.txt (+10/-8)
To merge this branch: bzr merge lp:~stevenk/launchpad/bugs-use-information_type-redux
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+99236@code.launchpad.net

Commit message

Resurrect use of IBug.information_type, setting it properly at creation time.

Description of the change

Resurrect the rollback of using IBug.information_type that happened in r15006. The original MPs description is as follows:

Rename IBug.{private,security_related} to IBug._{private,security_related} as the next step in switching to information_type. This branch also adds IBug.{private,security_related} back as a property that depends on information_type if it is set.

Fixed some tests that assumed that IBug.{private,security_related} was something they could set directly.

I cleaned up a small amount of lint.

Further to those changes, I have created a new method, convert_to_information_type, which returns the relevant InformationType given a private and security_related parameter, which allows me to remove IBug._setInformationType() and to set the information_type of the bug right at the initial insert. I have added a test which checks that case.

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

251 + def test_information_type_does_not_leak(self):
252 + product = self.factory.makeProduct()

Not entirely descriptive :)

Also:

10:52:57 < wgrant> StevenK: I think I mentioned this in the last review, but from the diff it looks like bug.private writes were previously permitted by the security policy, but I don't see any ZCML changes in this brnach to prevent that.
10:58:14 < StevenK> wgrant: wgrant: private/security_related aren't in the ZCML, so I'm not sure how to tell it "forbid anyone setting these"
11:00:04 < wgrant> StevenK: Indeed. I wonder if the removeSecurityProxy is really required, then

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/bugs/adapters/bug.py'
2--- lib/lp/bugs/adapters/bug.py 2010-08-20 20:31:18 +0000
3+++ lib/lp/bugs/adapters/bug.py 2012-03-26 00:18:19 +0000
4@@ -1,4 +1,4 @@
5-# Copyright 2009 Canonical Ltd. This software is licensed under the
6+# Copyright 2009-2012 Canonical Ltd. This software is licensed under the
7 # GNU Affero General Public License version 3 (see the file LICENSE).
8
9 """Resources having to do with Launchpad bugs."""
10@@ -7,11 +7,14 @@
11 __all__ = [
12 'bugcomment_to_entry',
13 'bugtask_to_privacy',
14+ 'convert_to_information_type',
15 ]
16
17 from lazr.restful.interfaces import IEntry
18 from zope.component import getMultiAdapter
19
20+from lp.registry.enums import InformationType
21+
22
23 def bugcomment_to_entry(comment, version):
24 """Will adapt to the bugcomment to the real IMessage.
25@@ -22,9 +25,21 @@
26 return getMultiAdapter(
27 (comment.bugtask.bug.messages[comment.index], version), IEntry)
28
29+
30 def bugtask_to_privacy(bugtask):
31 """Adapt the bugtask to the underlying bug (which implements IPrivacy).
32
33 Needed because IBugTask does not implement IPrivacy.
34 """
35 return bugtask.bug
36+
37+
38+def convert_to_information_type(private, security_related):
39+ if private and security_related:
40+ return InformationType.EMBARGOEDSECURITY
41+ elif security_related:
42+ return InformationType.UNEMBARGOEDSECURITY
43+ elif private:
44+ return InformationType.USERDATA
45+ else:
46+ return InformationType.PUBLIC
47
48=== modified file 'lib/lp/bugs/browser/tests/test_bugsubscription_views.py'
49--- lib/lp/bugs/browser/tests/test_bugsubscription_views.py 2012-03-24 04:42:32 +0000
50+++ lib/lp/bugs/browser/tests/test_bugsubscription_views.py 2012-03-26 00:18:19 +0000
51@@ -40,10 +40,6 @@
52 from lp.testing.views import create_initialized_view
53
54
55-ON = 'on'
56-OFF = None
57-
58-
59 class BugsubscriptionPrivacyTests(TestCaseWithFactory):
60
61 layer = LaunchpadFunctionalLayer
62@@ -52,7 +48,7 @@
63 super(BugsubscriptionPrivacyTests, self).setUp()
64 self.user = self.factory.makePerson()
65 self.bug = self.factory.makeBug(owner=self.user)
66- removeSecurityProxy(self.bug).private = True
67+ removeSecurityProxy(self.bug).setPrivate(True, self.user)
68
69 def _assert_subscription_fails(self, team):
70 with person_logged_in(self.user):
71
72=== modified file 'lib/lp/bugs/mail/tests/test_handler.py'
73--- lib/lp/bugs/mail/tests/test_handler.py 2012-03-24 04:42:32 +0000
74+++ lib/lp/bugs/mail/tests/test_handler.py 2012-03-26 00:18:19 +0000
75@@ -287,7 +287,7 @@
76 notification = self.getLatestBugNotification()
77 bug = notification.bug
78 self.assertEqual('unsecure code', bug.title)
79- self.assertEqual(True, bug.security_related)
80+ self.assertTrue(bug.security_related)
81 self.assertEqual(['ajax'], bug.tags)
82 self.assertEqual(1, len(bug.bugtasks))
83 self.assertEqual(project, bug.bugtasks[0].target)
84@@ -310,7 +310,7 @@
85 notification = self.getLatestBugNotification()
86 bug = notification.bug
87 self.assertEqual('security issue', bug.title)
88- self.assertEqual(True, bug.security_related)
89+ self.assertTrue(bug.security_related)
90 self.assertEqual(1, len(bug.bugtasks))
91 self.assertEqual(project, bug.bugtasks[0].target)
92 recipients = set()
93
94=== modified file 'lib/lp/bugs/model/bug.py'
95--- lib/lp/bugs/model/bug.py 2012-03-24 04:42:32 +0000
96+++ lib/lp/bugs/model/bug.py 2012-03-26 00:18:19 +0000
97@@ -89,6 +89,7 @@
98 )
99 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
100 from lp.app.validators import LaunchpadValidationError
101+from lp.bugs.adapters.bug import convert_to_information_type
102 from lp.bugs.adapters.bugchange import (
103 BranchLinkedToBug,
104 BranchUnlinkedFromBug,
105@@ -157,7 +158,11 @@
106 )
107 from lp.code.interfaces.branchcollection import IAllBranches
108 from lp.hardwaredb.interfaces.hwdb import IHWSubmissionBugSet
109-from lp.registry.enums import InformationType
110+from lp.registry.enums import (
111+ InformationType,
112+ PRIVATE_INFORMATION_TYPES,
113+ SECURITY_INFORMATION_TYPES,
114+ )
115 from lp.registry.interfaces.distribution import IDistribution
116 from lp.registry.interfaces.distroseries import IDistroSeries
117 from lp.registry.interfaces.person import (
118@@ -347,12 +352,13 @@
119 dbName='duplicateof', foreignKey='Bug', default=None)
120 datecreated = UtcDateTimeCol(notNull=True, default=UTC_NOW)
121 date_last_updated = UtcDateTimeCol(notNull=True, default=UTC_NOW)
122- private = BoolCol(notNull=True, default=False)
123+ _private = BoolCol(dbName='private', notNull=True, default=False)
124 date_made_private = UtcDateTimeCol(notNull=False, default=None)
125 who_made_private = ForeignKey(
126 dbName='who_made_private', foreignKey='Person',
127 storm_validator=validate_public_person, default=None)
128- security_related = BoolCol(notNull=True, default=False)
129+ _security_related = BoolCol(
130+ dbName='security_related', notNull=True, default=False)
131 information_type = EnumCol(
132 enum=InformationType, default=InformationType.PUBLIC)
133
134@@ -389,6 +395,20 @@
135 heat_last_updated = UtcDateTimeCol(default=None)
136 latest_patch_uploaded = UtcDateTimeCol(default=None)
137
138+ @property
139+ def private(self):
140+ if self.information_type:
141+ return self.information_type in PRIVATE_INFORMATION_TYPES
142+ else:
143+ return self._private
144+
145+ @property
146+ def security_related(self):
147+ if self.information_type:
148+ return self.information_type in SECURITY_INFORMATION_TYPES
149+ else:
150+ return self._security_related
151+
152 @cachedproperty
153 def _subscriber_cache(self):
154 """Caches known subscribers."""
155@@ -1697,16 +1717,6 @@
156
157 return bugtask
158
159- def _setInformationType(self):
160- if self.private and self.security_related:
161- self.information_type = InformationType.EMBARGOEDSECURITY
162- elif self.private:
163- self.information_type = InformationType.USERDATA
164- elif self.security_related:
165- self.information_type = InformationType.UNEMBARGOEDSECURITY
166- else:
167- self.information_type = InformationType.PUBLIC
168-
169 def setPrivacyAndSecurityRelated(self, private, security_related, who):
170 """ See `IBug`."""
171 private_changed = False
172@@ -1734,7 +1744,7 @@
173 raise BugCannotBePrivate(
174 "Multi-pillar bugs cannot be private.")
175 private_changed = True
176- self.private = private
177+ self._private = private
178
179 if private:
180 self.who_made_private = who
181@@ -1750,14 +1760,15 @@
182
183 if self.security_related != security_related:
184 security_related_changed = True
185- self.security_related = security_related
186+ self._security_related = security_related
187
188 if private_changed or security_related_changed:
189 # Correct the heat for the bug immediately, so that we don't have
190 # to wait for the next calculation job for the adjusted heat.
191 self.updateHeat()
192
193- self._setInformationType()
194+ self.information_type = convert_to_information_type(
195+ self._private, self._security_related)
196
197 if private_changed or security_related_changed:
198 changed_fields = []
199@@ -2829,9 +2840,6 @@
200 bug.subscribe(params.product.bug_supervisor, params.owner)
201 else:
202 bug.subscribe(params.product.owner, params.owner)
203- else:
204- # nothing to do
205- pass
206
207 # Create the task on a product if one was passed.
208 if params.product:
209@@ -2858,8 +2866,6 @@
210 if notify_event:
211 notify(event)
212
213- bug._setInformationType()
214-
215 # Calculate the bug's initial heat.
216 bug.updateHeat()
217
218@@ -2906,11 +2912,14 @@
219 date_made_private=params.datecreated,
220 who_made_private=params.owner)
221
222+ information_type = convert_to_information_type(
223+ params.private, params.security_related)
224 bug = Bug(
225 title=params.title, description=params.description,
226- private=params.private, owner=params.owner,
227+ _private=params.private, owner=params.owner,
228 datecreated=params.datecreated,
229- security_related=params.security_related,
230+ _security_related=params.security_related,
231+ information_type=information_type,
232 **extra_params)
233
234 if params.subscribe_owner:
235
236=== modified file 'lib/lp/bugs/model/tests/test_bug.py'
237--- lib/lp/bugs/model/tests/test_bug.py 2012-03-14 12:39:42 +0000
238+++ lib/lp/bugs/model/tests/test_bug.py 2012-03-26 00:18:19 +0000
239@@ -9,6 +9,7 @@
240 )
241
242 from pytz import UTC
243+from storm.expr import Join
244 from storm.store import Store
245 from testtools.testcase import ExpectedException
246 from zope.component import getUtility
247@@ -970,6 +971,24 @@
248 bug.setPrivate(True, bug.owner)
249 self.assertEqual(InformationType.USERDATA, bug.information_type)
250
251+ def test_information_type_does_not_leak(self):
252+ # Make sure that bug notifications for private bugs do not leak to
253+ # people with a subscription on the product.
254+ product = self.factory.makeProduct()
255+ with person_logged_in(product.owner):
256+ product.addSubscription(product.owner, product.owner)
257+ reporter = self.factory.makePerson()
258+ bug = self.factory.makeBug(
259+ private=True, product=product, owner=reporter)
260+ recipients = Store.of(bug).using(
261+ BugNotificationRecipient,
262+ Join(BugNotification, BugNotification.bugID == bug.id)).find(
263+ BugNotificationRecipient,
264+ BugNotificationRecipient.bug_notificationID ==
265+ BugNotification.id)
266+ self.assertEqual(
267+ [reporter], [recipient.person for recipient in recipients])
268+
269
270 class TestBugPrivateAndSecurityRelatedUpdatesPrivateProject(
271 TestBugPrivateAndSecurityRelatedUpdatesMixin, TestCaseWithFactory):
272
273=== modified file 'lib/lp/bugs/tests/test_bug_mirror_access_triggers.py'
274--- lib/lp/bugs/tests/test_bug_mirror_access_triggers.py 2012-03-24 04:42:32 +0000
275+++ lib/lp/bugs/tests/test_bug_mirror_access_triggers.py 2012-03-26 00:18:19 +0000
276@@ -136,7 +136,7 @@
277 bug = self.makeBugAndPolicies(private=True)
278 self.assertIsNot(
279 None, getUtility(IAccessArtifactSource).find([bug]).one())
280- bug.private = False
281+ bug.setPrivate(False, bug.owner)
282 self.assertIs(
283 None, getUtility(IAccessArtifactSource).find([bug]).one())
284
285@@ -144,7 +144,7 @@
286 bug = self.makeBugAndPolicies(private=False)
287 self.assertIs(
288 None, getUtility(IAccessArtifactSource).find([bug]).one())
289- bug.private = True
290+ bug.setPrivate(True, bug.owner)
291 self.assertIsNot(
292 None, getUtility(IAccessArtifactSource).find([bug]).one())
293 self.assertEqual((1, 1), self.assertMirrored(bug))
294@@ -158,7 +158,7 @@
295 self.assertContentEqual(
296 [InformationType.USERDATA],
297 self.getPolicyTypesForArtifact(artifact))
298- bug.security_related = True
299+ bug.setSecurityRelated(True, bug.owner)
300 self.assertEqual((1, 1), self.assertMirrored(bug))
301 self.assertContentEqual(
302 [InformationType.EMBARGOEDSECURITY],
303
304=== modified file 'lib/lp/registry/enums.py'
305--- lib/lp/registry/enums.py 2012-03-24 04:42:32 +0000
306+++ lib/lp/registry/enums.py 2012-03-26 00:18:19 +0000
307@@ -9,7 +9,9 @@
308 'DistroSeriesDifferenceType',
309 'InformationType',
310 'PersonTransferJobType',
311+ 'PRIVATE_INFORMATION_TYPES',
312 'ProductJobType',
313+ 'SECURITY_INFORMATION_TYPES',
314 'SharingPermission',
315 ]
316
317@@ -61,6 +63,15 @@
318 """)
319
320
321+PRIVATE_INFORMATION_TYPES = (
322+ InformationType.EMBARGOEDSECURITY, InformationType.USERDATA,
323+ InformationType.PROPRIETARY)
324+
325+
326+SECURITY_INFORMATION_TYPES = (
327+ InformationType.UNEMBARGOEDSECURITY, InformationType.EMBARGOEDSECURITY)
328+
329+
330 class SharingPermission(DBEnumeratedType):
331 """Sharing permission.
332
333
334=== modified file 'lib/lp/registry/model/person.py'
335--- lib/lp/registry/model/person.py 2012-03-24 04:42:32 +0000
336+++ lib/lp/registry/model/person.py 2012-03-26 00:18:19 +0000
337@@ -1744,14 +1744,14 @@
338 Bug,
339 Join(BugSubscription, BugSubscription.bug_id == Bug.id)),
340 where=And(
341- Bug.private == True,
342+ Bug._private == True,
343 BugSubscription.person_id == self.id)),
344 Select(
345 Bug.id,
346 tables=(
347 Bug,
348 Join(BugTask, BugTask.bugID == Bug.id)),
349- where=And(Bug.private == True, BugTask.assignee == self.id)),
350+ where=And(Bug._private == True, BugTask.assignee == self.id)),
351 limit=1))
352 if private_bugs_involved.rowcount:
353 raise TeamSubscriptionPolicyError(
354
355=== modified file 'lib/lp/services/feeds/stories/xx-security.txt'
356--- lib/lp/services/feeds/stories/xx-security.txt 2012-03-24 04:42:32 +0000
357+++ lib/lp/services/feeds/stories/xx-security.txt 2012-03-26 00:18:19 +0000
358@@ -1,16 +1,16 @@
359-== Feeds do not display private bugs ==
360+Feeds do not display private bugs
361+=================================
362
363 Feeds never contain private bugs, as we are serving feeds over HTTP.
364 First, set all the bugs to private.
365
366 >>> from zope.security.interfaces import Unauthorized
367 >>> from BeautifulSoup import BeautifulStoneSoup as BSS
368+ >>> from lp.services.database.lpstorm import IStore
369+ >>> import transaction
370 >>> from lp.bugs.model.bug import Bug
371- >>> bugs = Bug.select()
372- >>> for bug in bugs:
373- ... bug.private = True
374- >>> from lp.services.database.sqlbase import flush_database_updates
375- >>> flush_database_updates()
376+ >>> IStore(Bug).find(Bug).set(_private=True)
377+ >>> transaction.commit()
378
379 There should be zero entries in these feeds, since all the bugs are private.
380
381@@ -26,7 +26,8 @@
382 >>> BSS(browser.contents)('entry')
383 []
384
385- >>> browser.open('http://feeds.launchpad.dev/~simple-team/latest-bugs.atom')
386+ >>> browser.open(
387+ ... 'http://feeds.launchpad.dev/~simple-team/latest-bugs.atom')
388 >>> BSS(browser.contents)('entry')
389 []
390
391@@ -66,7 +67,8 @@
392 >>> print extract_text(BSS(browser.contents)('tr')[0])
393 Bugs for Foo Bar
394
395- >>> browser.open('http://feeds.launchpad.dev/~simple-team/latest-bugs.html')
396+ >>> browser.open(
397+ ... 'http://feeds.launchpad.dev/~simple-team/latest-bugs.html')
398 >>> len(BSS(browser.contents)('tr'))
399 1
400