Merge ~lgp171188/launchpad:split-security.py-blueprints into launchpad:master
- Git
- lp:~lgp171188/launchpad
- split-security.py-blueprints
- Merge into master
Proposed by
Guruprasad
Status: | Merged |
---|---|
Approved by: | Guruprasad |
Approved revision: | 01a3e3a8b000ee27e7aa7cd5141bf08e33b4dbc6 |
Merge reported by: | Otto Co-Pilot |
Merged at revision: | not available |
Proposed branch: | ~lgp171188/launchpad:split-security.py-blueprints |
Merge into: | launchpad:master |
Diff against target: |
450 lines (+210/-188) 4 files modified
lib/lp/blueprints/configure.zcml (+1/-0) lib/lp/blueprints/security.py (+204/-0) lib/lp/blueprints/tests/test_specification.py (+5/-5) lib/lp/security.py (+0/-183) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Colin Watson (community) | Approve | ||
Review via email: mp+426620@code.launchpad.net |
Commit message
Move the blueprints-related security adapters to lp.blueprints.
Description of the change
To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/lib/lp/blueprints/configure.zcml b/lib/lp/blueprints/configure.zcml |
2 | index 6279e87..36b9af7 100644 |
3 | --- a/lib/lp/blueprints/configure.zcml |
4 | +++ b/lib/lp/blueprints/configure.zcml |
5 | @@ -11,6 +11,7 @@ |
6 | xmlns:lp="http://namespaces.canonical.com/lp" |
7 | i18n_domain="launchpad"> |
8 | |
9 | + <authorizations module=".security" /> |
10 | <include package=".browser"/> |
11 | <include package=".vocabularies"/> |
12 | |
13 | diff --git a/lib/lp/blueprints/security.py b/lib/lp/blueprints/security.py |
14 | new file mode 100644 |
15 | index 0000000..b4d8da2 |
16 | --- /dev/null |
17 | +++ b/lib/lp/blueprints/security.py |
18 | @@ -0,0 +1,204 @@ |
19 | +# Copyright 2009-2022 Canonical Ltd. This software is licensed under the |
20 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
21 | + |
22 | +"""Security adapters for the blueprints package.""" |
23 | + |
24 | +__all__ = [] |
25 | + |
26 | +from lp.app.security import AnonymousAuthorization, AuthorizationBase |
27 | +from lp.blueprints.interfaces.specification import ( |
28 | + ISpecification, |
29 | + ISpecificationPublic, |
30 | + ISpecificationView, |
31 | +) |
32 | +from lp.blueprints.interfaces.specificationbranch import ISpecificationBranch |
33 | +from lp.blueprints.interfaces.specificationsubscription import ( |
34 | + ISpecificationSubscription, |
35 | +) |
36 | +from lp.blueprints.interfaces.sprint import ISprint |
37 | +from lp.blueprints.interfaces.sprintspecification import ISprintSpecification |
38 | +from lp.security import EditByOwnersOrAdmins, ModerateByRegistryExpertsOrAdmins |
39 | + |
40 | + |
41 | +class EditSpecificationBranch(AuthorizationBase): |
42 | + |
43 | + usedfor = ISpecificationBranch |
44 | + permission = "launchpad.Edit" |
45 | + |
46 | + def checkAuthenticated(self, user): |
47 | + """See `IAuthorization.checkAuthenticated`. |
48 | + |
49 | + :return: True or False. |
50 | + """ |
51 | + return True |
52 | + |
53 | + |
54 | +class ViewSpecificationBranch(EditSpecificationBranch): |
55 | + |
56 | + permission = "launchpad.View" |
57 | + |
58 | + def checkUnauthenticated(self): |
59 | + """See `IAuthorization.checkUnauthenticated`. |
60 | + |
61 | + :return: True or False. |
62 | + """ |
63 | + return True |
64 | + |
65 | + |
66 | +class AnonymousAccessToISpecificationPublic(AnonymousAuthorization): |
67 | + """Anonymous users have launchpad.View on ISpecificationPublic. |
68 | + |
69 | + This is only needed because lazr.restful is hard-coded to check that |
70 | + permission before returning things in a collection. |
71 | + """ |
72 | + |
73 | + permission = "launchpad.View" |
74 | + usedfor = ISpecificationPublic |
75 | + |
76 | + |
77 | +class ViewSpecification(AuthorizationBase): |
78 | + |
79 | + permission = "launchpad.LimitedView" |
80 | + usedfor = ISpecificationView |
81 | + |
82 | + def checkAuthenticated(self, user): |
83 | + return self.obj.userCanView(user) |
84 | + |
85 | + def checkUnauthenticated(self): |
86 | + return self.obj.userCanView(None) |
87 | + |
88 | + |
89 | +class EditSpecificationByRelatedPeople(AuthorizationBase): |
90 | + """We want everybody "related" to a specification to be able to edit it. |
91 | + You are related if you have a role on the spec, or if you have a role on |
92 | + the spec target (distro/product) or goal (distroseries/productseries). |
93 | + """ |
94 | + |
95 | + permission = "launchpad.Edit" |
96 | + usedfor = ISpecification |
97 | + |
98 | + def checkAuthenticated(self, user): |
99 | + assert self.obj.target |
100 | + goal = self.obj.goal |
101 | + if goal is not None: |
102 | + if user.isOwner(goal) or user.isDriver(goal): |
103 | + return True |
104 | + return ( |
105 | + user.in_admin |
106 | + or user.in_registry_experts |
107 | + or user.isOwner(self.obj.target) |
108 | + or user.isDriver(self.obj.target) |
109 | + or user.isOneOf( |
110 | + self.obj, ["owner", "drafter", "assignee", "approver"] |
111 | + ) |
112 | + ) |
113 | + |
114 | + |
115 | +class AdminSpecification(AuthorizationBase): |
116 | + permission = "launchpad.Admin" |
117 | + usedfor = ISpecification |
118 | + |
119 | + def checkAuthenticated(self, user): |
120 | + assert self.obj.target |
121 | + return ( |
122 | + user.in_admin |
123 | + or user.in_registry_experts |
124 | + or user.isOwner(self.obj.target) |
125 | + or user.isDriver(self.obj.target) |
126 | + ) |
127 | + |
128 | + |
129 | +class DriverSpecification(AuthorizationBase): |
130 | + permission = "launchpad.Driver" |
131 | + usedfor = ISpecification |
132 | + |
133 | + def checkAuthenticated(self, user): |
134 | + # If no goal is proposed for the spec then there can be no |
135 | + # drivers for it - we use launchpad.Driver on a spec to decide |
136 | + # if the person can see the page which lets you decide whether |
137 | + # to accept the goal, and if there is no goal then this is |
138 | + # extremely difficult to do :-) |
139 | + return self.obj.goal and self.forwardCheckAuthenticated( |
140 | + user, self.obj.goal |
141 | + ) |
142 | + |
143 | + |
144 | +class EditSprintSpecification(AuthorizationBase): |
145 | + """The sprint owner or driver can say what makes it onto the agenda for |
146 | + the sprint. |
147 | + """ |
148 | + |
149 | + permission = "launchpad.Driver" |
150 | + usedfor = ISprintSpecification |
151 | + |
152 | + def checkAuthenticated(self, user): |
153 | + sprint = self.obj.sprint |
154 | + return user.isOwner(sprint) or user.isDriver(sprint) or user.in_admin |
155 | + |
156 | + |
157 | +class DriveSprint(AuthorizationBase): |
158 | + """The sprint owner or driver can say what makes it onto the agenda for |
159 | + the sprint. |
160 | + """ |
161 | + |
162 | + permission = "launchpad.Driver" |
163 | + usedfor = ISprint |
164 | + |
165 | + def checkAuthenticated(self, user): |
166 | + return ( |
167 | + user.isOwner(self.obj) or user.isDriver(self.obj) or user.in_admin |
168 | + ) |
169 | + |
170 | + |
171 | +class ViewSprint(AuthorizationBase): |
172 | + """An attendee, owner, or driver of a sprint.""" |
173 | + |
174 | + permission = "launchpad.View" |
175 | + usedfor = ISprint |
176 | + |
177 | + def checkAuthenticated(self, user): |
178 | + return ( |
179 | + user.isOwner(self.obj) |
180 | + or user.isDriver(self.obj) |
181 | + or user.person |
182 | + in [attendance.attendee for attendance in self.obj.attendances] |
183 | + or user.in_admin |
184 | + ) |
185 | + |
186 | + |
187 | +class EditSprint(EditByOwnersOrAdmins): |
188 | + usedfor = ISprint |
189 | + |
190 | + |
191 | +class ModerateSprint(ModerateByRegistryExpertsOrAdmins): |
192 | + """The sprint owner, registry experts, and admins can moderate sprints.""" |
193 | + |
194 | + permission = "launchpad.Moderate" |
195 | + usedfor = ISprint |
196 | + |
197 | + def checkAuthenticated(self, user): |
198 | + return super().checkAuthenticated(user) or user.isOwner(self.obj) |
199 | + |
200 | + |
201 | +class EditSpecificationSubscription(AuthorizationBase): |
202 | + """The subscriber, and people related to the spec or the target of the |
203 | + spec can determine who is essential.""" |
204 | + |
205 | + permission = "launchpad.Edit" |
206 | + usedfor = ISpecificationSubscription |
207 | + |
208 | + def checkAuthenticated(self, user): |
209 | + if self.obj.specification.goal is not None: |
210 | + if user.isDriver(self.obj.specification.goal): |
211 | + return True |
212 | + else: |
213 | + if user.isDriver(self.obj.specification.target): |
214 | + return True |
215 | + return ( |
216 | + user.inTeam(self.obj.person) |
217 | + or user.isOneOf( |
218 | + self.obj.specification, |
219 | + ["owner", "drafter", "assignee", "approver"], |
220 | + ) |
221 | + or user.in_admin |
222 | + ) |
223 | diff --git a/lib/lp/blueprints/tests/test_specification.py b/lib/lp/blueprints/tests/test_specification.py |
224 | index 211cb7d..bb01e66 100644 |
225 | --- a/lib/lp/blueprints/tests/test_specification.py |
226 | +++ b/lib/lp/blueprints/tests/test_specification.py |
227 | @@ -34,17 +34,17 @@ from lp.blueprints.model.specification import Specification |
228 | from lp.blueprints.model.specificationsearch import ( |
229 | get_specification_privacy_filter, |
230 | ) |
231 | +from lp.blueprints.security import ( |
232 | + AdminSpecification, |
233 | + EditSpecificationByRelatedPeople, |
234 | + ViewSpecification, |
235 | +) |
236 | from lp.registry.enums import SharingPermission, SpecificationSharingPolicy |
237 | from lp.registry.interfaces.accesspolicy import ( |
238 | IAccessArtifactGrantSource, |
239 | IAccessPolicySource, |
240 | ) |
241 | from lp.registry.interfaces.person import IPersonSet |
242 | -from lp.security import ( |
243 | - AdminSpecification, |
244 | - EditSpecificationByRelatedPeople, |
245 | - ViewSpecification, |
246 | -) |
247 | from lp.services.propertycache import get_property_cache |
248 | from lp.services.webapp.authorization import check_permission |
249 | from lp.services.webapp.interaction import ANONYMOUS |
250 | diff --git a/lib/lp/security.py b/lib/lp/security.py |
251 | index bfe9868..46c0f4a 100644 |
252 | --- a/lib/lp/security.py |
253 | +++ b/lib/lp/security.py |
254 | @@ -34,17 +34,6 @@ from lp.app.security import ( |
255 | DelegatedAuthorization, |
256 | ) |
257 | from lp.archivepublisher.interfaces.publisherconfig import IPublisherConfig |
258 | -from lp.blueprints.interfaces.specification import ( |
259 | - ISpecification, |
260 | - ISpecificationPublic, |
261 | - ISpecificationView, |
262 | - ) |
263 | -from lp.blueprints.interfaces.specificationbranch import ISpecificationBranch |
264 | -from lp.blueprints.interfaces.specificationsubscription import ( |
265 | - ISpecificationSubscription, |
266 | - ) |
267 | -from lp.blueprints.interfaces.sprint import ISprint |
268 | -from lp.blueprints.interfaces.sprintspecification import ISprintSpecification |
269 | from lp.bugs.interfaces.bugtarget import IOfficialBugTagTargetRestricted |
270 | from lp.bugs.interfaces.structuralsubscription import IStructuralSubscription |
271 | from lp.buildmaster.interfaces.builder import ( |
272 | @@ -268,178 +257,6 @@ class EditByOwnersOrAdmins(AuthorizationBase): |
273 | return user.isOwner(self.obj) or user.in_admin |
274 | |
275 | |
276 | -class EditSpecificationBranch(AuthorizationBase): |
277 | - |
278 | - usedfor = ISpecificationBranch |
279 | - permission = 'launchpad.Edit' |
280 | - |
281 | - def checkAuthenticated(self, user): |
282 | - """See `IAuthorization.checkAuthenticated`. |
283 | - |
284 | - :return: True or False. |
285 | - """ |
286 | - return True |
287 | - |
288 | - |
289 | -class ViewSpecificationBranch(EditSpecificationBranch): |
290 | - |
291 | - permission = 'launchpad.View' |
292 | - |
293 | - def checkUnauthenticated(self): |
294 | - """See `IAuthorization.checkUnauthenticated`. |
295 | - |
296 | - :return: True or False. |
297 | - """ |
298 | - return True |
299 | - |
300 | - |
301 | -class AnonymousAccessToISpecificationPublic(AnonymousAuthorization): |
302 | - """Anonymous users have launchpad.View on ISpecificationPublic. |
303 | - |
304 | - This is only needed because lazr.restful is hard-coded to check that |
305 | - permission before returning things in a collection. |
306 | - """ |
307 | - |
308 | - permission = 'launchpad.View' |
309 | - usedfor = ISpecificationPublic |
310 | - |
311 | - |
312 | -class ViewSpecification(AuthorizationBase): |
313 | - |
314 | - permission = 'launchpad.LimitedView' |
315 | - usedfor = ISpecificationView |
316 | - |
317 | - def checkAuthenticated(self, user): |
318 | - return self.obj.userCanView(user) |
319 | - |
320 | - def checkUnauthenticated(self): |
321 | - return self.obj.userCanView(None) |
322 | - |
323 | - |
324 | -class EditSpecificationByRelatedPeople(AuthorizationBase): |
325 | - """We want everybody "related" to a specification to be able to edit it. |
326 | - You are related if you have a role on the spec, or if you have a role on |
327 | - the spec target (distro/product) or goal (distroseries/productseries). |
328 | - """ |
329 | - |
330 | - permission = 'launchpad.Edit' |
331 | - usedfor = ISpecification |
332 | - |
333 | - def checkAuthenticated(self, user): |
334 | - assert self.obj.target |
335 | - goal = self.obj.goal |
336 | - if goal is not None: |
337 | - if user.isOwner(goal) or user.isDriver(goal): |
338 | - return True |
339 | - return (user.in_admin or |
340 | - user.in_registry_experts or |
341 | - user.isOwner(self.obj.target) or |
342 | - user.isDriver(self.obj.target) or |
343 | - user.isOneOf( |
344 | - self.obj, ['owner', 'drafter', 'assignee', 'approver'])) |
345 | - |
346 | - |
347 | -class AdminSpecification(AuthorizationBase): |
348 | - permission = 'launchpad.Admin' |
349 | - usedfor = ISpecification |
350 | - |
351 | - def checkAuthenticated(self, user): |
352 | - assert self.obj.target |
353 | - return ( |
354 | - user.in_admin or |
355 | - user.in_registry_experts or |
356 | - user.isOwner(self.obj.target) or |
357 | - user.isDriver(self.obj.target)) |
358 | - |
359 | - |
360 | -class DriverSpecification(AuthorizationBase): |
361 | - permission = 'launchpad.Driver' |
362 | - usedfor = ISpecification |
363 | - |
364 | - def checkAuthenticated(self, user): |
365 | - # If no goal is proposed for the spec then there can be no |
366 | - # drivers for it - we use launchpad.Driver on a spec to decide |
367 | - # if the person can see the page which lets you decide whether |
368 | - # to accept the goal, and if there is no goal then this is |
369 | - # extremely difficult to do :-) |
370 | - return ( |
371 | - self.obj.goal and |
372 | - self.forwardCheckAuthenticated(user, self.obj.goal)) |
373 | - |
374 | - |
375 | -class EditSprintSpecification(AuthorizationBase): |
376 | - """The sprint owner or driver can say what makes it onto the agenda for |
377 | - the sprint. |
378 | - """ |
379 | - permission = 'launchpad.Driver' |
380 | - usedfor = ISprintSpecification |
381 | - |
382 | - def checkAuthenticated(self, user): |
383 | - sprint = self.obj.sprint |
384 | - return user.isOwner(sprint) or user.isDriver(sprint) or user.in_admin |
385 | - |
386 | - |
387 | -class DriveSprint(AuthorizationBase): |
388 | - """The sprint owner or driver can say what makes it onto the agenda for |
389 | - the sprint. |
390 | - """ |
391 | - permission = 'launchpad.Driver' |
392 | - usedfor = ISprint |
393 | - |
394 | - def checkAuthenticated(self, user): |
395 | - return (user.isOwner(self.obj) or |
396 | - user.isDriver(self.obj) or |
397 | - user.in_admin) |
398 | - |
399 | - |
400 | -class ViewSprint(AuthorizationBase): |
401 | - """An attendee, owner, or driver of a sprint.""" |
402 | - permission = 'launchpad.View' |
403 | - usedfor = ISprint |
404 | - |
405 | - def checkAuthenticated(self, user): |
406 | - return (user.isOwner(self.obj) or |
407 | - user.isDriver(self.obj) or |
408 | - user.person in [attendance.attendee |
409 | - for attendance in self.obj.attendances] or |
410 | - user.in_admin) |
411 | - |
412 | - |
413 | -class EditSprint(EditByOwnersOrAdmins): |
414 | - usedfor = ISprint |
415 | - |
416 | - |
417 | -class ModerateSprint(ModerateByRegistryExpertsOrAdmins): |
418 | - """The sprint owner, registry experts, and admins can moderate sprints.""" |
419 | - permission = 'launchpad.Moderate' |
420 | - usedfor = ISprint |
421 | - |
422 | - def checkAuthenticated(self, user): |
423 | - return ( |
424 | - super().checkAuthenticated(user) or |
425 | - user.isOwner(self.obj)) |
426 | - |
427 | - |
428 | -class EditSpecificationSubscription(AuthorizationBase): |
429 | - """The subscriber, and people related to the spec or the target of the |
430 | - spec can determine who is essential.""" |
431 | - permission = 'launchpad.Edit' |
432 | - usedfor = ISpecificationSubscription |
433 | - |
434 | - def checkAuthenticated(self, user): |
435 | - if self.obj.specification.goal is not None: |
436 | - if user.isDriver(self.obj.specification.goal): |
437 | - return True |
438 | - else: |
439 | - if user.isDriver(self.obj.specification.target): |
440 | - return True |
441 | - return (user.inTeam(self.obj.person) or |
442 | - user.isOneOf( |
443 | - self.obj.specification, |
444 | - ['owner', 'drafter', 'assignee', 'approver']) or |
445 | - user.in_admin) |
446 | - |
447 | - |
448 | class OnlyRosettaExpertsAndAdmins(AuthorizationBase): |
449 | """Base class that allow access to Rosetta experts and Launchpad admins. |
450 | """ |