← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~lgp171188/launchpad:split-security.py-services into launchpad:master

 

Guruprasad has proposed merging ~lgp171188/launchpad:split-security.py-services into launchpad:master with ~lgp171188/launchpad:split-security.py-blueprints as a prerequisite.

Commit message:
Move the security adapters for lp.services.* to lp.services.*

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~lgp171188/launchpad/+git/launchpad/+merge/426635
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~lgp171188/launchpad:split-security.py-services into launchpad:master.
diff --git a/lib/lp/security.py b/lib/lp/security.py
index 46c0f4a..a87f341 100644
--- a/lib/lp/security.py
+++ b/lib/lp/security.py
@@ -24,10 +24,8 @@ from datetime import (
     )
 
 import pytz
-from zope.component import queryAdapter
 from zope.interface import Interface
 
-from lp.app.interfaces.security import IAuthorization
 from lp.app.security import (
     AnonymousAuthorization,
     AuthorizationBase,
@@ -60,27 +58,8 @@ 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.auth.interfaces import IAccessToken
 from lp.services.config import config
-from lp.services.identity.interfaces.emailaddress import IEmailAddress
-from lp.services.librarian.interfaces import ILibraryFileAliasWithParent
-from lp.services.messages.interfaces.message import IMessage
-from lp.services.messages.interfaces.messagerevision import IMessageRevision
-from lp.services.oauth.interfaces import (
-    IOAuthAccessToken,
-    IOAuthRequestToken,
-    )
-from lp.services.openid.interfaces.openididentifier import IOpenIdIdentifier
 from lp.services.webapp.interfaces import ILaunchpadRoot
-from lp.services.webhooks.interfaces import (
-    IWebhook,
-    IWebhookDeliveryJob,
-    )
-from lp.services.worlddata.interfaces.country import ICountry
-from lp.services.worlddata.interfaces.language import (
-    ILanguage,
-    ILanguageSet,
-    )
 from lp.snappy.interfaces.snap import (
     ISnap,
     ISnapBuildRequest,
@@ -210,45 +189,6 @@ class ModerateByRegistryExpertsOrAdmins(AuthorizationBase):
         return user.in_admin or user.in_registry_experts
 
 
-class ViewOpenIdIdentifierBySelfOrAdmin(AuthorizationBase):
-    permission = 'launchpad.View'
-    usedfor = IOpenIdIdentifier
-
-    def checkAuthenticated(self, user):
-        return user.in_admin or user.person.accountID == self.obj.accountID
-
-
-class EditOAuthAccessToken(AuthorizationBase):
-    permission = 'launchpad.Edit'
-    usedfor = IOAuthAccessToken
-
-    def checkAuthenticated(self, user):
-        return self.obj.person == user.person or user.in_admin
-
-
-class EditOAuthRequestToken(EditOAuthAccessToken):
-    permission = 'launchpad.Edit'
-    usedfor = IOAuthRequestToken
-
-
-class EditAccessToken(AuthorizationBase):
-    permission = 'launchpad.Edit'
-    usedfor = IAccessToken
-
-    def checkAuthenticated(self, user):
-        if user.inTeam(self.obj.owner):
-            return True
-        # Being able to edit the token doesn't allow extracting the secret,
-        # so it's OK to allow the owner of the target to do so too.  This
-        # allows target owners to exercise some control over access to their
-        # object.
-        adapter = queryAdapter(
-            self.obj.target, IAuthorization, 'launchpad.Edit')
-        if adapter is not None and adapter.checkAuthenticated(user):
-            return True
-        return False
-
-
 class EditByOwnersOrAdmins(AuthorizationBase):
     permission = 'launchpad.Edit'
     usedfor = IHasOwner
@@ -278,11 +218,6 @@ class BugTargetOwnerOrBugSupervisorOrAdmins(AuthorizationBase):
                 user.in_admin)
 
 
-class ViewCountry(AnonymousAuthorization):
-    """Anyone can view a Country."""
-    usedfor = ICountry
-
-
 class EditStructuralSubscription(AuthorizationBase):
     permission = 'launchpad.Edit'
     usedfor = IStructuralSubscription
@@ -375,158 +310,10 @@ class EditPackageBuild(EditBuildFarmJob):
                 user.inTeam(self.obj.archive.owner))
 
 
