Merge ~cjwatson/launchpad:bug-attachment-removal-roles into launchpad:master

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: a4eb7199bfb5ac42c5b914fef1581abc6e3b6083
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~cjwatson/launchpad:bug-attachment-removal-roles
Merge into: launchpad:master
Diff against target: 153 lines (+48/-28)
2 files modified
lib/lp/bugs/browser/tests/test_bugattachment_edit_view.py (+24/-4)
lib/lp/bugs/security.py (+24/-24)
Reviewer Review Type Date Requested Status
Thiago F. Pappacena (community) Approve
Review via email: mp+382608@code.launchpad.net

Commit message

Allow people with bug target roles to edit attachments

Description of the change

In particular, this ensures that bug supervisors can edit attachments on bugs with corresponding tasks, which is needed by Ubuntu's retracers.

The implementation of this is borrowed from EditBug, and, as in that case, isn't particularly fast for bugs with many tasks. However, checking whether we can edit a bug attachment isn't a hot operation (in the UI, I don't believe we check this when viewing bugs, only on the page to edit an attachment), so we can continue to get away with this for the time being.

To post a comment you must log in.
Revision history for this message
Thiago F. Pappacena (pappacena) wrote :

LGTM! Thanks!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/lib/lp/bugs/browser/tests/test_bugattachment_edit_view.py b/lib/lp/bugs/browser/tests/test_bugattachment_edit_view.py
2index 34d9a03..8301459 100644
3--- a/lib/lp/bugs/browser/tests/test_bugattachment_edit_view.py
4+++ b/lib/lp/bugs/browser/tests/test_bugattachment_edit_view.py
5@@ -8,6 +8,7 @@ from zope.component import getUtility
6
7 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
8 from lp.testing import (
9+ login_admin,
10 login_person,
11 person_logged_in,
12 TestCaseWithFactory,
13@@ -56,8 +57,7 @@ class TestBugAttachmentEditView(TestCaseWithFactory):
14 'application/whatever', self.bugattachment.libraryfile.mimetype)
15
16 def test_admin_changes_any_attachment(self):
17- admin = self.factory.makeAdministrator()
18- login_person(admin)
19+ login_admin()
20 create_initialized_view(
21 self.bugattachment, name='+edit', form=self.CHANGE_FORM_DATA)
22 self.assertEqual('new description', self.bugattachment.title)
23@@ -74,6 +74,18 @@ class TestBugAttachmentEditView(TestCaseWithFactory):
24 self.assertEqual(
25 'application/whatever', self.bugattachment.libraryfile.mimetype)
26
27+ def test_pillar_bug_supervisor_changes_any_attachment(self):
28+ login_admin()
29+ bug_supervisor = self.factory.makePerson()
30+ self.bug.default_bugtask.pillar.bug_supervisor = bug_supervisor
31+ login_person(bug_supervisor)
32+ create_initialized_view(
33+ self.bugattachment, name='+edit', form=self.CHANGE_FORM_DATA)
34+ self.assertEqual('new description', self.bugattachment.title)
35+ self.assertTrue(self.bugattachment.is_patch)
36+ self.assertEqual(
37+ 'application/whatever', self.bugattachment.libraryfile.mimetype)
38+
39 def test_other_user_changes_attachment_fails(self):
40 random_user = self.factory.makePerson()
41 login_person(random_user)
42@@ -95,8 +107,7 @@ class TestBugAttachmentEditView(TestCaseWithFactory):
43 self.assertEqual(1, self.bug.attachments.count())
44
45 def test_admin_can_delete_any_attachment(self):
46- admin = self.factory.makeAdministrator()
47- login_person(admin)
48+ login_admin()
49 create_initialized_view(
50 self.bugattachment, name='+edit', form=self.DELETE_FORM_DATA)
51 self.assertEqual(0, self.bug.attachments.count())
52@@ -107,6 +118,15 @@ class TestBugAttachmentEditView(TestCaseWithFactory):
53 self.bugattachment, name='+edit', form=self.DELETE_FORM_DATA)
54 self.assertEqual(0, self.bug.attachments.count())
55
56+ def test_pillar_bug_supervisor_can_delete_any_attachment(self):
57+ login_admin()
58+ bug_supervisor = self.factory.makePerson()
59+ self.bug.default_bugtask.pillar.bug_supervisor = bug_supervisor
60+ login_person(bug_supervisor)
61+ create_initialized_view(
62+ self.bugattachment, name='+edit', form=self.DELETE_FORM_DATA)
63+ self.assertEqual(0, self.bug.attachments.count())
64+
65 def test_attachment_owner_can_delete_their_own_attachment(self):
66 bug = self.factory.makeBug(owner=self.bug_owner)
67 another_user = self.factory.makePerson()
68diff --git a/lib/lp/bugs/security.py b/lib/lp/bugs/security.py
69index 573f85a..381f93c 100644
70--- a/lib/lp/bugs/security.py
71+++ b/lib/lp/bugs/security.py
72@@ -1,4 +1,4 @@
73-# Copyright 2010-2019 Canonical Ltd. This software is licensed under the
74+# Copyright 2010-2020 Canonical Ltd. This software is licensed under the
75 # GNU Affero General Public License version 3 (see the file LICENSE).
76
77 """Security adapters for the bugs module."""
78@@ -122,6 +122,25 @@ class AppendBug(AuthorizationBase):
79 return False
80
81
82+def _has_any_bug_role(user, targets):
83+ """Return True if the user has any role on any of these bug targets."""
84+ # XXX cjwatson 2019-03-26: This is inefficient for bugs with many
85+ # targets. However, we only get here if we can't easily establish that
86+ # the user seems legitimate, so it shouldn't be a big problem in
87+ # practice. We can optimise this further if it turns out to matter.
88+ for target in targets:
89+ roles = []
90+ if IHasOwner.providedBy(target):
91+ roles.append('owner')
92+ if IHasAppointedDriver.providedBy(target):
93+ roles.append('driver')
94+ if IHasBugSupervisor.providedBy(target):
95+ roles.append('bug_supervisor')
96+ if user.isOneOf(target, roles):
97+ return True
98+ return False
99+
100+
101 class EditBug(AuthorizationBase):
102 """Security adapter for editing bugs.
103
104@@ -132,25 +151,6 @@ class EditBug(AuthorizationBase):
105 permission = 'launchpad.Edit'
106 usedfor = IBug
107
108- def _hasAnyRole(self, user, targets):
109- """Return True if the user has any role on any of these bug targets."""
110- # XXX cjwatson 2019-03-26: This is inefficient for bugs with many
111- # targets. However, we only get here if we can't easily establish
112- # that the user seems legitimate, so it shouldn't be a big problem
113- # in practice. We can optimise this further if it turns out to
114- # matter.
115- for target in targets:
116- roles = []
117- if IHasOwner.providedBy(target):
118- roles.append('owner')
119- if IHasAppointedDriver.providedBy(target):
120- roles.append('driver')
121- if IHasBugSupervisor.providedBy(target):
122- roles.append('bug_supervisor')
123- if user.isOneOf(target, roles):
124- return True
125- return False
126-
127 def checkAuthenticated(self, user):
128 """Allow sufficiently-trusted users to edit bugs.
129
130@@ -174,7 +174,7 @@ class EditBug(AuthorizationBase):
131 # Users with relevant roles can edit the bug.
132 user.in_admin or user.in_commercial_admin or
133 user.in_registry_experts or
134- self._hasAnyRole(
135+ _has_any_bug_role(
136 user, [task.target for task in self.obj.bugtasks]))
137
138 def checkUnauthenticated(self):
139@@ -221,11 +221,11 @@ class EditBugAttachment(AuthorizationBase):
140 usedfor = IBugAttachment
141
142 def checkAuthenticated(self, user):
143- # XXX: pappacena 2020-04-02: Maybe for bug tasks we should also allow
144- # pillar's bug supervisor to edit the attachments.
145 return (user.in_admin or
146 user.in_registry_experts or
147- user.inTeam(self.obj.message.owner))
148+ user.inTeam(self.obj.message.owner) or
149+ _has_any_bug_role(
150+ user, [task.target for task in self.obj.bug.bugtasks]))
151
152 def checkUnauthenticated(self):
153 return False

Subscribers

People subscribed via source and target branches

to status/vote changes: