← Back to team overview

launchpad-reviewers team mailing list archive

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

 

Guruprasad has proposed merging ~lgp171188/launchpad:split-security.py-charms-oci-snappy into launchpad:master.

Commit message:
Split and move the security adapters for charms, oci, security packages

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~lgp171188/launchpad/+git/launchpad/+merge/426643
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~lgp171188/launchpad:split-security.py-charms-oci-snappy into launchpad:master.
diff --git a/lib/lp/charms/configure.zcml b/lib/lp/charms/configure.zcml
index 330a72d..74d490d 100644
--- a/lib/lp/charms/configure.zcml
+++ b/lib/lp/charms/configure.zcml
@@ -11,6 +11,7 @@
     xmlns:xmlrpc="http://namespaces.zope.org/xmlrpc";
     i18n_domain="launchpad">
 
+    <authorizations module=".security" />
     <include package=".browser" />
 
     <lp:help-folder folder="help" name="+help-charms" />
diff --git a/lib/lp/charms/security.py b/lib/lp/charms/security.py
new file mode 100644
index 0000000..9d8dad2
--- /dev/null
+++ b/lib/lp/charms/security.py
@@ -0,0 +1,112 @@
+# Copyright 2009-2022 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Security adapters for the charms package."""
+
+__all__ = []
+
+from lp.app.security import (
+    AnonymousAuthorization,
+    AuthorizationBase,
+    DelegatedAuthorization,
+)
+from lp.charms.interfaces.charmbase import ICharmBase, ICharmBaseSet
+from lp.charms.interfaces.charmrecipe import (
+    ICharmRecipe,
+    ICharmRecipeBuildRequest,
+)
+from lp.charms.interfaces.charmrecipebuild import ICharmRecipeBuild
+from lp.security import AdminByBuilddAdmin, EditByRegistryExpertsOrAdmins
+
+
+class ViewCharmRecipe(AuthorizationBase):
+    """Private charm recipes are only visible to their owners and admins."""
+
+    permission = "launchpad.View"
+    usedfor = ICharmRecipe
+
+    def checkAuthenticated(self, user):
+        return self.obj.visibleByUser(user.person)
+
+    def checkUnauthenticated(self):
+        return self.obj.visibleByUser(None)
+
+
+class EditCharmRecipe(AuthorizationBase):
+    permission = "launchpad.Edit"
+    usedfor = ICharmRecipe
+
+    def checkAuthenticated(self, user):
+        return (
+            user.isOwner(self.obj) or user.in_commercial_admin or user.in_admin
+        )
+
+
+class AdminCharmRecipe(AuthorizationBase):
+    """Restrict changing build settings on charm recipes.
+
+    The security of the non-virtualised build farm depends on these
+    settings, so they can only be changed by "PPA"/commercial admins, or by
+    "PPA" self admins on charm recipes that they can already edit.
+    """
+
+    permission = "launchpad.Admin"
+    usedfor = ICharmRecipe
+
+    def checkAuthenticated(self, user):
+        if user.in_ppa_admin or user.in_commercial_admin or user.in_admin:
+            return True
+        return user.in_ppa_self_admins and EditCharmRecipe(
+            self.obj
+        ).checkAuthenticated(user)
+
+
+class ViewCharmRecipeBuildRequest(DelegatedAuthorization):
+    permission = "launchpad.View"
+    usedfor = ICharmRecipeBuildRequest
+
+    def __init__(self, obj):
+        super().__init__(obj, obj.recipe, "launchpad.View")
+
+
+class ViewCharmRecipeBuild(DelegatedAuthorization):
+    permission = "launchpad.View"
+    usedfor = ICharmRecipeBuild
+
+    def iter_objects(self):
+        yield self.obj.recipe
+
+
+class EditCharmRecipeBuild(AdminByBuilddAdmin):
+    permission = "launchpad.Edit"
+    usedfor = ICharmRecipeBuild
+
+    def checkAuthenticated(self, user):
+        """Check edit access for snap package builds.
+
+        Allow admins, buildd admins, and the owner of the charm recipe.
+        (Note that the requester of the build is required to be in the team
+        that owns the charm recipe.)
+        """
+        auth_recipe = EditCharmRecipe(self.obj.recipe)
+        if auth_recipe.checkAuthenticated(user):
+            return True
+        return super().checkAuthenticated(user)
+
+
+class AdminCharmRecipeBuild(AdminByBuilddAdmin):
+    usedfor = ICharmRecipeBuild
+
+
+class ViewCharmBase(AnonymousAuthorization):
+    """Anyone can view an `ICharmBase`."""
+
+    usedfor = ICharmBase
+
+
+class EditCharmBase(EditByRegistryExpertsOrAdmins):
+    usedfor = ICharmBase
+
+
+class EditCharmBaseSet(EditByRegistryExpertsOrAdmins):
+    usedfor = ICharmBaseSet
diff --git a/lib/lp/oci/configure.zcml b/lib/lp/oci/configure.zcml
index 82cf7ff..098a26d 100644
--- a/lib/lp/oci/configure.zcml
+++ b/lib/lp/oci/configure.zcml
@@ -8,6 +8,7 @@
     xmlns:webservice="http://namespaces.canonical.com/webservice";
     i18n_domain="launchpad">
 
