Merge ~lgp171188/launchpad:split-security.py-charms-oci-snappy into launchpad:master

Proposed by Guruprasad
Status: Merged
Approved by: Guruprasad
Approved revision: 064be2fb8291f6e94991404a98bc4f332a6220d0
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~lgp171188/launchpad:split-security.py-charms-oci-snappy
Merge into: launchpad:master
Diff against target: 911 lines (+438/-397)
7 files modified
lib/lp/charms/configure.zcml (+1/-0)
lib/lp/charms/security.py (+112/-0)
lib/lp/oci/configure.zcml (+1/-0)
lib/lp/oci/security.py (+157/-0)
lib/lp/security.py (+1/-397)
lib/lp/snappy/configure.zcml (+1/-0)
lib/lp/snappy/security.py (+165/-0)
Reviewer Review Type Date Requested Status
Colin Watson (community) Approve
Review via email: mp+426643@code.launchpad.net

Commit message

Split and move the security adapters for the charms, oci, security packages

To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) :
review: Approve
Revision history for this message
Guruprasad (lgp171188) :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/lib/lp/charms/configure.zcml b/lib/lp/charms/configure.zcml
2index 330a72d..74d490d 100644
3--- a/lib/lp/charms/configure.zcml
4+++ b/lib/lp/charms/configure.zcml
5@@ -11,6 +11,7 @@
6 xmlns:xmlrpc="http://namespaces.zope.org/xmlrpc"
7 i18n_domain="launchpad">
8
9+ <authorizations module=".security" />
10 <include package=".browser" />
11
12 <lp:help-folder folder="help" name="+help-charms" />
13diff --git a/lib/lp/charms/security.py b/lib/lp/charms/security.py
14new file mode 100644
15index 0000000..9d8dad2
16--- /dev/null
17+++ b/lib/lp/charms/security.py
18@@ -0,0 +1,112 @@
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 charms package."""
23+
24+__all__ = []
25+
26+from lp.app.security import (
27+ AnonymousAuthorization,
28+ AuthorizationBase,
29+ DelegatedAuthorization,
30+)
31+from lp.charms.interfaces.charmbase import ICharmBase, ICharmBaseSet
32+from lp.charms.interfaces.charmrecipe import (
33+ ICharmRecipe,
34+ ICharmRecipeBuildRequest,
35+)
36+from lp.charms.interfaces.charmrecipebuild import ICharmRecipeBuild
37+from lp.security import AdminByBuilddAdmin, EditByRegistryExpertsOrAdmins
38+
39+
40+class ViewCharmRecipe(AuthorizationBase):
41+ """Private charm recipes are only visible to their owners and admins."""
42+
43+ permission = "launchpad.View"
44+ usedfor = ICharmRecipe
45+
46+ def checkAuthenticated(self, user):
47+ return self.obj.visibleByUser(user.person)
48+
49+ def checkUnauthenticated(self):
50+ return self.obj.visibleByUser(None)
51+
52+
53+class EditCharmRecipe(AuthorizationBase):
54+ permission = "launchpad.Edit"
55+ usedfor = ICharmRecipe
56+
57+ def checkAuthenticated(self, user):
58+ return (
59+ user.isOwner(self.obj) or user.in_commercial_admin or user.in_admin
60+ )
61+
62+
63+class AdminCharmRecipe(AuthorizationBase):
64+ """Restrict changing build settings on charm recipes.
65+
66+ The security of the non-virtualised build farm depends on these
67+ settings, so they can only be changed by "PPA"/commercial admins, or by
68+ "PPA" self admins on charm recipes that they can already edit.
69+ """
70+
71+ permission = "launchpad.Admin"
72+ usedfor = ICharmRecipe
73+
74+ def checkAuthenticated(self, user):
75+ if user.in_ppa_admin or user.in_commercial_admin or user.in_admin:
76+ return True
77+ return user.in_ppa_self_admins and EditCharmRecipe(
78+ self.obj
79+ ).checkAuthenticated(user)
80+
81+
82+class ViewCharmRecipeBuildRequest(DelegatedAuthorization):
83+ permission = "launchpad.View"
84+ usedfor = ICharmRecipeBuildRequest
85+
86+ def __init__(self, obj):
87+ super().__init__(obj, obj.recipe, "launchpad.View")
88+
89+
90+class ViewCharmRecipeBuild(DelegatedAuthorization):
91+ permission = "launchpad.View"
92+ usedfor = ICharmRecipeBuild
93+
94+ def iter_objects(self):
95+ yield self.obj.recipe
96+
97+
98+class EditCharmRecipeBuild(AdminByBuilddAdmin):
99+ permission = "launchpad.Edit"
100+ usedfor = ICharmRecipeBuild
101+
102+ def checkAuthenticated(self, user):
103+ """Check edit access for snap package builds.
104+
105+ Allow admins, buildd admins, and the owner of the charm recipe.
106+ (Note that the requester of the build is required to be in the team
107+ that owns the charm recipe.)
108+ """
109+ auth_recipe = EditCharmRecipe(self.obj.recipe)
110+ if auth_recipe.checkAuthenticated(user):
111+ return True
112+ return super().checkAuthenticated(user)
113+
114+
115+class AdminCharmRecipeBuild(AdminByBuilddAdmin):
116+ usedfor = ICharmRecipeBuild
117+
118+
119+class ViewCharmBase(AnonymousAuthorization):
120+ """Anyone can view an `ICharmBase`."""
121+
122+ usedfor = ICharmBase
123+
124+
125+class EditCharmBase(EditByRegistryExpertsOrAdmins):
126+ usedfor = ICharmBase
127+
128+
129+class EditCharmBaseSet(EditByRegistryExpertsOrAdmins):
130+ usedfor = ICharmBaseSet
131diff --git a/lib/lp/oci/configure.zcml b/lib/lp/oci/configure.zcml
132index 82cf7ff..098a26d 100644
133--- a/lib/lp/oci/configure.zcml
134+++ b/lib/lp/oci/configure.zcml
135@@ -8,6 +8,7 @@
136 xmlns:webservice="http://namespaces.canonical.com/webservice"
137 i18n_domain="launchpad">
138
139+ <authorizations module=".security" />
140 <include package=".browser" />
141 <include file="vocabularies.zcml" />
142
143diff --git a/lib/lp/oci/security.py b/lib/lp/oci/security.py
144new file mode 100644
145index 0000000..0546859
146--- /dev/null
147+++ b/lib/lp/oci/security.py
148@@ -0,0 +1,157 @@
149+# Copyright 2009-2022 Canonical Ltd. This software is licensed under the
150+# GNU Affero General Public License version 3 (see the file LICENSE).
151+
152+"""Security adapters for the oci package."""
153+
154+__all__ = []
155+
156+from lp.app.security import (
157+ AnonymousAuthorization,
158+ AuthorizationBase,
159+ DelegatedAuthorization,
160+ )
161+from lp.oci.interfaces.ocipushrule import IOCIPushRule
162+from lp.oci.interfaces.ocirecipe import (
163+ IOCIRecipe,
164+ IOCIRecipeBuildRequest,
165+ )
166+from lp.oci.interfaces.ocirecipebuild import IOCIRecipeBuild
167+from lp.oci.interfaces.ocirecipesubscription import IOCIRecipeSubscription
168+from lp.oci.interfaces.ociregistrycredentials import IOCIRegistryCredentials
169+from lp.security import AdminByBuilddAdmin
170+
171+
172+class ViewOCIRecipeBuildRequest(DelegatedAuthorization):
173+ permission = 'launchpad.View'
174+ usedfor = IOCIRecipeBuildRequest
175+
176+ def __init__(self, obj):
177+ super().__init__(obj, obj.recipe, 'launchpad.View')
178+
179+
180+class ViewOCIRecipe(AnonymousAuthorization):
181+ """Anyone can view public `IOCIRecipe`, but only subscribers can view
182+ private ones.
183+ """
184+ usedfor = IOCIRecipe
185+
186+ def checkUnauthenticated(self):
187+ return self.obj.visibleByUser(None)
188+
189+ def checkAuthenticated(self, user):
190+ return self.obj.visibleByUser(user.person)
191+
192+
193+class EditOCIRecipe(AuthorizationBase):
194+ permission = 'launchpad.Edit'
195+ usedfor = IOCIRecipe
196+
197+ def checkAuthenticated(self, user):
198+ return (
199+ user.isOwner(self.obj) or
200+ user.in_commercial_admin or user.in_admin)
201+
202+
203+class AdminOCIRecipe(AuthorizationBase):
204+ """Restrict changing build settings on OCI recipes.
205+
206+ The security of the non-virtualised build farm depends on these
207+ settings, so they can only be changed by "PPA"/commercial admins, or by
208+ "PPA" self admins on OCI recipes that they can already edit.
209+ """
210+ permission = 'launchpad.Admin'
211+ usedfor = IOCIRecipe
212+
213+ def checkAuthenticated(self, user):
214+ if user.in_ppa_admin or user.in_commercial_admin or user.in_admin:
215+ return True
216+ return (
217+ user.in_ppa_self_admins
218+ and EditOCIRecipe(self.obj).checkAuthenticated(user))
219+
220+
221+class OCIRecipeSubscriptionEdit(AuthorizationBase):
222+ permission = 'launchpad.Edit'
223+ usedfor = IOCIRecipeSubscription
224+
225+ def checkAuthenticated(self, user):
226+ """Is the user able to edit an OCI recipe subscription?
227+
228+ Any team member can edit a OCI recipe subscription for their
229+ team.
230+ Launchpad Admins can also edit any OCI recipe subscription.
231+ The owner of the subscribed OCI recipe can edit the subscription. If
232+ the OCI recipe owner is a team, then members of the team can edit
233+ the subscription.
234+ """
235+ return (user.inTeam(self.obj.recipe.owner) or
236+ user.inTeam(self.obj.person) or
237+ user.inTeam(self.obj.subscribed_by) or
238+ user.in_admin)
239+
240+
241+class OCIRecipeSubscriptionView(AuthorizationBase):
242+ permission = 'launchpad.View'
243+ usedfor = IOCIRecipeSubscription
244+
245+ def checkUnauthenticated(self):
246+ return self.obj.recipe.visibleByUser(None)
247+
248+ def checkAuthenticated(self, user):
249+ return self.obj.recipe.visibleByUser(user.person)
250+
251+
252+class ViewOCIRecipeBuild(DelegatedAuthorization):
253+ permission = 'launchpad.View'
254+ usedfor = IOCIRecipeBuild
255+
256+ def iter_objects(self):
257+ yield self.obj.recipe
258+
259+
260+class EditOCIRecipeBuild(AdminByBuilddAdmin):
261+ permission = 'launchpad.Edit'
262+ usedfor = IOCIRecipeBuild
263+
264+ def checkAuthenticated(self, user):
265+ """Check edit access for OCI recipe builds.
266+
267+ Allow admins, buildd admins, and the owner of the OCI recipe.
268+ (Note that the requester of the build is required to be in the team
269+ that owns the OCI recipe.)
270+ """
271+ auth_recipe = EditOCIRecipe(self.obj.recipe)
272+ if auth_recipe.checkAuthenticated(user):
273+ return True
274+ return super().checkAuthenticated(user)
275+
276+
277+class AdminOCIRecipeBuild(AdminByBuilddAdmin):
278+ usedfor = IOCIRecipeBuild
279+
280+
281+class ViewOCIRegistryCredentials(AuthorizationBase):
282+ permission = 'launchpad.View'
283+ usedfor = IOCIRegistryCredentials
284+
285+ def checkAuthenticated(self, user):
286+ # This must be kept in sync with user_can_edit_credentials_for_owner
287+ # in lp.oci.interfaces.ociregistrycredentials.
288+ return (
289+ user.isOwner(self.obj) or
290+ user.in_admin)
291+
292+
293+class ViewOCIPushRule(AnonymousAuthorization):
294+ """Anyone can view an `IOCIPushRule`."""
295+ usedfor = IOCIPushRule
296+
297+
298+class OCIPushRuleEdit(AuthorizationBase):
299+ permission = 'launchpad.Edit'
300+ usedfor = IOCIPushRule
301+
302+ def checkAuthenticated(self, user):
303+ return (
304+ user.isOwner(self.obj.recipe) or
305+ user.in_commercial_admin or user.in_admin)
306diff --git a/lib/lp/security.py b/lib/lp/security.py
307index a87f341..71c71e8 100644
308--- a/lib/lp/security.py
309+++ b/lib/lp/security.py
310@@ -26,11 +26,7 @@ from datetime import (
311 import pytz
312 from zope.interface import Interface
313
314-from lp.app.security import (
315- AnonymousAuthorization,
316- AuthorizationBase,
317- DelegatedAuthorization,
318- )
319+from lp.app.security import AuthorizationBase
320 from lp.archivepublisher.interfaces.publisherconfig import IPublisherConfig
321 from lp.bugs.interfaces.bugtarget import IOfficialBugTagTargetRestricted
322 from lp.bugs.interfaces.structuralsubscription import IStructuralSubscription
323@@ -40,40 +36,9 @@ from lp.buildmaster.interfaces.builder import (
324 )
325 from lp.buildmaster.interfaces.buildfarmjob import IBuildFarmJob
326 from lp.buildmaster.interfaces.packagebuild import IPackageBuild
327-from lp.charms.interfaces.charmbase import (
328- ICharmBase,
329- ICharmBaseSet,
330- )
331-from lp.charms.interfaces.charmrecipe import (
332- ICharmRecipe,
333- ICharmRecipeBuildRequest,
334- )
335-from lp.charms.interfaces.charmrecipebuild import ICharmRecipeBuild
336-from lp.oci.interfaces.ocipushrule import IOCIPushRule
337-from lp.oci.interfaces.ocirecipe import (
338- IOCIRecipe,
339- IOCIRecipeBuildRequest,
340- )
341-from lp.oci.interfaces.ocirecipebuild import IOCIRecipeBuild
342-from lp.oci.interfaces.ocirecipesubscription import IOCIRecipeSubscription
343-from lp.oci.interfaces.ociregistrycredentials import IOCIRegistryCredentials
344 from lp.registry.interfaces.role import IHasOwner
345 from lp.services.config import config
346 from lp.services.webapp.interfaces import ILaunchpadRoot
347-from lp.snappy.interfaces.snap import (
348- ISnap,
349- ISnapBuildRequest,
350- )
351-from lp.snappy.interfaces.snapbase import (
352- ISnapBase,
353- ISnapBaseSet,
354- )
355-from lp.snappy.interfaces.snapbuild import ISnapBuild
356-from lp.snappy.interfaces.snappyseries import (
357- ISnappySeries,
358- ISnappySeriesSet,
359- )
360-from lp.snappy.interfaces.snapsubscription import ISnapSubscription
361
362
363 def is_commercial_case(obj, user):
364@@ -312,364 +277,3 @@ class EditPackageBuild(EditBuildFarmJob):
365
366 class ViewPublisherConfig(AdminByAdminsTeam):
367 usedfor = IPublisherConfig
368-
369-
370-class ViewSnap(AuthorizationBase):
371- """Private snaps are only visible to their owners and admins."""
372- permission = 'launchpad.View'
373- usedfor = ISnap
374-
375- def checkAuthenticated(self, user):
376- return self.obj.visibleByUser(user.person)
377-
378- def checkUnauthenticated(self):
379- return self.obj.visibleByUser(None)
380-
381-
382-class EditSnap(AuthorizationBase):
383- permission = 'launchpad.Edit'
384- usedfor = ISnap
385-
386- def checkAuthenticated(self, user):
387- return (
388- user.isOwner(self.obj) or
389- user.in_commercial_admin or user.in_admin)
390-
391-
392-class AdminSnap(AuthorizationBase):
393- """Restrict changing build settings on snap packages.
394-
395- The security of the non-virtualised build farm depends on these
396- settings, so they can only be changed by "PPA"/commercial admins, or by
397- "PPA" self admins on snap packages that they can already edit.
398- """
399- permission = 'launchpad.Admin'
400- usedfor = ISnap
401-
402- def checkAuthenticated(self, user):
403- if user.in_ppa_admin or user.in_commercial_admin or user.in_admin:
404- return True
405- return (
406- user.in_ppa_self_admins
407- and EditSnap(self.obj).checkAuthenticated(user))
408-
409-
410-class SnapSubscriptionEdit(AuthorizationBase):
411- permission = 'launchpad.Edit'
412- usedfor = ISnapSubscription
413-
414- def checkAuthenticated(self, user):
415- """Is the user able to edit a Snap recipe subscription?
416-
417- Any team member can edit a Snap recipe subscription for their
418- team.
419- Launchpad Admins can also edit any Snap recipe subscription.
420- The owner of the subscribed Snap can edit the subscription. If
421- the Snap owner is a team, then members of the team can edit
422- the subscription.
423- """
424- return (user.inTeam(self.obj.snap.owner) or
425- user.inTeam(self.obj.person) or
426- user.inTeam(self.obj.subscribed_by) or
427- user.in_admin)
428-
429-
430-class SnapSubscriptionView(AuthorizationBase):
431- permission = 'launchpad.View'
432- usedfor = ISnapSubscription
433-
434- def checkUnauthenticated(self):
435- return self.obj.snap.visibleByUser(None)
436-
437- def checkAuthenticated(self, user):
438- return self.obj.snap.visibleByUser(user.person)
439-
440-
441-class ViewSnapBuildRequest(DelegatedAuthorization):
442- permission = 'launchpad.View'
443- usedfor = ISnapBuildRequest
444-
445- def __init__(self, obj):
446- super().__init__(obj, obj.snap, 'launchpad.View')
447-
448-
449-class ViewSnapBuild(DelegatedAuthorization):
450- permission = 'launchpad.View'
451- usedfor = ISnapBuild
452-
453- def iter_objects(self):
454- yield self.obj.snap
455- yield self.obj.archive
456-
457-
458-class EditSnapBuild(AdminByBuilddAdmin):
459- permission = 'launchpad.Edit'
460- usedfor = ISnapBuild
461-
462- def checkAuthenticated(self, user):
463- """Check edit access for snap package builds.
464-
465- Allow admins, buildd admins, and the owner of the snap package.
466- (Note that the requester of the build is required to be in the team
467- that owns the snap package.)
468- """
469- auth_snap = EditSnap(self.obj.snap)
470- if auth_snap.checkAuthenticated(user):
471- return True
472- return super().checkAuthenticated(user)
473-
474-
475-class AdminSnapBuild(AdminByBuilddAdmin):
476- usedfor = ISnapBuild
477-
478-
479-class ViewSnappySeries(AnonymousAuthorization):
480- """Anyone can view an `ISnappySeries`."""
481- usedfor = ISnappySeries
482-
483-
484-class EditSnappySeries(EditByRegistryExpertsOrAdmins):
485- usedfor = ISnappySeries
486-
487-
488-class EditSnappySeriesSet(EditByRegistryExpertsOrAdmins):
489- usedfor = ISnappySeriesSet
490-
491-
492-class ViewSnapBase(AnonymousAuthorization):
493- """Anyone can view an `ISnapBase`."""
494- usedfor = ISnapBase
495-
496-
497-class EditSnapBase(EditByRegistryExpertsOrAdmins):
498- usedfor = ISnapBase
499-
500-
501-class EditSnapBaseSet(EditByRegistryExpertsOrAdmins):
502- usedfor = ISnapBaseSet
503-
504-
505-class ViewOCIRecipeBuildRequest(DelegatedAuthorization):
506- permission = 'launchpad.View'
507- usedfor = IOCIRecipeBuildRequest
508-
509- def __init__(self, obj):
510- super().__init__(obj, obj.recipe, 'launchpad.View')
511-
512-
513-class ViewOCIRecipe(AnonymousAuthorization):
514- """Anyone can view public `IOCIRecipe`, but only subscribers can view
515- private ones.
516- """
517- usedfor = IOCIRecipe
518-
519- def checkUnauthenticated(self):
520- return self.obj.visibleByUser(None)
521-
522- def checkAuthenticated(self, user):
523- return self.obj.visibleByUser(user.person)
524-
525-
526-class EditOCIRecipe(AuthorizationBase):
527- permission = 'launchpad.Edit'
528- usedfor = IOCIRecipe
529-
530- def checkAuthenticated(self, user):
531- return (
532- user.isOwner(self.obj) or
533- user.in_commercial_admin or user.in_admin)
534-
535-
536-class AdminOCIRecipe(AuthorizationBase):
537- """Restrict changing build settings on OCI recipes.
538-
539- The security of the non-virtualised build farm depends on these
540- settings, so they can only be changed by "PPA"/commercial admins, or by
541- "PPA" self admins on OCI recipes that they can already edit.
542- """
543- permission = 'launchpad.Admin'
544- usedfor = IOCIRecipe
545-
546- def checkAuthenticated(self, user):
547- if user.in_ppa_admin or user.in_commercial_admin or user.in_admin:
548- return True
549- return (
550- user.in_ppa_self_admins
551- and EditSnap(self.obj).checkAuthenticated(user))
552-
553-
554-class OCIRecipeSubscriptionEdit(AuthorizationBase):
555- permission = 'launchpad.Edit'
556- usedfor = IOCIRecipeSubscription
557-
558- def checkAuthenticated(self, user):
559- """Is the user able to edit an OCI recipe subscription?
560-
561- Any team member can edit a OCI recipe subscription for their
562- team.
563- Launchpad Admins can also edit any OCI recipe subscription.
564- The owner of the subscribed OCI recipe can edit the subscription. If
565- the OCI recipe owner is a team, then members of the team can edit
566- the subscription.
567- """
568- return (user.inTeam(self.obj.recipe.owner) or
569- user.inTeam(self.obj.person) or
570- user.inTeam(self.obj.subscribed_by) or
571- user.in_admin)
572-
573-
574-class OCIRecipeSubscriptionView(AuthorizationBase):
575- permission = 'launchpad.View'
576- usedfor = IOCIRecipeSubscription
577-
578- def checkUnauthenticated(self):
579- return self.obj.recipe.visibleByUser(None)
580-
581- def checkAuthenticated(self, user):
582- return self.obj.recipe.visibleByUser(user.person)
583-
584-
585-class ViewOCIRecipeBuild(DelegatedAuthorization):
586- permission = 'launchpad.View'
587- usedfor = IOCIRecipeBuild
588-
589- def iter_objects(self):
590- yield self.obj.recipe
591-
592-
593-class EditOCIRecipeBuild(AdminByBuilddAdmin):
594- permission = 'launchpad.Edit'
595- usedfor = IOCIRecipeBuild
596-
597- def checkAuthenticated(self, user):
598- """Check edit access for OCI recipe builds.
599-
600- Allow admins, buildd admins, and the owner of the OCI recipe.
601- (Note that the requester of the build is required to be in the team
602- that owns the OCI recipe.)
603- """
604- auth_recipe = EditOCIRecipe(self.obj.recipe)
605- if auth_recipe.checkAuthenticated(user):
606- return True
607- return super().checkAuthenticated(user)
608-
609-
610-class AdminOCIRecipeBuild(AdminByBuilddAdmin):
611- usedfor = IOCIRecipeBuild
612-
613-
614-class ViewOCIRegistryCredentials(AuthorizationBase):
615- permission = 'launchpad.View'
616- usedfor = IOCIRegistryCredentials
617-
618- def checkAuthenticated(self, user):
619- # This must be kept in sync with user_can_edit_credentials_for_owner
620- # in lp.oci.interfaces.ociregistrycredentials.
621- return (
622- user.isOwner(self.obj) or
623- user.in_admin)
624-
625-
626-class ViewOCIPushRule(AnonymousAuthorization):
627- """Anyone can view an `IOCIPushRule`."""
628- usedfor = IOCIPushRule
629-
630-
631-class OCIPushRuleEdit(AuthorizationBase):
632- permission = 'launchpad.Edit'
633- usedfor = IOCIPushRule
634-
635- def checkAuthenticated(self, user):
636- return (
637- user.isOwner(self.obj.recipe) or
638- user.in_commercial_admin or user.in_admin)
639-
640-
641-class ViewCharmRecipe(AuthorizationBase):
642- """Private charm recipes are only visible to their owners and admins."""
643- permission = 'launchpad.View'
644- usedfor = ICharmRecipe
645-
646- def checkAuthenticated(self, user):
647- return self.obj.visibleByUser(user.person)
648-
649- def checkUnauthenticated(self):
650- return self.obj.visibleByUser(None)
651-
652-
653-class EditCharmRecipe(AuthorizationBase):
654- permission = 'launchpad.Edit'
655- usedfor = ICharmRecipe
656-
657- def checkAuthenticated(self, user):
658- return (
659- user.isOwner(self.obj) or
660- user.in_commercial_admin or user.in_admin)
661-
662-
663-class AdminCharmRecipe(AuthorizationBase):
664- """Restrict changing build settings on charm recipes.
665-
666- The security of the non-virtualised build farm depends on these
667- settings, so they can only be changed by "PPA"/commercial admins, or by
668- "PPA" self admins on charm recipes that they can already edit.
669- """
670- permission = 'launchpad.Admin'
671- usedfor = ICharmRecipe
672-
673- def checkAuthenticated(self, user):
674- if user.in_ppa_admin or user.in_commercial_admin or user.in_admin:
675- return True
676- return (
677- user.in_ppa_self_admins
678- and EditCharmRecipe(self.obj).checkAuthenticated(user))
679-
680-
681-class ViewCharmRecipeBuildRequest(DelegatedAuthorization):
682- permission = 'launchpad.View'
683- usedfor = ICharmRecipeBuildRequest
684-
685- def __init__(self, obj):
686- super().__init__(obj, obj.recipe, 'launchpad.View')
687-
688-
689-class ViewCharmRecipeBuild(DelegatedAuthorization):
690- permission = 'launchpad.View'
691- usedfor = ICharmRecipeBuild
692-
693- def iter_objects(self):
694- yield self.obj.recipe
695-
696-
697-class EditCharmRecipeBuild(AdminByBuilddAdmin):
698- permission = 'launchpad.Edit'
699- usedfor = ICharmRecipeBuild
700-
701- def checkAuthenticated(self, user):
702- """Check edit access for snap package builds.
703-
704- Allow admins, buildd admins, and the owner of the charm recipe.
705- (Note that the requester of the build is required to be in the team
706- that owns the charm recipe.)
707- """
708- auth_recipe = EditCharmRecipe(self.obj.recipe)
709- if auth_recipe.checkAuthenticated(user):
710- return True
711- return super().checkAuthenticated(user)
712-
713-
714-class AdminCharmRecipeBuild(AdminByBuilddAdmin):
715- usedfor = ICharmRecipeBuild
716-
717-
718-class ViewCharmBase(AnonymousAuthorization):
719- """Anyone can view an `ICharmBase`."""
720- usedfor = ICharmBase
721-
722-
723-class EditCharmBase(EditByRegistryExpertsOrAdmins):
724- usedfor = ICharmBase
725-
726-
727-class EditCharmBaseSet(EditByRegistryExpertsOrAdmins):
728- usedfor = ICharmBaseSet
729diff --git a/lib/lp/snappy/configure.zcml b/lib/lp/snappy/configure.zcml
730index 3dcc7cc..5f8081a 100644
731--- a/lib/lp/snappy/configure.zcml
732+++ b/lib/lp/snappy/configure.zcml
733@@ -11,6 +11,7 @@
734 xmlns:xmlrpc="http://namespaces.zope.org/xmlrpc"
735 i18n_domain="launchpad">
736
737+ <authorizations module=".security" />
738 <include package=".browser" />
739 <include file="vocabularies.zcml" />
740
741diff --git a/lib/lp/snappy/security.py b/lib/lp/snappy/security.py
742new file mode 100644
743index 0000000..1c24258
744--- /dev/null
745+++ b/lib/lp/snappy/security.py
746@@ -0,0 +1,165 @@
747+# Copyright 2009-2022 Canonical Ltd. This software is licensed under the
748+# GNU Affero General Public License version 3 (see the file LICENSE).
749+
750+"""Security adapters for the snappy package."""
751+
752+__all__ = []
753+
754+from lp.app.security import (
755+ AnonymousAuthorization,
756+ AuthorizationBase,
757+ DelegatedAuthorization,
758+ )
759+from lp.security import (
760+ AdminByBuilddAdmin,
761+ EditByRegistryExpertsOrAdmins,
762+ )
763+from lp.snappy.interfaces.snap import (
764+ ISnap,
765+ ISnapBuildRequest,
766+ )
767+from lp.snappy.interfaces.snapbase import (
768+ ISnapBase,
769+ ISnapBaseSet,
770+ )
771+from lp.snappy.interfaces.snapbuild import ISnapBuild
772+from lp.snappy.interfaces.snappyseries import (
773+ ISnappySeries,
774+ ISnappySeriesSet,
775+ )
776+from lp.snappy.interfaces.snapsubscription import ISnapSubscription
777+
778+
779+class ViewSnap(AuthorizationBase):
780+ """Private snaps are only visible to their owners and admins."""
781+ permission = 'launchpad.View'
782+ usedfor = ISnap
783+
784+ def checkAuthenticated(self, user):
785+ return self.obj.visibleByUser(user.person)
786+
787+ def checkUnauthenticated(self):
788+ return self.obj.visibleByUser(None)
789+
790+
791+class EditSnap(AuthorizationBase):
792+ permission = 'launchpad.Edit'
793+ usedfor = ISnap
794+
795+ def checkAuthenticated(self, user):
796+ return (
797+ user.isOwner(self.obj) or
798+ user.in_commercial_admin or user.in_admin)
799+
800+
801+class AdminSnap(AuthorizationBase):
802+ """Restrict changing build settings on snap packages.
803+
804+ The security of the non-virtualised build farm depends on these
805+ settings, so they can only be changed by "PPA"/commercial admins, or by
806+ "PPA" self admins on snap packages that they can already edit.
807+ """
808+ permission = 'launchpad.Admin'
809+ usedfor = ISnap
810+
811+ def checkAuthenticated(self, user):
812+ if user.in_ppa_admin or user.in_commercial_admin or user.in_admin:
813+ return True
814+ return (
815+ user.in_ppa_self_admins
816+ and EditSnap(self.obj).checkAuthenticated(user))
817+
818+
819+class SnapSubscriptionEdit(AuthorizationBase):
820+ permission = 'launchpad.Edit'
821+ usedfor = ISnapSubscription
822+
823+ def checkAuthenticated(self, user):
824+ """Is the user able to edit a Snap recipe subscription?
825+
826+ Any team member can edit a Snap recipe subscription for their
827+ team.
828+ Launchpad Admins can also edit any Snap recipe subscription.
829+ The owner of the subscribed Snap can edit the subscription. If
830+ the Snap owner is a team, then members of the team can edit
831+ the subscription.
832+ """
833+ return (user.inTeam(self.obj.snap.owner) or
834+ user.inTeam(self.obj.person) or
835+ user.inTeam(self.obj.subscribed_by) or
836+ user.in_admin)
837+
838+
839+class SnapSubscriptionView(AuthorizationBase):
840+ permission = 'launchpad.View'
841+ usedfor = ISnapSubscription
842+
843+ def checkUnauthenticated(self):
844+ return self.obj.snap.visibleByUser(None)
845+
846+ def checkAuthenticated(self, user):
847+ return self.obj.snap.visibleByUser(user.person)
848+
849+
850+class ViewSnapBuildRequest(DelegatedAuthorization):
851+ permission = 'launchpad.View'
852+ usedfor = ISnapBuildRequest
853+
854+ def __init__(self, obj):
855+ super().__init__(obj, obj.snap, 'launchpad.View')
856+
857+
858+class ViewSnapBuild(DelegatedAuthorization):
859+ permission = 'launchpad.View'
860+ usedfor = ISnapBuild
861+
862+ def iter_objects(self):
863+ yield self.obj.snap
864+ yield self.obj.archive
865+
866+
867+class EditSnapBuild(AdminByBuilddAdmin):
868+ permission = 'launchpad.Edit'
869+ usedfor = ISnapBuild
870+
871+ def checkAuthenticated(self, user):
872+ """Check edit access for snap package builds.
873+
874+ Allow admins, buildd admins, and the owner of the snap package.
875+ (Note that the requester of the build is required to be in the team
876+ that owns the snap package.)
877+ """
878+ auth_snap = EditSnap(self.obj.snap)
879+ if auth_snap.checkAuthenticated(user):
880+ return True
881+ return super().checkAuthenticated(user)
882+
883+
884+class AdminSnapBuild(AdminByBuilddAdmin):
885+ usedfor = ISnapBuild
886+
887+
888+class ViewSnappySeries(AnonymousAuthorization):
889+ """Anyone can view an `ISnappySeries`."""
890+ usedfor = ISnappySeries
891+
892+
893+class EditSnappySeries(EditByRegistryExpertsOrAdmins):
894+ usedfor = ISnappySeries
895+
896+
897+class EditSnappySeriesSet(EditByRegistryExpertsOrAdmins):
898+ usedfor = ISnappySeriesSet
899+
900+
901+class ViewSnapBase(AnonymousAuthorization):
902+ """Anyone can view an `ISnapBase`."""
903+ usedfor = ISnapBase
904+
905+
906+class EditSnapBase(EditByRegistryExpertsOrAdmins):
907+ usedfor = ISnapBase
908+
909+
910+class EditSnapBaseSet(EditByRegistryExpertsOrAdmins):
911+ usedfor = ISnapBaseSet

Subscribers

People subscribed via source and target branches

to status/vote changes: