launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #28777
[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