launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #33070
[Merge] ~ines-almeida/launchpad:update-bugpresence-permissions into launchpad:master
Ines Almeida has proposed merging ~ines-almeida/launchpad:update-bugpresence-permissions into launchpad:master.
Commit message:
Fix BugPresence permissions to grant view access to certain roles
This is a relatively new model that isn't used widely, but will be used by the UCT exporter
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~ines-almeida/launchpad/+git/launchpad/+merge/493612
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~ines-almeida/launchpad:update-bugpresence-permissions into launchpad:master.
diff --git a/lib/lp/bugs/configure.zcml b/lib/lp/bugs/configure.zcml
index 0c6c864..25f2d61 100644
--- a/lib/lp/bugs/configure.zcml
+++ b/lib/lp/bugs/configure.zcml
@@ -947,7 +947,6 @@
permission="launchpad.View"
set_schema="lp.bugs.interfaces.bugpresence.IBugPresence"
attributes="
- id
bug
product
distribution
@@ -955,7 +954,7 @@
git_repository
break_fix_data"/>
<require
- permission="launchpad.Edit"
+ permission="launchpad.Delete"
attributes="destroySelf"/>
</class>
diff --git a/lib/lp/bugs/interfaces/bugpresence.py b/lib/lp/bugs/interfaces/bugpresence.py
index 3eef378..b072a61 100644
--- a/lib/lp/bugs/interfaces/bugpresence.py
+++ b/lib/lp/bugs/interfaces/bugpresence.py
@@ -7,7 +7,7 @@ __all__ = [
"IBugPresence",
"IBugPresenceSet",
]
-
+from lazr.restful.declarations import exported
from zope.interface import Interface
from zope.schema import Dict, Int
@@ -19,12 +19,14 @@ class IBugPresence(Interface):
"""A single `BugPresence` database entry."""
id = Int(title=_("ID"), required=True, readonly=True)
- bug = BugField(title=_("Bug"), readonly=True)
- product = Int(title=_("Product"))
- distribution = Int(title=_("Distribution"))
- source_package_name = Int(title=_("Source Package Name"))
- git_repository = Int(title=_("Git Repository"))
- break_fix_data = Dict(title=_("Break-Fix"))
+ bug = exported(BugField(title=_("Bug"), readonly=True))
+ product = exported(Int(title=_("Product"), readonly=True))
+ distribution = exported(Int(title=_("Distribution"), readonly=True))
+ source_package_name = exported(
+ Int(title=_("Source Package Name"), readonly=True)
+ )
+ git_repository = exported(Int(title=_("Git Repository"), readonly=True))
+ break_fix_data = exported(Dict(title=_("Break-Fix"), readonly=True))
def destroySelf(self):
"""Destroy this `IBugPresence` object."""
diff --git a/lib/lp/bugs/model/tests/test_bugpresence.py b/lib/lp/bugs/model/tests/test_bugpresence.py
new file mode 100644
index 0000000..fd311ad
--- /dev/null
+++ b/lib/lp/bugs/model/tests/test_bugpresence.py
@@ -0,0 +1,70 @@
+# Copyright 2025 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Tests for the bugpresence model."""
+
+from zope.component import getUtility
+from zope.security.management import checkPermission
+
+from lp.bugs.interfaces.bugpresence import IBugPresenceSet
+from lp.testing import (
+ TestCaseWithFactory,
+ admin_logged_in,
+ celebrity_logged_in,
+ person_logged_in,
+)
+from lp.testing.layers import DatabaseFunctionalLayer
+
+
+class TestBugPresence(TestCaseWithFactory):
+ layer = DatabaseFunctionalLayer
+
+ def setUp(self):
+ super().setUp()
+ self.bug = self.factory.makeBug()
+ self.bugpresence = getUtility(IBugPresenceSet).create(
+ bug=self.bug,
+ product=None,
+ distribution=None,
+ source_package_name=None,
+ git_repository=None,
+ break_fix_data=[],
+ )
+
+ def test_view_denied_to_anonymous(self):
+ self.assertFalse(checkPermission("launchpad.View", self.bugpresence))
+
+ def test_view_allowed_to_admin(self):
+ with admin_logged_in():
+ self.assertTrue(
+ checkPermission("launchpad.View", self.bugpresence)
+ )
+
+ def test_view_allowed_to_registry_experts(self):
+ with celebrity_logged_in("registry_experts"):
+ self.assertTrue(
+ checkPermission("launchpad.View", self.bugpresence)
+ )
+
+ def test_view_allowed_to_owner_role(self):
+ owner = self.bug.default_bugtask.target.owner
+ with person_logged_in(owner):
+ self.assertTrue(
+ checkPermission("launchpad.View", self.bugpresence)
+ )
+
+ def test_delete_denied_to_anonymous(self):
+ self.assertFalse(checkPermission("launchpad.Delete", self.bugpresence))
+
+ def test_delete_allowed_to_admin(self):
+ with admin_logged_in():
+ self.assertTrue(
+ checkPermission("launchpad.Delete", self.bugpresence)
+ )
+
+ def test_delete_denied_to_owner_role(self):
+ owner = self.bug.default_bugtask.target.owner
+ with person_logged_in(owner):
+ self.assertFalse(
+ checkPermission("launchpad.Delete", self.bugpresence)
+ )
diff --git a/lib/lp/bugs/security.py b/lib/lp/bugs/security.py
index 4d961ac..76fb30a 100644
--- a/lib/lp/bugs/security.py
+++ b/lib/lp/bugs/security.py
@@ -17,6 +17,7 @@ from lp.bugs.interfaces.bug import IBug
from lp.bugs.interfaces.bugactivity import IBugActivity
from lp.bugs.interfaces.bugattachment import IBugAttachment
from lp.bugs.interfaces.bugnomination import IBugNomination
+from lp.bugs.interfaces.bugpresence import IBugPresence
from lp.bugs.interfaces.bugsubscription import IBugSubscription
from lp.bugs.interfaces.bugsubscriptionfilter import IBugSubscriptionFilter
from lp.bugs.interfaces.bugsupervisor import IHasBugSupervisor
@@ -309,6 +310,37 @@ class EditBugAttachment(AuthorizationBase):
return False
+class ViewBugPresence(DelegatedAuthorization):
+ """Security adapter for viewing a bug presence.
+
+ If the user is authorized to view the bug, they're allowed to view the
+ bug presence.
+ """
+
+ def checkAuthenticated(self, user):
+ return (
+ user.in_admin
+ or user.in_registry_experts
+ or _has_any_bug_role(user, self.obj.bug.bugtasks)
+ )
+
+ def checkUnauthenticated(self):
+ return False
+
+
+class DeleteBugPresence(DelegatedAuthorization):
+ """Security adapter for deleting a bug presence."""
+
+ permission = "launchpad.Delete"
+ usedfor = IBugPresence
+
+ def checkAuthenticated(self, user):
+ return user.in_admin
+
+ def checkUnauthenticated(self):
+ return False
+
+
class ViewBugActivity(DelegatedAuthorization):
"""Security adapter for viewing a bug activity record.