Merge ~pappacena/launchpad:bug-attachment-removal-restrictions into launchpad:master
- Git
- lp:~pappacena/launchpad
- bug-attachment-removal-restrictions
- Merge into master
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Thiago F. Pappacena | ||||
Approved revision: | 0aa3b0b5c64a04589bcc4c379b82ad012578fe5d | ||||
Merge reported by: | Otto Co-Pilot | ||||
Merged at revision: | not available | ||||
Proposed branch: | ~pappacena/launchpad:bug-attachment-removal-restrictions | ||||
Merge into: | launchpad:master | ||||
Diff against target: |
482 lines (+150/-107) 9 files modified
lib/lp/bugs/browser/bugattachment.py (+7/-3) lib/lp/bugs/browser/tests/test_bugattachment_edit_view.py (+52/-41) lib/lp/bugs/browser/tests/test_bugattachment_file_access.py (+3/-2) lib/lp/bugs/configure.zcml (+4/-3) lib/lp/bugs/doc/bugattachments.txt (+7/-13) lib/lp/bugs/interfaces/bugattachment.py (+48/-40) lib/lp/bugs/model/bugattachment.py (+1/-1) lib/lp/bugs/security.py (+10/-4) lib/lp/bugs/stories/bugattachments/xx-delete-bug-attachment.txt (+18/-0) |
||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Colin Watson (community) | Approve | ||
Review via email: mp+381537@code.launchpad.net |
Commit message
Restricting bug attachments removal so that only the attachment owner, admin users or registry experts can remove attachments.
This is done by double checking permission before removing at the backend, and removing the "Delete Attachment" button from the interface for users without permission to do so.
Description of the change
Thiago F. Pappacena (pappacena) : | # |
Ioana Lasc (ilasc) wrote : | # |
Colin Watson (cjwatson) wrote : | # |
I agree with Ioana that we should add an extra test here, though (per my comments) for a registry expert rather than for an LP developer.
- 3581fef... by Thiago F. Pappacena
-
Using zope permission system for bug attachment edit permission
- d48828a... by Thiago F. Pappacena
-
Minor refactoring
- 77ce41b... by Thiago F. Pappacena
-
Removing unused exception class
- ec057d2... by Thiago F. Pappacena
-
Adjusting some tests
- d7d73cd... by Thiago F. Pappacena
-
Minor refactoring and comment
Thiago F. Pappacena (pappacena) : | # |
Thiago F. Pappacena (pappacena) wrote : | # |
Changed the way we check permission to use Zope's machinery for both change and delete.
Also, changed the list of who can change/delete an attachment:
- Admin
- Registry expert
- The owner of the message with the attachment
- 51e3f3c... by Thiago F. Pappacena
-
Merge branch 'master' into bug-attachment-
removal- restrictions
Colin Watson (cjwatson) wrote : | # |
Looks good now, thanks! Just a few nits.
Also, please update the commit message of this MP, which is now out of date with the security policy you're applying.
- 0aa3b0b... by Thiago F. Pappacena
-
Formatting import and coding style changes
Thiago F. Pappacena (pappacena) wrote : | # |
Thanks for the review, cjwatson. The requested changes were pushed. I'll land this in some minutes.
Preview Diff
1 | diff --git a/lib/lp/bugs/browser/bugattachment.py b/lib/lp/bugs/browser/bugattachment.py |
2 | index b239c45..e96e9fe 100644 |
3 | --- a/lib/lp/bugs/browser/bugattachment.py |
4 | +++ b/lib/lp/bugs/browser/bugattachment.py |
5 | @@ -1,4 +1,4 @@ |
6 | -# Copyright 2010-2018 Canonical Ltd. This software is licensed under the |
7 | +# Copyright 2010-2020 Canonical Ltd. This software is licensed under the |
8 | # GNU Affero General Public License version 3 (see the file LICENSE). |
9 | |
10 | """Bug attachment views.""" |
11 | @@ -42,6 +42,7 @@ from lp.services.webapp import ( |
12 | GetitemNavigation, |
13 | Navigation, |
14 | ) |
15 | +from lp.services.webapp.authorization import check_permission |
16 | from lp.services.webapp.escaping import structured |
17 | from lp.services.webapp.interfaces import ( |
18 | ICanonicalUrlData, |
19 | @@ -135,7 +136,10 @@ class BugAttachmentEditView(LaunchpadFormView, BugAttachmentContentCheck): |
20 | patch=attachment.type == BugAttachmentType.PATCH, |
21 | contenttype=attachment.libraryfile.mimetype) |
22 | |
23 | - @action('Change', name='change') |
24 | + def canEditAttachment(self, action): |
25 | + return check_permission('launchpad.Edit', self.context) |
26 | + |
27 | + @action('Change', name='change', condition=canEditAttachment) |
28 | def change_action(self, action, data): |
29 | if data['patch']: |
30 | new_type = BugAttachmentType.PATCH |
31 | @@ -169,7 +173,7 @@ class BugAttachmentEditView(LaunchpadFormView, BugAttachmentContentCheck): |
32 | ILibraryFileAliasWithParent) |
33 | lfa_with_parent.mimetype = data['contenttype'] |
34 | |
35 | - @action('Delete Attachment', name='delete') |
36 | + @action('Delete Attachment', name='delete', condition=canEditAttachment) |
37 | def delete_action(self, action, data): |
38 | libraryfile_url = ProxiedLibraryFileAlias( |
39 | self.context.libraryfile, self.context).http_url |
40 | diff --git a/lib/lp/bugs/browser/tests/test_bugattachment_edit_view.py b/lib/lp/bugs/browser/tests/test_bugattachment_edit_view.py |
41 | index 2710d05..34d9a03 100644 |
42 | --- a/lib/lp/bugs/browser/tests/test_bugattachment_edit_view.py |
43 | +++ b/lib/lp/bugs/browser/tests/test_bugattachment_edit_view.py |
44 | @@ -1,11 +1,12 @@ |
45 | -# Copyright 2010 Canonical Ltd. This software is licensed under the |
46 | +# Copyright 2010-2020 Canonical Ltd. This software is licensed under the |
47 | # GNU Affero General Public License version 3 (see the file LICENSE). |
48 | |
49 | __metaclass__ = type |
50 | |
51 | import transaction |
52 | -from zope.security.interfaces import Unauthorized |
53 | +from zope.component import getUtility |
54 | |
55 | +from lp.app.interfaces.launchpad import ILaunchpadCelebrities |
56 | from lp.testing import ( |
57 | login_person, |
58 | person_logged_in, |
59 | @@ -30,6 +31,11 @@ class TestBugAttachmentEditView(TestCaseWithFactory): |
60 | def setUp(self): |
61 | super(TestBugAttachmentEditView, self).setUp() |
62 | self.bug_owner = self.factory.makePerson() |
63 | + self.registry_expert = self.factory.makePerson() |
64 | + registry = getUtility(ILaunchpadCelebrities).registry_experts |
65 | + with person_logged_in(registry.teamowner): |
66 | + registry.addMember(self.registry_expert, registry.teamowner) |
67 | + |
68 | login_person(self.bug_owner) |
69 | self.bug = self.factory.makeBug(owner=self.bug_owner) |
70 | self.bugattachment = self.factory.makeBugAttachment( |
71 | @@ -40,11 +46,8 @@ class TestBugAttachmentEditView(TestCaseWithFactory): |
72 | # we start the tests. |
73 | transaction.commit() |
74 | |
75 | - def test_change_action_public_bug(self): |
76 | - # Properties of attachments for public bugs can be |
77 | - # changed by every user. |
78 | - user = self.factory.makePerson() |
79 | - login_person(user) |
80 | + def test_user_changes_their_own_attachment(self): |
81 | + login_person(self.bugattachment.message.owner) |
82 | create_initialized_view( |
83 | self.bugattachment, name='+edit', form=self.CHANGE_FORM_DATA) |
84 | self.assertEqual('new description', self.bugattachment.title) |
85 | @@ -52,14 +55,9 @@ class TestBugAttachmentEditView(TestCaseWithFactory): |
86 | self.assertEqual( |
87 | 'application/whatever', self.bugattachment.libraryfile.mimetype) |
88 | |
89 | - def test_change_action_private_bug(self): |
90 | - # Subscribers of a private bug can edit attachments. |
91 | - user = self.factory.makePerson() |
92 | - self.bug.setPrivate(True, self.bug_owner) |
93 | - with person_logged_in(self.bug_owner): |
94 | - self.bug.subscribe(user, self.bug_owner) |
95 | - transaction.commit() |
96 | - login_person(user) |
97 | + def test_admin_changes_any_attachment(self): |
98 | + admin = self.factory.makeAdministrator() |
99 | + login_person(admin) |
100 | create_initialized_view( |
101 | self.bugattachment, name='+edit', form=self.CHANGE_FORM_DATA) |
102 | self.assertEqual('new description', self.bugattachment.title) |
103 | @@ -67,44 +65,57 @@ class TestBugAttachmentEditView(TestCaseWithFactory): |
104 | self.assertEqual( |
105 | 'application/whatever', self.bugattachment.libraryfile.mimetype) |
106 | |
107 | - def test_change_action_private_bug_unauthorized(self): |
108 | - # Other users cannot edit attachments of private bugs. |
109 | - user = self.factory.makePerson() |
110 | - self.bug.setPrivate(True, self.bug_owner) |
111 | - transaction.commit() |
112 | - login_person(user) |
113 | - self.assertRaises( |
114 | - Unauthorized, create_initialized_view, self.bugattachment, |
115 | - name='+edit', form=self.CHANGE_FORM_DATA) |
116 | + def test_registry_expert_changes_any_attachment(self): |
117 | + login_person(self.registry_expert) |
118 | + create_initialized_view( |
119 | + self.bugattachment, name='+edit', form=self.CHANGE_FORM_DATA) |
120 | + self.assertEqual('new description', self.bugattachment.title) |
121 | + self.assertTrue(self.bugattachment.is_patch) |
122 | + self.assertEqual( |
123 | + 'application/whatever', self.bugattachment.libraryfile.mimetype) |
124 | + |
125 | + def test_other_user_changes_attachment_fails(self): |
126 | + random_user = self.factory.makePerson() |
127 | + login_person(random_user) |
128 | + create_initialized_view( |
129 | + self.bugattachment, name='+edit', form=self.CHANGE_FORM_DATA) |
130 | + self.assertEqual('attachment description', self.bugattachment.title) |
131 | + self.assertFalse(self.bugattachment.is_patch) |
132 | + self.assertEqual('text/plain', self.bugattachment.libraryfile.mimetype) |
133 | |
134 | DELETE_FORM_DATA = { |
135 | 'field.actions.delete': 'Delete Attachment', |
136 | } |
137 | |
138 | - def test_delete_action_public_bug(self): |
139 | - # Bug attachments can be removed from a bug. |
140 | + def test_delete_cannot_be_performed_by_other_users(self): |
141 | user = self.factory.makePerson() |
142 | login_person(user) |
143 | create_initialized_view( |
144 | self.bugattachment, name='+edit', form=self.DELETE_FORM_DATA) |
145 | + self.assertEqual(1, self.bug.attachments.count()) |
146 | + |
147 | + def test_admin_can_delete_any_attachment(self): |
148 | + admin = self.factory.makeAdministrator() |
149 | + login_person(admin) |
150 | + create_initialized_view( |
151 | + self.bugattachment, name='+edit', form=self.DELETE_FORM_DATA) |
152 | self.assertEqual(0, self.bug.attachments.count()) |
153 | |
154 | - def test_delete_action_private_bug(self): |
155 | - # Subscribers of a private bug can delete attachments. |
156 | - user = self.factory.makePerson() |
157 | - self.bug.setPrivate(True, self.bug_owner) |
158 | - with person_logged_in(self.bug_owner): |
159 | - self.bug.subscribe(user, self.bug_owner) |
160 | - login_person(user) |
161 | + def test_registry_expert_can_delete_any_attachment(self): |
162 | + login_person(self.registry_expert) |
163 | create_initialized_view( |
164 | self.bugattachment, name='+edit', form=self.DELETE_FORM_DATA) |
165 | self.assertEqual(0, self.bug.attachments.count()) |
166 | |
167 | - def test_delete_action_private_bug_unautorized(self): |
168 | - # Other users cannot delete private bug attachments. |
169 | - user = self.factory.makePerson() |
170 | - self.bug.setPrivate(True, self.bug_owner) |
171 | - login_person(user) |
172 | - self.assertRaises( |
173 | - Unauthorized, create_initialized_view, self.bugattachment, |
174 | - name='+edit', form=self.DELETE_FORM_DATA) |
175 | + def test_attachment_owner_can_delete_their_own_attachment(self): |
176 | + bug = self.factory.makeBug(owner=self.bug_owner) |
177 | + another_user = self.factory.makePerson() |
178 | + attachment = self.factory.makeBugAttachment( |
179 | + bug=bug, owner=another_user, filename='foo.diff', |
180 | + data=b'the file content', description='some file', |
181 | + content_type='text/plain', is_patch=False) |
182 | + |
183 | + login_person(another_user) |
184 | + create_initialized_view( |
185 | + attachment, name='+edit', form=self.DELETE_FORM_DATA) |
186 | + self.assertEqual(0, bug.attachments.count()) |
187 | diff --git a/lib/lp/bugs/browser/tests/test_bugattachment_file_access.py b/lib/lp/bugs/browser/tests/test_bugattachment_file_access.py |
188 | index 2f68b38..8a967c8 100644 |
189 | --- a/lib/lp/bugs/browser/tests/test_bugattachment_file_access.py |
190 | +++ b/lib/lp/bugs/browser/tests/test_bugattachment_file_access.py |
191 | @@ -1,4 +1,4 @@ |
192 | -# Copyright 2010-2019 Canonical Ltd. This software is licensed under the |
193 | +# Copyright 2010-2020 Canonical Ltd. This software is licensed under the |
194 | # GNU Affero General Public License version 3 (see the file LICENSE). |
195 | |
196 | __metaclass__ = type |
197 | @@ -53,7 +53,8 @@ class TestAccessToBugAttachmentFiles(TestCaseWithFactory): |
198 | login_person(self.bug_owner) |
199 | self.bug = self.factory.makeBug(owner=self.bug_owner) |
200 | self.bugattachment = self.factory.makeBugAttachment( |
201 | - bug=self.bug, filename='foo.txt', data=b'file content') |
202 | + owner=self.bug_owner, bug=self.bug, filename='foo.txt', |
203 | + data=b'file content') |
204 | |
205 | def test_traversal_to_lfa_of_bug_attachment(self): |
206 | # Traversing to the URL provided by a ProxiedLibraryFileAlias of a |
207 | diff --git a/lib/lp/bugs/configure.zcml b/lib/lp/bugs/configure.zcml |
208 | index f410aa8..7edae44 100644 |
209 | --- a/lib/lp/bugs/configure.zcml |
210 | +++ b/lib/lp/bugs/configure.zcml |
211 | @@ -1,4 +1,4 @@ |
212 | -<!-- Copyright 2009-2019 Canonical Ltd. This software is licensed under the |
213 | +<!-- Copyright 2009-2020 Canonical Ltd. This software is licensed under the |
214 | GNU Affero General Public License version 3 (see the file LICENSE). |
215 | --> |
216 | |
217 | @@ -338,10 +338,11 @@ |
218 | class="lp.bugs.model.bugattachment.BugAttachment"> |
219 | <require |
220 | permission="launchpad.View" |
221 | - interface="lp.bugs.interfaces.bugattachment.IBugAttachment"/> |
222 | + interface="lp.bugs.interfaces.bugattachment.IBugAttachmentView"/> |
223 | <require |
224 | permission="launchpad.Edit" |
225 | - set_schema="lp.bugs.interfaces.bugattachment.IBugAttachment"/> |
226 | + interface="lp.bugs.interfaces.bugattachment.IBugAttachmentEdit" |
227 | + set_schema="lp.bugs.interfaces.bugattachment.IBugAttachmentView" /> |
228 | </class> |
229 | |
230 | <!-- BugAttachmentSet --> |
231 | diff --git a/lib/lp/bugs/doc/bugattachments.txt b/lib/lp/bugs/doc/bugattachments.txt |
232 | index 0bac3b7..977c944 100644 |
233 | --- a/lib/lp/bugs/doc/bugattachments.txt |
234 | +++ b/lib/lp/bugs/doc/bugattachments.txt |
235 | @@ -315,29 +315,25 @@ anonymous can read the attachment's attributes, but they can't set them: |
236 | >>> import transaction |
237 | >>> transaction.abort() |
238 | |
239 | -Both Sample Person and Foo Bar can access and set the attributes, though: |
240 | - |
241 | - >>> login('test@canonical.com') |
242 | - >>> attachment.title |
243 | - u'this fixes the bug' |
244 | - >>> attachment.title = 'Better Title' |
245 | +Attachment owner can access and set the attributes, though: |
246 | |
247 | >>> login('foo.bar@canonical.com') |
248 | >>> attachment.title |
249 | - u'Better Title' |
250 | + u'this fixes the bug' |
251 | >>> attachment.title = 'Even Better Title' |
252 | |
253 | Now let's make the bug private instead: |
254 | |
255 | >>> bug_four.setPrivate(True, getUtility(ILaunchBag).user) |
256 | True |
257 | + >>> logout() |
258 | |
259 | Foo Bar isn't explicitly subscribed to the bug, BUT they are an admin, so |
260 | -they can access and set the attachment's attributes: |
261 | +they can access the attachment's attributes: |
262 | |
263 | + >>> login('test@canonical.com') |
264 | >>> attachment.title |
265 | u'Even Better Title' |
266 | - >>> attachment.title = 'Even Better Title' |
267 | |
268 | Mr. No Privs, who is not subscribed to bug_four, cannot access or set the |
269 | attachments attributes: |
270 | @@ -365,13 +361,11 @@ Of course, anonymous is also not allowed to access or set them: |
271 | ... |
272 | Unauthorized: (..., 'title',... |
273 | |
274 | -Sample Person is explicitly subscribed, so they can both access and set |
275 | -the attributes: |
276 | +Sample Person is explicitly subscribed, so they can access the attributes: |
277 | |
278 | >>> login('test@canonical.com') |
279 | >>> attachment.title |
280 | u'Even Better Title' |
281 | - >>> attachment.title = 'Better Title' |
282 | |
283 | |
284 | Let's make the bug public again: |
285 | @@ -423,7 +417,7 @@ There are no patches attached to any bugs: |
286 | Let's make our attachment a patch and search again: |
287 | |
288 | >>> from lp.services.database.sqlbase import flush_database_updates |
289 | - >>> login('test@canonical.com') |
290 | + >>> login('foo.bar@canonical.com') |
291 | >>> attachment.type = BugAttachmentType.PATCH |
292 | >>> flush_database_updates() |
293 | >>> attachmenttype = BugAttachmentType.PATCH |
294 | diff --git a/lib/lp/bugs/interfaces/bugattachment.py b/lib/lp/bugs/interfaces/bugattachment.py |
295 | index bbfa055..7616fd6 100644 |
296 | --- a/lib/lp/bugs/interfaces/bugattachment.py |
297 | +++ b/lib/lp/bugs/interfaces/bugattachment.py |
298 | @@ -1,4 +1,4 @@ |
299 | -# Copyright 2009 Canonical Ltd. This software is licensed under the |
300 | +# Copyright 2009-2020 Canonical Ltd. This software is licensed under the |
301 | # GNU Affero General Public License version 3 (see the file LICENSE). |
302 | |
303 | """Bug attachment interfaces.""" |
304 | @@ -64,36 +64,8 @@ class BugAttachmentType(DBEnumeratedType): |
305 | """) |
306 | |
307 | |
308 | -class IBugAttachment(IHasBug): |
309 | - """A file attachment to an IBug. |
310 | - |
311 | - Launchpadlib example of accessing content of an attachment:: |
312 | - |
313 | - for attachment in bug.attachments: |
314 | - buffer = attachment.data.open() |
315 | - for line in buffer: |
316 | - print line |
317 | - buffer.close() |
318 | - |
319 | - Launchpadlib example of accessing metadata about an attachment:: |
320 | - |
321 | - attachment = bug.attachments[0] |
322 | - print "title:", attachment.title |
323 | - print "ispatch:", attachment.type |
324 | - |
325 | - For information about the file-like object returned by |
326 | - attachment.data.open() see lazr.restfulclient's documentation of the |
327 | - HostedFile object. |
328 | - |
329 | - Details about the message associated with an attachment can be found on |
330 | - the "message" attribute:: |
331 | - |
332 | - message = attachment.message |
333 | - print "subject:", message.subject.encode('utf-8') |
334 | - print "owner:", message.owner.display_name.encode('utf-8') |
335 | - print "created:", message.date_created |
336 | - """ |
337 | - export_as_webservice_entry() |
338 | +class IBugAttachmentView(IHasBug): |
339 | + """Interface for BugAttachment that requires launchpad.View permission.""" |
340 | |
341 | id = Int(title=_('ID'), required=True, readonly=True) |
342 | bug = exported( |
343 | @@ -126,10 +98,16 @@ class IBugAttachment(IHasBug): |
344 | description=_('Is this attachment a patch?'), |
345 | readonly=True) |
346 | |
347 | - @call_with(user=REQUEST_USER) |
348 | - @export_write_operation() |
349 | - def removeFromBug(user): |
350 | - """Remove the attachment from the bug.""" |
351 | + def getFileByName(filename): |
352 | + """Return the `ILibraryFileAlias` for the given file name. |
353 | + |
354 | + NotFoundError is raised if the given filename does not match |
355 | + libraryfile.filename. |
356 | + """ |
357 | + |
358 | + |
359 | +class IBugAttachmentEdit(Interface): |
360 | + """Interface for BugAttachment that requires launchpad.Edit permission.""" |
361 | |
362 | def destroySelf(): |
363 | """Delete this record. |
364 | @@ -137,12 +115,42 @@ class IBugAttachment(IHasBug): |
365 | The library file content for this attachment is set to None. |
366 | """ |
367 | |
368 | - def getFileByName(filename): |
369 | - """Return the `ILibraryFileAlias for the given file name. |
370 | + @call_with(user=REQUEST_USER) |
371 | + @export_write_operation() |
372 | + def removeFromBug(user): |
373 | + """Remove the attachment from the bug.""" |
374 | |
375 | - NotFoundError is raised if the given filename does not match |
376 | - libraryfile.filename. |
377 | - """ |
378 | + |
379 | +class IBugAttachment(IBugAttachmentView, IBugAttachmentEdit): |
380 | + """A file attachment to an IBug. |
381 | + |
382 | + Launchpadlib example of accessing content of an attachment:: |
383 | + |
384 | + for attachment in bug.attachments: |
385 | + buffer = attachment.data.open() |
386 | + for line in buffer: |
387 | + print line |
388 | + buffer.close() |
389 | + |
390 | + Launchpadlib example of accessing metadata about an attachment:: |
391 | + |
392 | + attachment = bug.attachments[0] |
393 | + print "title:", attachment.title |
394 | + print "ispatch:", attachment.type |
395 | + |
396 | + For information about the file-like object returned by |
397 | + attachment.data.open() see lazr.restfulclient's documentation of the |
398 | + HostedFile object. |
399 | + |
400 | + Details about the message associated with an attachment can be found on |
401 | + the "message" attribute:: |
402 | + |
403 | + message = attachment.message |
404 | + print "subject:", message.subject.encode('utf-8') |
405 | + print "owner:", message.owner.display_name.encode('utf-8') |
406 | + print "created:", message.date_created |
407 | + """ |
408 | + export_as_webservice_entry() |
409 | |
410 | |
411 | # Need to do this here because of circular imports. |
412 | diff --git a/lib/lp/bugs/model/bugattachment.py b/lib/lp/bugs/model/bugattachment.py |
413 | index d514e31..e621b52 100644 |
414 | --- a/lib/lp/bugs/model/bugattachment.py |
415 | +++ b/lib/lp/bugs/model/bugattachment.py |
416 | @@ -1,4 +1,4 @@ |
417 | -# Copyright 2009 Canonical Ltd. This software is licensed under the |
418 | +# Copyright 2009-2020 Canonical Ltd. This software is licensed under the |
419 | # GNU Affero General Public License version 3 (see the file LICENSE). |
420 | |
421 | __metaclass__ = type |
422 | diff --git a/lib/lp/bugs/security.py b/lib/lp/bugs/security.py |
423 | index 4e22be0..573f85a 100644 |
424 | --- a/lib/lp/bugs/security.py |
425 | +++ b/lib/lp/bugs/security.py |
426 | @@ -211,7 +211,7 @@ class ViewBugAttachment(DelegatedAuthorization): |
427 | bugattachment, bugattachment.bug) |
428 | |
429 | |
430 | -class EditBugAttachment(DelegatedAuthorization): |
431 | +class EditBugAttachment(AuthorizationBase): |
432 | """Security adapter for editing a bug attachment. |
433 | |
434 | If the user is authorized to view the bug, they're allowed to edit the |
435 | @@ -220,9 +220,15 @@ class EditBugAttachment(DelegatedAuthorization): |
436 | permission = 'launchpad.Edit' |
437 | usedfor = IBugAttachment |
438 | |
439 | - def __init__(self, bugattachment): |
440 | - super(EditBugAttachment, self).__init__( |
441 | - bugattachment, bugattachment.bug) |
442 | + def checkAuthenticated(self, user): |
443 | + # XXX: pappacena 2020-04-02: Maybe for bug tasks we should also allow |
444 | + # pillar's bug supervisor to edit the attachments. |
445 | + return (user.in_admin or |
446 | + user.in_registry_experts or |
447 | + user.inTeam(self.obj.message.owner)) |
448 | + |
449 | + def checkUnauthenticated(self): |
450 | + return False |
451 | |
452 | |
453 | class ViewBugActivity(DelegatedAuthorization): |
454 | diff --git a/lib/lp/bugs/stories/bugattachments/xx-delete-bug-attachment.txt b/lib/lp/bugs/stories/bugattachments/xx-delete-bug-attachment.txt |
455 | index 53ab490..3a45f61 100644 |
456 | --- a/lib/lp/bugs/stories/bugattachments/xx-delete-bug-attachment.txt |
457 | +++ b/lib/lp/bugs/stories/bugattachments/xx-delete-bug-attachment.txt |
458 | @@ -42,6 +42,24 @@ attachment. |
459 | >>> user_browser.getControl('Delete Attachment') is not None |
460 | True |
461 | |
462 | +But this delete option should not be shown for other users. |
463 | + |
464 | + >>> from lp.testing.pages import setupBrowserForUser |
465 | + >>> login('foo.bar@canonical.com') |
466 | + >>> another_user = factory.makePerson() |
467 | + >>> another_browser = setupBrowserForUser(another_user) |
468 | + >>> logout() |
469 | + |
470 | + >>> another_browser.open('http://launchpad.test/bugs/2') |
471 | + >>> another_browser.getLink(url=re.compile(r'.*/\+attachment/\d+$')).click() |
472 | + >>> print(another_browser.title) |
473 | + Bug #2... |
474 | + >>> try: |
475 | + ... another_browser.getControl('Delete Attachment') |
476 | + ... raise ValueError("'Delete Attachment' button shouldn't be here!") |
477 | + ... except LookupError: |
478 | + ... pass |
479 | + |
480 | If the button is pressed, the attachment will be deleted, which means |
481 | that it won't show up in the attachments portlet anymore. Since there |
482 | arent' any other attachments, the portlet won't show up at all. |
Code looks good to me but I would have the same question as Thiago typed himself inline: is this list what we want: attachments can be removed by admin users, launchpad developers, bug owner or by the user who uploaded the attachment?
Unit Tests also look good, one question: this test case might be in there and I might be missing this but if answer to above is yes, should we also test for LP developers being able delete ?