-class ViewLanguageSet(AnonymousAuthorization):
-    """Anyone can view an ILanguageSet."""
-    usedfor = ILanguageSet
-
-
-class AdminLanguageSet(OnlyRosettaExpertsAndAdmins):
-    permission = 'launchpad.Admin'
-    usedfor = ILanguageSet
-
-
-class ViewLanguage(AnonymousAuthorization):
-    """Anyone can view an ILanguage."""
-    usedfor = ILanguage
-
-
-class AdminLanguage(OnlyRosettaExpertsAndAdmins):
-    permission = 'launchpad.Admin'
-    usedfor = ILanguage
-
-
-class ViewEmailAddress(AuthorizationBase):
-    permission = 'launchpad.View'
-    usedfor = IEmailAddress
-
-    def checkUnauthenticated(self):
-        """See `AuthorizationBase`."""
-        # Anonymous users can never see email addresses.
-        return False
-
-    def checkAuthenticated(self, user):
-        """Can the user see the details of this email address?
-
-        If the email address' owner doesn't want their email addresses to be
-        hidden, anyone can see them.  Otherwise only the owner themselves or
-        admins can see them.
-        """
-        # Always allow users to see their own email addresses.
-        if self.obj.person == user:
-            return True
-
-        if not (self.obj.person is None or
-                self.obj.person.hide_email_addresses):
-            return True
-
-        return (self.obj.person is not None and user.inTeam(self.obj.person)
-                or user.in_commercial_admin
-                or user.in_registry_experts
-                or user.in_admin)
-
-
-class EditEmailAddress(EditByOwnersOrAdmins):
-    permission = 'launchpad.Edit'
-    usedfor = IEmailAddress
-
-    def checkAuthenticated(self, user):
-        # Always allow users to see their own email addresses.
-        if self.obj.person == user:
-            return True
-        return super().checkAuthenticated(user)
-
-
-class EditLibraryFileAliasWithParent(AuthorizationBase):
-    permission = 'launchpad.Edit'
-    usedfor = ILibraryFileAliasWithParent
-
-    def checkAuthenticated(self, user):
-        """Only persons which can edit an LFA's parent can edit an LFA.
-
-        By default, a LibraryFileAlias does not know about its parent.
-        Such aliases are never editable. Use an adapter to provide a
-        parent object.
-
-        If a parent is known, users which can edit the parent can also
-        edit properties of the LibraryFileAlias.
-        """
-        parent = getattr(self.obj, '__parent__', None)
-        if parent is None:
-            return False
-        return self.forwardCheckAuthenticated(user, parent)
-
-
-class ViewLibraryFileAliasWithParent(AuthorizationBase):
-    """Authorization class for viewing LibraryFileAliass having a parent."""
-
-    permission = 'launchpad.View'
-    usedfor = ILibraryFileAliasWithParent
-
-    def checkAuthenticated(self, user):
-        """Only persons which can edit an LFA's parent can edit an LFA.
-
-        By default, a LibraryFileAlias does not know about its parent.
-
-        If a parent is known, users which can view the parent can also
-        view the LibraryFileAlias.
-        """
-        parent = getattr(self.obj, '__parent__', None)
-        if parent is None:
-            return False
-        return self.forwardCheckAuthenticated(user, parent)
-
-
-class SetMessageVisibility(AuthorizationBase):
-    permission = 'launchpad.Admin'
-    usedfor = IMessage
-
-    def checkAuthenticated(self, user):
-        """Admins and registry admins can set bug comment visibility."""
-        return (user.in_admin or user.in_registry_experts)
-
-
-class EditMessage(AuthorizationBase):
-    permission = 'launchpad.Edit'
-    usedfor = IMessage
-
-    def checkAuthenticated(self, user):
-        """Only message owner can edit it."""
-        return user.isOwner(self.obj)
-
-
-class EditMessageRevision(DelegatedAuthorization):
-    permission = 'launchpad.Edit'
-    usedfor = IMessageRevision
-
-    def __init__(self, obj):
-        super().__init__(obj, obj.message, 'launchpad.Edit')
-
-
 class ViewPublisherConfig(AdminByAdminsTeam):
     usedfor = IPublisherConfig
 
 