+    <authorizations module=".security" />
     <include package=".browser" />
     <include file="vocabularies.zcml" />
 
diff --git a/lib/lp/oci/security.py b/lib/lp/oci/security.py
new file mode 100644
index 0000000..fd3d429
--- /dev/null
+++ b/lib/lp/oci/security.py
@@ -0,0 +1,158 @@
+# Copyright 2009-2022 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Security adapters for the oci package."""
+
+__all__ = []
+
+from lp.app.security import (
+    AnonymousAuthorization,
+    AuthorizationBase,
+    DelegatedAuthorization,
+    )
+from lp.oci.interfaces.ocipushrule import IOCIPushRule
+from lp.oci.interfaces.ocirecipe import (
+    IOCIRecipe,
+    IOCIRecipeBuildRequest,
+    )
+from lp.oci.interfaces.ocirecipebuild import IOCIRecipeBuild
+from lp.oci.interfaces.ocirecipesubscription import IOCIRecipeSubscription
+from lp.oci.interfaces.ociregistrycredentials import IOCIRegistryCredentials
+from lp.security import AdminByBuilddAdmin
+from lp.snappy.security import EditSnap
+
+
+class ViewOCIRecipeBuildRequest(DelegatedAuthorization):
+    permission = 'launchpad.View'
+    usedfor = IOCIRecipeBuildRequest
+
+    def __init__(self, obj):
+        super().__init__(obj, obj.recipe, 'launchpad.View')
+
+
+class ViewOCIRecipe(AnonymousAuthorization):
+    """Anyone can view public `IOCIRecipe`, but only subscribers can view
+    private ones.
+    """
+    usedfor = IOCIRecipe
+
+    def checkUnauthenticated(self):
+        return self.obj.visibleByUser(None)
+
+    def checkAuthenticated(self, user):
+        return self.obj.visibleByUser(user.person)
+
+
+class EditOCIRecipe(AuthorizationBase):
+    permission = 'launchpad.Edit'
+    usedfor = IOCIRecipe
+
+    def checkAuthenticated(self, user):
+        return (
+            user.isOwner(self.obj) or
+            user.in_commercial_admin or user.in_admin)
+
+
+class AdminOCIRecipe(AuthorizationBase):
+    """Restrict changing build settings on OCI recipes.
+
+    The security of the non-virtualised build farm depends on these
+    settings, so they can only be changed by "PPA"/commercial admins, or by
+    "PPA" self admins on OCI recipes that they can already edit.
+    """
+    permission = 'launchpad.Admin'
+    usedfor = IOCIRecipe
+
+    def checkAuthenticated(self, user):
+        if user.in_ppa_admin or user.in_commercial_admin or user.in_admin:
+            return True
+        return (
+            user.in_ppa_self_admins
+            and EditSnap(self.obj).checkAuthenticated(user))
+
+
+class OCIRecipeSubscriptionEdit(AuthorizationBase):
+    permission = 'launchpad.Edit'
+    usedfor = IOCIRecipeSubscription
+
+    def checkAuthenticated(self, user):
+        """Is the user able to edit an OCI recipe subscription?
+
+        Any team member can edit a OCI recipe subscription for their
+        team.
+        Launchpad Admins can also edit any OCI recipe subscription.
+        The owner of the subscribed OCI recipe can edit the subscription. If
+        the OCI recipe owner is a team, then members of the team can edit
+        the subscription.
+        """
+        return (user.inTeam(self.obj.recipe.owner) or
+                user.inTeam(self.obj.person) or
+                user.inTeam(self.obj.subscribed_by) or
+                user.in_admin)
+
+
+class OCIRecipeSubscriptionView(AuthorizationBase):
+    permission = 'launchpad.View'
+    usedfor = IOCIRecipeSubscription
+
+    def checkUnauthenticated(self):
+        return self.obj.recipe.visibleByUser(None)
+
+    def checkAuthenticated(self, user):
+        return self.obj.recipe.visibleByUser(user.person)
+
+
+class ViewOCIRecipeBuild(DelegatedAuthorization):
+    permission = 'launchpad.View'
+    usedfor = IOCIRecipeBuild
+
+    def iter_objects(self):
+        yield self.obj.recipe
+
+
+class EditOCIRecipeBuild(AdminByBuilddAdmin):
+    permission = 'launchpad.Edit'
+    usedfor = IOCIRecipeBuild
+
+    def checkAuthenticated(self, user):
+        """Check edit access for OCI recipe builds.
+
+        Allow admins, buildd admins, and the owner of the OCI recipe.
+        (Note that the requester of the build is required to be in the team
+        that owns the OCI recipe.)
+        """
+        auth_recipe = EditOCIRecipe(self.obj.recipe)
+        if auth_recipe.checkAuthenticated(user):
+            return True
+        return super().checkAuthenticated(user)
+
+
+class AdminOCIRecipeBuild(AdminByBuilddAdmin):
+    usedfor = IOCIRecipeBuild
+
+
+class ViewOCIRegistryCredentials(AuthorizationBase):
+    permission = 'launchpad.View'
+    usedfor = IOCIRegistryCredentials
+
+    def checkAuthenticated(self, user):
+        # This must be kept in sync with user_can_edit_credentials_for_owner
+        # in lp.oci.interfaces.ociregistrycredentials.
+        return (
+            user.isOwner(self.obj) or
+            user.in_admin)
+
+
+class ViewOCIPushRule(AnonymousAuthorization):
+    """Anyone can view an `IOCIPushRule`."""
+    usedfor = IOCIPushRule
+
+
+class OCIPushRuleEdit(AuthorizationBase):
+    permission = 'launchpad.Edit'
+    usedfor = IOCIPushRule
+
+    def checkAuthenticated(self, user):
+        return (
+            user.isOwner(self.obj.recipe) or
+            user.in_commercial_admin or user.in_admin)
diff --git a/lib/lp/security.py b/lib/lp/security.py
index a87f341..71c71e8 100644
--- a/lib/lp/security.py
+++ b/lib/lp/security.py
@@ -26,11 +26,7 @@ from datetime import (
 import pytz
 from zope.interface import Interface
 
-from lp.app.security import (
-    AnonymousAuthorization,
-    AuthorizationBase,
-    DelegatedAuthorization,
-    )
+from lp.app.security import AuthorizationBase
 from lp.archivepublisher.interfaces.publisherconfig import IPublisherConfig
 from lp.bugs.interfaces.bugtarget import IOfficialBugTagTargetRestricted
 from lp.bugs.interfaces.structuralsubscription import IStructuralSubscription
@@ -40,40 +36,9 @@ from lp.buildmaster.interfaces.builder import (
     )
 from lp.buildmaster.interfaces.buildfarmjob import IBuildFarmJob
 from lp.buildmaster.interfaces.packagebuild import IPackageBuild
-from lp.charms.interfaces.charmbase import (
-    ICharmBase,
-    ICharmBaseSet,
-    )
-from lp.charms.interfaces.charmrecipe import (
-    ICharmRecipe,
-    ICharmRecipeBuildRequest,
-    )
-from lp.charms.interfaces.charmrecipebuild import ICharmRecipeBuild
-from lp.oci.interfaces.ocipushrule import IOCIPushRule
-from lp.oci.interfaces.ocirecipe import (
-    IOCIRecipe,
-    IOCIRecipeBuildRequest,
-    )
-from lp.oci.interfaces.ocirecipebuild import IOCIRecipeBuild
-from lp.oci.interfaces.ocirecipesubscription import IOCIRecipeSubscription
-from lp.oci.interfaces.ociregistrycredentials import IOCIRegistryCredentials
 from lp.registry.interfaces.role import IHasOwner
 from lp.services.config import config
 from lp.services.webapp.interfaces import ILaunchpadRoot
-from lp.snappy.interfaces.snap import (
-    ISnap,
-    ISnapBuildRequest,
-    )
-from lp.snappy.interfaces.snapbase import (
-    ISnapBase,
-    ISnapBaseSet,
-    )
-from lp.snappy.interfaces.snapbuild import ISnapBuild
-from lp.snappy.interfaces.snappyseries import (
-    ISnappySeries,
-    ISnappySeriesSet,
-    )
-from lp.snappy.interfaces.snapsubscription import ISnapSubscription
 
 
 def is_commercial_case(obj, user):
@@ -312,364 +277,3 @@ class EditPackageBuild(EditBuildFarmJob):
 
 class ViewPublisherConfig(AdminByAdminsTeam):
     usedfor = IPublisherConfig
-
-
-class ViewSnap(AuthorizationBase):
-    """Private snaps are only visible to their owners and admins."""
-    permission = 'launchpad.View'
-    usedfor = ISnap
-
-    def checkAuthenticated(self, user):
-        return self.obj.visibleByUser(user.person)
-
-    def checkUnauthenticated(self):
-        return self.obj.visibleByUser(None)
-
-
-class EditSnap(AuthorizationBase):
-    permission = 'launchpad.Edit'
-    usedfor = ISnap
-
-    def checkAuthenticated(self, user):
-        return (
-            user.isOwner(self.obj) or
-            user.in_commercial_admin or user.in_admin)
-
-
-class AdminSnap(AuthorizationBase):
-    """Restrict changing build settings on snap packages.
-
-    The security of the non-virtualised build farm depends on these
-    settings, so they can only be changed by "PPA"/commercial admins, or by
-    "PPA" self admins on snap packages that they can already edit.
-    """
-    permission = 'launchpad.Admin'
-    usedfor = ISnap
-
-    def checkAuthenticated(self, user):
-        if user.in_ppa_admin or user.in_commercial_admin or user.in_admin:
-            return True
-        return (
-            user.in_ppa_self_admins
-            and EditSnap(self.obj).checkAuthenticated(user))
-
-
-class SnapSubscriptionEdit(AuthorizationBase):
-    permission = 'launchpad.Edit'
-    usedfor = ISnapSubscription
-
-    def checkAuthenticated(self, user):
-        """Is the user able to edit a Snap recipe subscription?
-
-        Any team member can edit a Snap recipe subscription for their
-        team.
-        Launchpad Admins can also edit any Snap recipe subscription.
-        The owner of the subscribed Snap can edit the subscription. If
-        the Snap owner is a team, then members of the team can edit
-        the subscription.
-        """
-        return (user.inTeam(self.obj.snap.owner) or
-                user.inTeam(self.obj.person) or
-                user.inTeam(self.obj.subscribed_by) or
-                user.in_admin)
-
-
-class SnapSubscriptionView(AuthorizationBase):
-    permission = 'launchpad.View'
-    usedfor = ISnapSubscription
-
-    def checkUnauthenticated(self):
-        return self.obj.snap.visibleByUser(None)
-
-    def checkAuthenticated(self, user):
-        return self.obj.snap.visibleByUser(user.person)
-
-
-class ViewSnapBuildRequest(DelegatedAuthorization):
-    permission = 'launchpad.View'
-    usedfor = ISnapBuildRequest
-
-    def __init__(self, obj):
-        super().__init__(obj, obj.snap, 'launchpad.View')
-
-
-class ViewSnapBuild(DelegatedAuthorization):
-    permission = 'launchpad.View'
-    usedfor = ISnapBuild
-
-    def iter_objects(self):
-        yield self.obj.snap
-        yield self.obj.archive
-
-
-class EditSnapBuild(AdminByBuilddAdmin):
-    permission = 'launchpad.Edit'
-    usedfor = ISnapBuild
-
-    def checkAuthenticated(self, user):
-        """Check edit access for snap package builds.
-
-        Allow admins, buildd admins, and the owner of the snap package.
-        (Note that the requester of the build is required to be in the team
-        that owns the snap package.)
-        """
-        auth_snap = EditSnap(self.obj.snap)
-        if auth_snap.checkAuthenticated(user):
-            return True
-        return super().checkAuthenticated(user)
-
-
-class AdminSnapBuild(AdminByBuilddAdmin):
-    usedfor = ISnapBuild
-
-
-class ViewSnappySeries(AnonymousAuthorization):
-    """Anyone can view an `ISnappySeries`."""
-    usedfor = ISnappySeries
-
-
-class EditSnappySeries(EditByRegistryExpertsOrAdmins):
-    usedfor = ISnappySeries
-
-
-class EditSnappySeriesSet(EditByRegistryExpertsOrAdmins):
-    usedfor = ISnappySeriesSet
-
-
-class ViewSnapBase(AnonymousAuthorization):
-    """Anyone can view an `ISnapBase`."""
-    usedfor = ISnapBase
-
-
-class EditSnapBase(EditByRegistryExpertsOrAdmins):
-    usedfor = ISnapBase
-
-
-class EditSnapBaseSet(EditByRegistryExpertsOrAdmins):
-    usedfor = ISnapBaseSet
-
-
-class ViewOCIRecipeBuildRequest(DelegatedAuthorization):
-    permission = 'launchpad.View'
-    usedfor = IOCIRecipeBuildRequest
-
-    def __init__(self, obj):
-        super().__init__(obj, obj.recipe, 'launchpad.View')
-
-
-class ViewOCIRecipe(AnonymousAuthorization):
-    """Anyone can view public `IOCIRecipe`, but only subscribers can view
-    private ones.
-    """
-    usedfor = IOCIRecipe
-
-    def checkUnauthenticated(self):
-        return self.obj.visibleByUser(None)
-
-    def checkAuthenticated(self, user):
-        return self.obj.visibleByUser(user.person)
-
-
-class EditOCIRecipe(AuthorizationBase):
-    permission = 'launchpad.Edit'
-    usedfor = IOCIRecipe
-
-    def checkAuthenticated(self, user):
-        return (
-            user.isOwner(self.obj) or
-            user.in_commercial_admin or user.in_admin)
-
-
-class AdminOCIRecipe(AuthorizationBase):
-    """Restrict changing build settings on OCI recipes.
-
-    The security of the non-virtualised build farm depends on these
-    settings, so they can only be changed by "PPA"/commercial admins, or by
-    "PPA" self admins on OCI recipes that they can already edit.
-    """
-    permission = 'launchpad.Admin'
-    usedfor = IOCIRecipe
-
-    def checkAuthenticated(self, user):
-        if user.in_ppa_admin or user.in_commercial_admin or user.in_admin:
-            return True
-        return (
-            user.in_ppa_self_admins
-            and EditSnap(self.obj).checkAuthenticated(user))
-
-
-class OCIRecipeSubscriptionEdit(AuthorizationBase):
-    permission = 'launchpad.Edit'
-    usedfor = IOCIRecipeSubscription
-
-    def checkAuthenticated(self, user):
-        """Is the user able to edit an OCI recipe subscription?
-
-        Any team member can edit a OCI recipe subscription for their
-        team.
-        Launchpad Admins can also edit any OCI recipe subscription.
-        The owner of the subscribed OCI recipe can edit the subscription. If
-        the OCI recipe owner is a team, then members of the team can edit
-        the subscription.
-        """
-        return (user.inTeam(self.obj.recipe.owner) or
-                user.inTeam(self.obj.person) or
-                user.inTeam(self.obj.subscribed_by) or
-                user.in_admin)
-
-
-class OCIRecipeSubscriptionView(AuthorizationBase):
-    permission = 'launchpad.View'
-    usedfor = IOCIRecipeSubscription
-
-    def checkUnauthenticated(self):
-        return self.obj.recipe.visibleByUser(None)
-
-    def checkAuthenticated(self, user):
-        return self.obj.recipe.visibleByUser(user.person)
-
-
-class ViewOCIRecipeBuild(DelegatedAuthorization):
-    permission = 'launchpad.View'
-    usedfor = IOCIRecipeBuild
-
-    def iter_objects(self):
-        yield self.obj.recipe
-
-
-class EditOCIRecipeBuild(AdminByBuilddAdmin):
-    permission = 'launchpad.Edit'
-    usedfor = IOCIRecipeBuild
-
-    def checkAuthenticated(self, user):
-        """Check edit access for OCI recipe builds.
-
-        Allow admins, buildd admins, and the owner of the OCI recipe.
-        (Note that the requester of the build is required to be in the team
-        that owns the OCI recipe.)
-        """
-        auth_recipe = EditOCIRecipe(self.obj.recipe)
-        if auth_recipe.checkAuthenticated(user):
-            return True
-        return super().checkAuthenticated(user)
-
-
-class AdminOCIRecipeBuild(AdminByBuilddAdmin):
-    usedfor = IOCIRecipeBuild
-
-
-class ViewOCIRegistryCredentials(AuthorizationBase):
-    permission = 'launchpad.View'
-    usedfor = IOCIRegistryCredentials
-
-    def checkAuthenticated(self, user):
-        # This must be kept in sync with user_can_edit_credentials_for_owner
-        # in lp.oci.interfaces.ociregistrycredentials.
-        return (
-            user.isOwner(self.obj) or
-            user.in_admin)
-
-
-class ViewOCIPushRule(AnonymousAuthorization):
-    """Anyone can view an `IOCIPushRule`."""
-    usedfor = IOCIPushRule
-
-
-class OCIPushRuleEdit(AuthorizationBase):
-    permission = 'launchpad.Edit'
-    usedfor = IOCIPushRule
-
-    def checkAuthenticated(self, user):
-        return (
-            user.isOwner(self.obj.recipe) or
-            user.in_commercial_admin or user.in_admin)
-
-
-class ViewCharmRecipe(AuthorizationBase):
-    """Private charm recipes are only visible to their owners and admins."""
-    permission = 'launchpad.View'
-    usedfor = ICharmRecipe
-
-    def checkAuthenticated(self, user):
-        return self.obj.visibleByUser(user.person)
-
-    def checkUnauthenticated(self):
-        return self.obj.visibleByUser(None)
-
-
-class EditCharmRecipe(AuthorizationBase):
-    permission = 'launchpad.Edit'
-    usedfor = ICharmRecipe
-
-    def checkAuthenticated(self, user):
-        return (
-            user.isOwner(self.obj) or
-            user.in_commercial_admin or user.in_admin)
-
-
-class AdminCharmRecipe(AuthorizationBase):
-    """Restrict changing build settings on charm recipes.
-
-    The security of the non-virtualised build farm depends on these
-    settings, so they can only be changed by "PPA"/commercial admins, or by
-    "PPA" self admins on charm recipes that they can already edit.
-    """
-    permission = 'launchpad.Admin'
-    usedfor = ICharmRecipe
-
-    def checkAuthenticated(self, user):
-        if user.in_ppa_admin or user.in_commercial_admin or user.in_admin:
-            return True
-        return (
-            user.in_ppa_self_admins
-            and EditCharmRecipe(self.obj).checkAuthenticated(user))
-
-
-class ViewCharmRecipeBuildRequest(DelegatedAuthorization):
-    permission = 'launchpad.View'
-    usedfor = ICharmRecipeBuildRequest
-
-    def __init__(self, obj):
-        super().__init__(obj, obj.recipe, 'launchpad.View')
-
-
-class ViewCharmRecipeBuild(DelegatedAuthorization):
-    permission = 'launchpad.View'
-    usedfor = ICharmRecipeBuild
-
-    def iter_objects(self):
-        yield self.obj.recipe
-
-
-class EditCharmRecipeBuild(AdminByBuilddAdmin):
-    permission = 'launchpad.Edit'
-    usedfor = ICharmRecipeBuild
-
-    def checkAuthenticated(self, user):
-        """Check edit access for snap package builds.
-
-        Allow admins, buildd admins, and the owner of the charm recipe.
-        (Note that the requester of the build is required to be in the team
-        that owns the charm recipe.)
-        """
-        auth_recipe = EditCharmRecipe(self.obj.recipe)
-        if auth_recipe.checkAuthenticated(user):
-            return True
-        return super().checkAuthenticated(user)
-
-
-class AdminCharmRecipeBuild(AdminByBuilddAdmin):
-    usedfor = ICharmRecipeBuild
-
-
-class ViewCharmBase(AnonymousAuthorization):
-    """Anyone can view an `ICharmBase`."""
-    usedfor = ICharmBase
-
-
-class EditCharmBase(EditByRegistryExpertsOrAdmins):
-    usedfor = ICharmBase
-
-
-class EditCharmBaseSet(EditByRegistryExpertsOrAdmins):
-    usedfor = ICharmBaseSet
diff --git a/lib/lp/snappy/configure.zcml b/lib/lp/snappy/configure.zcml
index 3dcc7cc..5f8081a 100644
--- a/lib/lp/snappy/configure.zcml
+++ b/lib/lp/snappy/configure.zcml
@@ -11,6 +11,7 @@
     xmlns:xmlrpc="http://namespaces.zope.org/xmlrpc";
     i18n_domain="launchpad">
 
+    <authorizations module=".security" />
     <include package=".browser" />
     <include file="vocabularies.zcml" />
 
diff --git a/lib/lp/snappy/security.py b/lib/lp/snappy/security.py
new file mode 100644
index 0000000..1c24258
--- /dev/null
+++ b/lib/lp/snappy/security.py
@@ -0,0 +1,165 @@
+# Copyright 2009-2022 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Security adapters for the snappy package."""
+
+__all__ = []
+
+from lp.app.security import (
+    AnonymousAuthorization,
+    AuthorizationBase,
+    DelegatedAuthorization,
+    )
+from lp.security import (
+    AdminByBuilddAdmin,
+    EditByRegistryExpertsOrAdmins,
+    )
+from lp.snappy.interfaces.snap import (
+    ISnap,
+    ISnapBuildRequest,
+    )
+from lp.snappy.interfaces.snapbase import (
+    ISnapBase,
+    ISnapBaseSet,
+    )
+from lp.snappy.interfaces.snapbuild import ISnapBuild
+from lp.snappy.interfaces.snappyseries import (
+    ISnappySeries,
+    ISnappySeriesSet,
+    )
+from lp.snappy.interfaces.snapsubscription import ISnapSubscription
+
+
+class ViewSnap(AuthorizationBase):
+    """Private snaps are only visible to their owners and admins."""
+    permission = 'launchpad.View'
+    usedfor = ISnap
+
+    def checkAuthenticated(self, user):
+        return self.obj.visibleByUser(user.person)
+
+    def checkUnauthenticated(self):
+        return self.obj.visibleByUser(None)
+
+
+class EditSnap(AuthorizationBase):
+    permission = 'launchpad.Edit'
+    usedfor = ISnap
+
+    def checkAuthenticated(self, user):
+        return (
+            user.isOwner(self.obj) or
+            user.in_commercial_admin or user.in_admin)
+
+
+class AdminSnap(AuthorizationBase):
+    """Restrict changing build settings on snap packages.
+
+    The security of the non-virtualised build farm depends on these
+    settings, so they can only be changed by "PPA"/commercial admins, or by
+    "PPA" self admins on snap packages that they can already edit.
+    """
+    permission = 'launchpad.Admin'
+    usedfor = ISnap
+
+    def checkAuthenticated(self, user):
+        if user.in_ppa_admin or user.in_commercial_admin or user.in_admin:
+            return True
+        return (
+            user.in_ppa_self_admins
+            and EditSnap(self.obj).checkAuthenticated(user))
+
+
+class SnapSubscriptionEdit(AuthorizationBase):
+    permission = 'launchpad.Edit'
+    usedfor = ISnapSubscription
+
+    def checkAuthenticated(self, user):
+        """Is the user able to edit a Snap recipe subscription?
+
+        Any team member can edit a Snap recipe subscription for their
+        team.
+        Launchpad Admins can also edit any Snap recipe subscription.
+        The owner of the subscribed Snap can edit the subscription. If
+        the Snap owner is a team, then members of the team can edit
+        the subscription.
+        """
+        return (user.inTeam(self.obj.snap.owner) or
+                user.inTeam(self.obj.person) or
+                user.inTeam(self.obj.subscribed_by) or
+                user.in_admin)
+
+
+class SnapSubscriptionView(AuthorizationBase):
+    permission = 'launchpad.View'
+    usedfor = ISnapSubscription
+
+    def checkUnauthenticated(self):
+        return self.obj.snap.visibleByUser(None)
+
+    def checkAuthenticated(self, user):
+        return self.obj.snap.visibleByUser(user.person)
+
+
+class ViewSnapBuildRequest(DelegatedAuthorization):
+    permission = 'launchpad.View'
+    usedfor = ISnapBuildRequest
+
+    def __init__(self, obj):
+        super().__init__(obj, obj.snap, 'launchpad.View')
+
+
+class ViewSnapBuild(DelegatedAuthorization):
+    permission = 'launchpad.View'
+    usedfor = ISnapBuild
+
+    def iter_objects(self):
+        yield self.obj.snap
+        yield self.obj.archive
+
+
+class EditSnapBuild(AdminByBuilddAdmin):
+    permission = 'launchpad.Edit'
+    usedfor = ISnapBuild
+
+    def checkAuthenticated(self, user):
+        """Check edit access for snap package builds.
+
+        Allow admins, buildd admins, and the owner of the snap package.
+        (Note that the requester of the build is required to be in the team
+        that owns the snap package.)
+        """
+        auth_snap = EditSnap(self.obj.snap)
+        if auth_snap.checkAuthenticated(user):
+            return True
+        return super().checkAuthenticated(user)
+
+
+class AdminSnapBuild(AdminByBuilddAdmin):
+    usedfor = ISnapBuild
+
+
+class ViewSnappySeries(AnonymousAuthorization):
+    """Anyone can view an `ISnappySeries`."""
+    usedfor = ISnappySeries
+
+
+class EditSnappySeries(EditByRegistryExpertsOrAdmins):
+    usedfor = ISnappySeries
+
+
+class EditSnappySeriesSet(EditByRegistryExpertsOrAdmins):
+    usedfor = ISnappySeriesSet
+
+
+class ViewSnapBase(AnonymousAuthorization):
+    """Anyone can view an `ISnapBase`."""
+    usedfor = ISnapBase
+
+
+class EditSnapBase(EditByRegistryExpertsOrAdmins):
+    usedfor = ISnapBase
+
+
+class EditSnapBaseSet(EditByRegistryExpertsOrAdmins):
+    usedfor = ISnapBaseSet

Follow ups