launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #28750
[Merge] ~lgp171188/launchpad:security.py-split-answers-package into launchpad:master
Guruprasad has proposed merging ~lgp171188/launchpad:security.py-split-answers-package into launchpad:master.
Commit message:
Move the answers-related security adapters to lp.answers.security
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~lgp171188/launchpad/+git/launchpad/+merge/426481
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~lgp171188/launchpad:security.py-split-answers-package into launchpad:master.
diff --git a/lib/lp/answers/configure.zcml b/lib/lp/answers/configure.zcml
index a7eefcd..90d1679 100644
--- a/lib/lp/answers/configure.zcml
+++ b/lib/lp/answers/configure.zcml
@@ -9,6 +9,7 @@
xmlns:lp="http://namespaces.canonical.com/lp"
i18n_domain="launchpad">
+ <authorizations module=".security" />
<include package=".browser" />
<publisher
diff --git a/lib/lp/answers/security.py b/lib/lp/answers/security.py
new file mode 100644
index 0000000..8332000
--- /dev/null
+++ b/lib/lp/answers/security.py
@@ -0,0 +1,142 @@
+# 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 answers package."""
+
+__all__ = []
+
+from lp.answers.interfaces.faq import IFAQ
+from lp.answers.interfaces.faqtarget import IFAQTarget
+from lp.answers.interfaces.question import IQuestion
+from lp.answers.interfaces.questionmessage import IQuestionMessage
+from lp.answers.interfaces.questionsperson import IQuestionsPerson
+from lp.answers.interfaces.questiontarget import IQuestionTarget
+from lp.app.security import AnonymousAuthorization, AuthorizationBase
+from lp.registry.interfaces.distributionsourcepackage import (
+ IDistributionSourcePackage,
+)
+from lp.security import EditByOwnersOrAdmins
+
+
+class AdminQuestion(AuthorizationBase):
+ permission = "launchpad.Admin"
+ usedfor = IQuestion
+
+ def checkAuthenticated(self, user):
+ """Allow only admins and owners of the question pillar target."""
+ context = self.obj.product or self.obj.distribution
+ return (
+ user.in_admin
+ or user.in_registry_experts
+ or user.inTeam(context.owner)
+ )
+
+
+class AppendQuestion(AdminQuestion):
+ permission = "launchpad.Append"
+ usedfor = IQuestion
+
+ def checkAuthenticated(self, user):
+ """Allow user who can administer the question and answer contacts."""
+ if AdminQuestion.checkAuthenticated(self, user):
+ return True
+ question_target = self.obj.target
+ if IDistributionSourcePackage.providedBy(question_target):
+ question_targets = (question_target, question_target.distribution)
+ else:
+ question_targets = (question_target,)
+ questions_person = IQuestionsPerson(user.person)
+ for target in questions_person.getDirectAnswerQuestionTargets():
+ if target in question_targets:
+ return True
+ for target in questions_person.getTeamAnswerQuestionTargets():
+ if target in question_targets:
+ return True
+ return False
+
+
+class QuestionOwner(AuthorizationBase):
+ permission = "launchpad.Owner"
+ usedfor = IQuestion
+
+ def checkAuthenticated(self, user):
+ """Allow the question's owner."""
+ return user.inTeam(self.obj.owner)
+
+
+class EditQuestion(AuthorizationBase):
+ permission = "launchpad.Edit"
+ usedfor = IQuestion
+
+ def checkAuthenticated(self, user):
+ return AdminQuestion(self.obj).checkAuthenticated(
+ user
+ ) or QuestionOwner(self.obj).checkAuthenticated(user)
+
+
+class ViewQuestion(AnonymousAuthorization):
+ usedfor = IQuestion
+
+
+class ViewQuestionMessage(AnonymousAuthorization):
+ usedfor = IQuestionMessage
+
+
+class ModerateQuestionMessage(AuthorizationBase):
+ permission = "launchpad.Moderate"
+ usedfor = IQuestionMessage
+
+ def checkAuthenticated(self, user):
+ """Admins, Registry, Maintainers, and comment owners can moderate."""
+ return (
+ user.in_admin
+ or user.in_registry_experts
+ or user.inTeam(self.obj.owner)
+ or user.inTeam(self.obj.question.target.owner)
+ )
+
+
+class AppendFAQTarget(EditByOwnersOrAdmins):
+ permission = "launchpad.Append"
+ usedfor = IFAQTarget
+
+ def checkAuthenticated(self, user):
+ """Allow people with launchpad.Edit or an answer contact."""
+ if (
+ EditByOwnersOrAdmins.checkAuthenticated(self, user)
+ or user.in_registry_experts
+ ):
+ return True
+ if IQuestionTarget.providedBy(self.obj):
+ # Adapt QuestionTargets to FAQTargets to ensure the correct
+ # object is being examined; the implementers are not synonymous.
+ faq_target = IFAQTarget(self.obj)
+ questions_person = IQuestionsPerson(user.person)
+ for target in questions_person.getDirectAnswerQuestionTargets():
+ if IFAQTarget(target) == faq_target:
+ return True
+ for target in questions_person.getTeamAnswerQuestionTargets():
+ if IFAQTarget(target) == faq_target:
+ return True
+ return False
+
+
+class EditFAQ(AuthorizationBase):
+ permission = "launchpad.Edit"
+ usedfor = IFAQ
+
+ def checkAuthenticated(self, user):
+ """Allow only admins and owners of the FAQ target."""
+ return (
+ user.in_admin
+ or user.in_registry_experts
+ or user.inTeam(self.obj.target.owner)
+ )
+
+
+class DeleteFAQ(AuthorizationBase):
+ permission = "launchpad.Delete"
+ usedfor = IFAQ
+
+ def checkAuthenticated(self, user):
+ return user.in_registry_experts or user.in_admin
diff --git a/lib/lp/security.py b/lib/lp/security.py
index cd24b0d..74a5924 100644
--- a/lib/lp/security.py
+++ b/lib/lp/security.py
@@ -28,12 +28,6 @@ from zope.component import (
)
from zope.interface import Interface
-from lp.answers.interfaces.faq import IFAQ
-from lp.answers.interfaces.faqtarget import IFAQTarget
-from lp.answers.interfaces.question import IQuestion
-from lp.answers.interfaces.questionmessage import IQuestionMessage
-from lp.answers.interfaces.questionsperson import IQuestionsPerson
-from lp.answers.interfaces.questiontarget import IQuestionTarget
from lp.app.interfaces.security import IAuthorization
from lp.app.security import (
AnonymousAuthorization,
@@ -1134,121 +1128,6 @@ class ViewTranslationTemplatesBuild(DelegatedAuthorization):
super().__init__(obj, obj.branch)
-class AdminQuestion(AuthorizationBase):
- permission = 'launchpad.Admin'
- usedfor = IQuestion
-
- def checkAuthenticated(self, user):
- """Allow only admins and owners of the question pillar target."""
- context = self.obj.product or self.obj.distribution
- return (
- user.in_admin or user.in_registry_experts
- or user.inTeam(context.owner))
-
-
-class AppendQuestion(AdminQuestion):
- permission = 'launchpad.Append'
- usedfor = IQuestion
-
- def checkAuthenticated(self, user):
- """Allow user who can administer the question and answer contacts."""
- if AdminQuestion.checkAuthenticated(self, user):
- return True
- question_target = self.obj.target
- if IDistributionSourcePackage.providedBy(question_target):
- question_targets = (question_target, question_target.distribution)
- else:
- question_targets = (question_target, )
- questions_person = IQuestionsPerson(user.person)
- for target in questions_person.getDirectAnswerQuestionTargets():
- if target in question_targets:
- return True
- for target in questions_person.getTeamAnswerQuestionTargets():
- if target in question_targets:
- return True
- return False
-
-
-class QuestionOwner(AuthorizationBase):
- permission = 'launchpad.Owner'
- usedfor = IQuestion
-
- def checkAuthenticated(self, user):
- """Allow the question's owner."""
- return user.inTeam(self.obj.owner)
-
-
-class EditQuestion(AuthorizationBase):
- permission = 'launchpad.Edit'
- usedfor = IQuestion
-
- def checkAuthenticated(self, user):
- return (
- AdminQuestion(self.obj).checkAuthenticated(user)
- or QuestionOwner(self.obj).checkAuthenticated(user))
-
-
-class ViewQuestion(AnonymousAuthorization):
- usedfor = IQuestion
-
-
-class ViewQuestionMessage(AnonymousAuthorization):
- usedfor = IQuestionMessage
-
-
-class ModerateQuestionMessage(AuthorizationBase):
- permission = 'launchpad.Moderate'
- usedfor = IQuestionMessage
-
- def checkAuthenticated(self, user):
- """Admins, Registry, Maintainers, and comment owners can moderate."""
- return (user.in_admin or user.in_registry_experts
- or user.inTeam(self.obj.owner)
- or user.inTeam(self.obj.question.target.owner))
-
-
-class AppendFAQTarget(EditByOwnersOrAdmins):
- permission = 'launchpad.Append'
- usedfor = IFAQTarget
-
- def checkAuthenticated(self, user):
- """Allow people with launchpad.Edit or an answer contact."""
- if (EditByOwnersOrAdmins.checkAuthenticated(self, user)
- or user.in_registry_experts):
- return True
- if IQuestionTarget.providedBy(self.obj):
- # Adapt QuestionTargets to FAQTargets to ensure the correct
- # object is being examined; the implementers are not synonymous.
- faq_target = IFAQTarget(self.obj)
- questions_person = IQuestionsPerson(user.person)
- for target in questions_person.getDirectAnswerQuestionTargets():
- if IFAQTarget(target) == faq_target:
- return True
- for target in questions_person.getTeamAnswerQuestionTargets():
- if IFAQTarget(target) == faq_target:
- return True
- return False
-
-
-class EditFAQ(AuthorizationBase):
- permission = 'launchpad.Edit'
- usedfor = IFAQ
-
- def checkAuthenticated(self, user):
- """Allow only admins and owners of the FAQ target."""
- return (
- user.in_admin or user.in_registry_experts or
- user.inTeam(self.obj.target.owner))
-
-
-class DeleteFAQ(AuthorizationBase):
- permission = 'launchpad.Delete'
- usedfor = IFAQ
-
- def checkAuthenticated(self, user):
- return user.in_registry_experts or user.in_admin
-
-
class ViewLanguageSet(AnonymousAuthorization):
"""Anyone can view an ILangaugeSet."""
usedfor = ILanguageSet
Follow ups