-class ViewWebhook(AuthorizationBase):
-    """Webhooks can be viewed and edited by someone who can edit the target."""
-    permission = 'launchpad.View'
-    usedfor = IWebhook
-
-    def checkUnauthenticated(self):
-        return False
-
-    def checkAuthenticated(self, user):
-        yield self.obj.target, 'launchpad.Edit'
-
-
-class ViewWebhookDeliveryJob(DelegatedAuthorization):
-    """Webhooks can be viewed and edited by someone who can edit the target."""
-    permission = 'launchpad.View'
-    usedfor = IWebhookDeliveryJob
-
-    def __init__(self, obj):
-        super().__init__(obj, obj.webhook, 'launchpad.View')
-
-
 class ViewSnap(AuthorizationBase):
     """Private snaps are only visible to their owners and admins."""
     permission = 'launchpad.View'
diff --git a/lib/lp/services/auth/configure.zcml b/lib/lp/services/auth/configure.zcml
index c0446d6..c50d89a 100644
--- a/lib/lp/services/auth/configure.zcml
+++ b/lib/lp/services/auth/configure.zcml
@@ -9,6 +9,7 @@
     xmlns:webservice="http://namespaces.canonical.com/webservice";
     i18n_domain="launchpad">
 
+    <authorizations module=".security" />
     <class class="lp.services.auth.model.AccessToken">
         <require
             permission="launchpad.Edit"
diff --git a/lib/lp/services/auth/security.py b/lib/lp/services/auth/security.py
new file mode 100644
index 0000000..837d343
--- /dev/null
+++ b/lib/lp/services/auth/security.py
@@ -0,0 +1,30 @@
+# 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 soyuz package."""
+
+__all__ = []
+
+from zope.component import queryAdapter
+
+from lp.app.interfaces.security import IAuthorization
+from lp.app.security import AuthorizationBase
+from lp.services.auth.interfaces import IAccessToken
+
+
+class EditAccessToken(AuthorizationBase):
+    permission = 'launchpad.Edit'
+    usedfor = IAccessToken
+
+    def checkAuthenticated(self, user):
+        if user.inTeam(self.obj.owner):
+            return True
+        # Being able to edit the token doesn't allow extracting the secret,
+        # so it's OK to allow the owner of the target to do so too.  This
+        # allows target owners to exercise some control over access to their
+        # object.
+        adapter = queryAdapter(
+            self.obj.target, IAuthorization, 'launchpad.Edit')
+        if adapter is not None and adapter.checkAuthenticated(user):
+            return True
+        return False
diff --git a/lib/lp/services/identity/configure.zcml b/lib/lp/services/identity/configure.zcml
index 1eb6c3a..f58ace8 100644
--- a/lib/lp/services/identity/configure.zcml
+++ b/lib/lp/services/identity/configure.zcml
@@ -5,6 +5,8 @@
     xmlns:xmlrpc="http://namespaces.zope.org/xmlrpc";
     xmlns:webservice="http://namespaces.canonical.com/webservice";
     i18n_domain="launchpad">
+
+    <authorizations module=".security" />
     <class
         class="lp.services.identity.model.emailaddress.EmailAddress">
         <allow
diff --git a/lib/lp/services/identity/security.py b/lib/lp/services/identity/security.py
new file mode 100644
index 0000000..4a9fd4f
--- /dev/null
+++ b/lib/lp/services/identity/security.py
@@ -0,0 +1,51 @@
+# 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 soyuz package."""
+
+__all__ = []
+
+from lp.app.security import AuthorizationBase
+from lp.security import EditByOwnersOrAdmins
+from lp.services.identity.interfaces.emailaddress import IEmailAddress
+
+
+class ViewEmailAddress(AuthorizationBase):
+    permission = 'launchpad.View'
+    usedfor = IEmailAddress
+
+    def checkUnauthenticated(self):
+        """See `AuthorizationBase`."""
+        # Anonymous users can never see email addresses.
+        return False
+
+    def checkAuthenticated(self, user):
+        """Can the user see the details of this email address?
+
+        If the email address' owner doesn't want their email addresses to be
+        hidden, anyone can see them.  Otherwise only the owner themselves or
+        admins can see them.
+        """
+        # Always allow users to see their own email addresses.
+        if self.obj.person == user:
+            return True
+
+        if not (self.obj.person is None or
+                self.obj.person.hide_email_addresses):
+            return True
+
+        return (self.obj.person is not None and user.inTeam(self.obj.person)
+                or user.in_commercial_admin
+                or user.in_registry_experts
+                or user.in_admin)
+
+
+class EditEmailAddress(EditByOwnersOrAdmins):
+    permission = 'launchpad.Edit'
+    usedfor = IEmailAddress
+
+    def checkAuthenticated(self, user):
+        # Always allow users to see their own email addresses.
+        if self.obj.person == user:
+            return True
+        return super().checkAuthenticated(user)
diff --git a/lib/lp/services/librarian/configure.zcml b/lib/lp/services/librarian/configure.zcml
index 269bbd8..84f70d1 100644
--- a/lib/lp/services/librarian/configure.zcml
+++ b/lib/lp/services/librarian/configure.zcml
@@ -8,6 +8,7 @@
     xmlns:zope="http://namespaces.zope.org/zope";
     i18n_domain="launchpad">
 
+  <authorizations module=".security" />
   <include file="client.zcml" />
 
   <class class="lp.services.librarian.model.LibraryFileAlias">
diff --git a/lib/lp/services/librarian/security.py b/lib/lp/services/librarian/security.py
new file mode 100644
index 0000000..3d459f5
--- /dev/null
+++ b/lib/lp/services/librarian/security.py
@@ -0,0 +1,46 @@
+# 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 security package."""
+from lp.app.security import AuthorizationBase
+from lp.services.librarian.interfaces import ILibraryFileAliasWithParent
+
+
+class EditLibraryFileAliasWithParent(AuthorizationBase):
+    permission = 'launchpad.Edit'
+    usedfor = ILibraryFileAliasWithParent
+
+    def checkAuthenticated(self, user):
+        """Only persons which can edit an LFA's parent can edit an LFA.
+
+        By default, a LibraryFileAlias does not know about its parent.
+        Such aliases are never editable. Use an adapter to provide a
+        parent object.
+
+        If a parent is known, users which can edit the parent can also
+        edit properties of the LibraryFileAlias.
+        """
+        parent = getattr(self.obj, '__parent__', None)
+        if parent is None:
+            return False
+        return self.forwardCheckAuthenticated(user, parent)
+
+
+class ViewLibraryFileAliasWithParent(AuthorizationBase):
+    """Authorization class for viewing LibraryFileAliass having a parent."""
+
+    permission = 'launchpad.View'
+    usedfor = ILibraryFileAliasWithParent
+
+    def checkAuthenticated(self, user):
+        """Only persons which can edit an LFA's parent can edit an LFA.
+
+        By default, a LibraryFileAlias does not know about its parent.
+
+        If a parent is known, users which can view the parent can also
+        view the LibraryFileAlias.
+        """
+        parent = getattr(self.obj, '__parent__', None)
+        if parent is None:
+            return False
+        return self.forwardCheckAuthenticated(user, parent)
diff --git a/lib/lp/services/messages/configure.zcml b/lib/lp/services/messages/configure.zcml
index 867b260..ab004f0 100644
--- a/lib/lp/services/messages/configure.zcml
+++ b/lib/lp/services/messages/configure.zcml
@@ -9,6 +9,7 @@
     xmlns:webservice="http://namespaces.canonical.com/webservice";
     i18n_domain="launchpad">
 
+    <authorizations module=".security" />
     <!-- Message -->
     <class class="lp.services.messages.model.message.Message">
         <allow
diff --git a/lib/lp/services/messages/security.py b/lib/lp/services/messages/security.py
new file mode 100644
index 0000000..2216f87
--- /dev/null
+++ b/lib/lp/services/messages/security.py
@@ -0,0 +1,56 @@
+# 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 messages package."""
+
+__all__ = []
+
+from lp.app.security import (
+    AuthorizationBase,
+    DelegatedAuthorization,
+    )
+from lp.services.messages.interfaces.message import IMessage
+from lp.services.messages.interfaces.messagerevision import IMessageRevision
+from lp.services.oauth.interfaces import (
+    IOAuthAccessToken,
+    IOAuthRequestToken,
+    )
+
+
+class SetMessageVisibility(AuthorizationBase):
+    permission = 'launchpad.Admin'
+    usedfor = IMessage
+
+    def checkAuthenticated(self, user):
+        """Admins and registry admins can set bug comment visibility."""
+        return (user.in_admin or user.in_registry_experts)
+
+
+class EditMessage(AuthorizationBase):
+    permission = 'launchpad.Edit'
+    usedfor = IMessage
+
+    def checkAuthenticated(self, user):
+        """Only message owner can edit it."""
+        return user.isOwner(self.obj)
+
+
+class EditMessageRevision(DelegatedAuthorization):
+    permission = 'launchpad.Edit'
+    usedfor = IMessageRevision
+
+    def __init__(self, obj):
+        super().__init__(obj, obj.message, 'launchpad.Edit')
+
+
+class EditOAuthAccessToken(AuthorizationBase):
+    permission = 'launchpad.Edit'
+    usedfor = IOAuthAccessToken
+
+    def checkAuthenticated(self, user):
+        return self.obj.person == user.person or user.in_admin
+
+
+class EditOAuthRequestToken(EditOAuthAccessToken):
+    permission = 'launchpad.Edit'
+    usedfor = IOAuthRequestToken
diff --git a/lib/lp/services/oauth/configure.zcml b/lib/lp/services/oauth/configure.zcml
index 802c231..db02a1b 100644
--- a/lib/lp/services/oauth/configure.zcml
+++ b/lib/lp/services/oauth/configure.zcml
@@ -9,6 +9,7 @@
     xmlns:browser="http://namespaces.zope.org/browser";
     i18n_domain="launchpad">
 
+  <authorizations module=".security" />
   <include package=".browser"/>
 
   <class class="lp.services.oauth.model.OAuthConsumer">
diff --git a/lib/lp/services/oauth/security.py b/lib/lp/services/oauth/security.py
new file mode 100644
index 0000000..3e24119
--- /dev/null
+++ b/lib/lp/services/oauth/security.py
@@ -0,0 +1,6 @@
+# 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 oauth package."""
+
+__all__ = []
diff --git a/lib/lp/services/openid/configure.zcml b/lib/lp/services/openid/configure.zcml
index a692cab..fd31827 100644
--- a/lib/lp/services/openid/configure.zcml
+++ b/lib/lp/services/openid/configure.zcml
@@ -8,6 +8,7 @@
     xmlns:i18n="http://namespaces.zope.org/i18n";
     i18n_domain="launchpad">
 
+    <authorizations module=".security" />
     <class class=".model.openididentifier.OpenIdIdentifier">
         <require
             permission="launchpad.View"
diff --git a/lib/lp/services/openid/security.py b/lib/lp/services/openid/security.py
new file mode 100644
index 0000000..776ee43
--- /dev/null
+++ b/lib/lp/services/openid/security.py
@@ -0,0 +1,17 @@
+# 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 openid package."""
+
+__all__ = []
+
+from lp.app.security import AuthorizationBase
+from lp.services.openid.interfaces.openididentifier import IOpenIdIdentifier
+
+
+class ViewOpenIdIdentifierBySelfOrAdmin(AuthorizationBase):
+    permission = 'launchpad.View'
+    usedfor = IOpenIdIdentifier
+
+    def checkAuthenticated(self, user):
+        return user.in_admin or user.person.accountID == self.obj.accountID
diff --git a/lib/lp/services/webapp/configure.zcml b/lib/lp/services/webapp/configure.zcml
index bb5ddc0..154944c 100644
--- a/lib/lp/services/webapp/configure.zcml
+++ b/lib/lp/services/webapp/configure.zcml
@@ -8,6 +8,7 @@
     xmlns:xmlrpc="http://namespaces.zope.org/xmlrpc";
     i18n_domain="launchpad">
 
+    <authorizations module=".security" />
     <include file="errorlog.zcml" />
     <include file="database.zcml" />
 
diff --git a/lib/lp/services/webapp/security.py b/lib/lp/services/webapp/security.py
new file mode 100644
index 0000000..887f40b
--- /dev/null
+++ b/lib/lp/services/webapp/security.py
@@ -0,0 +1,6 @@
+# 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 soyuz package."""
+
+__all__ = []
diff --git a/lib/lp/services/webhooks/configure.zcml b/lib/lp/services/webhooks/configure.zcml
index 9883b25..81530db 100644
--- a/lib/lp/services/webhooks/configure.zcml
+++ b/lib/lp/services/webhooks/configure.zcml
@@ -7,6 +7,7 @@
     xmlns:browser="http://namespaces.zope.org/browser";
     xmlns:webservice="http://namespaces.canonical.com/webservice";>
 
+    <authorizations module=".security" />
     <class class="lp.services.webhooks.model.Webhook">
         <require
             permission="launchpad.View"
diff --git a/lib/lp/services/webhooks/security.py b/lib/lp/services/webhooks/security.py
new file mode 100644
index 0000000..2237a57
--- /dev/null
+++ b/lib/lp/services/webhooks/security.py
@@ -0,0 +1,36 @@
+# 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 soyuz package."""
+
+__all__ = []
+
+from lp.app.security import (
+    AuthorizationBase,
+    DelegatedAuthorization,
+    )
+from lp.services.webhooks.interfaces import (
+    IWebhook,
+    IWebhookDeliveryJob,
+    )
+
+
+class ViewWebhook(AuthorizationBase):
+    """Webhooks can be viewed and edited by someone who can edit the target."""
+    permission = 'launchpad.View'
+    usedfor = IWebhook
+
+    def checkUnauthenticated(self):
+        return False
+
+    def checkAuthenticated(self, user):
+        yield self.obj.target, 'launchpad.Edit'
+
+
+class ViewWebhookDeliveryJob(DelegatedAuthorization):
+    """Webhooks can be viewed and edited by someone who can edit the target."""
+    permission = 'launchpad.View'
+    usedfor = IWebhookDeliveryJob
+
+    def __init__(self, obj):
+        super().__init__(obj, obj.webhook, 'launchpad.View')
diff --git a/lib/lp/services/worlddata/configure.zcml b/lib/lp/services/worlddata/configure.zcml
index f705487..9747a19 100644
--- a/lib/lp/services/worlddata/configure.zcml
+++ b/lib/lp/services/worlddata/configure.zcml
@@ -7,6 +7,7 @@
     xmlns:i18n="http://namespaces.zope.org/i18n";
     xmlns:webservice="http://namespaces.canonical.com/webservice";
     i18n_domain="launchpad">
+    <authorizations module=".security" />
     <include file="vocabularies.zcml"/>
 
     <class class=".model.country.Country">
diff --git a/lib/lp/services/worlddata/security.py b/lib/lp/services/worlddata/security.py
new file mode 100644
index 0000000..7213587
--- /dev/null
+++ b/lib/lp/services/worlddata/security.py
@@ -0,0 +1,39 @@
+# 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 soyuz package."""
+
+__all__ = []
+
+from lp.app.security import AnonymousAuthorization
+from lp.security import OnlyRosettaExpertsAndAdmins
+from lp.services.worlddata.interfaces.country import ICountry
+from lp.services.worlddata.interfaces.language import (
+    ILanguage,
+    ILanguageSet,
+    )
+
+
+class ViewCountry(AnonymousAuthorization):
+    """Anyone can view a Country."""
+    usedfor = ICountry
+
+
+class ViewLanguageSet(AnonymousAuthorization):
+    """Anyone can view an ILanguageSet."""
+    usedfor = ILanguageSet
+
+
+class AdminLanguageSet(OnlyRosettaExpertsAndAdmins):
+    permission = 'launchpad.Admin'
+    usedfor = ILanguageSet
+
+
+class ViewLanguage(AnonymousAuthorization):
+    """Anyone can view an ILanguage."""
+    usedfor = ILanguage
+
+
+class AdminLanguage(OnlyRosettaExpertsAndAdmins):
+    permission = 'launchpad.Admin'
+    usedfor = ILanguage

Follow ups