Merge lp:~deryck/launchpad/displayname-private-team-oops-634847 into lp:launchpad

Proposed by Deryck Hodge
Status: Merged
Approved by: Deryck Hodge
Approved revision: no longer in the source branch.
Merged at revision: 11617
Proposed branch: lp:~deryck/launchpad/displayname-private-team-oops-634847
Merge into: lp:launchpad
Diff against target: 87 lines (+50/-2)
2 files modified
lib/lp/bugs/model/bug.py (+7/-2)
lib/lp/bugs/tests/test_bug.py (+43/-0)
To merge this branch: bzr merge lp:~deryck/launchpad/displayname-private-team-oops-634847
Reviewer Review Type Date Requested Status
Abel Deuring (community) code Approve
Review via email: mp+36199@code.launchpad.net

Commit message

Fix an OOPS when trying to sort on displayname for bug subscribers.

Description of the change

This work fixes the OOPS reported in bug 634847, where a comment was
not being allowed to post to a bug. The error was because a team was
subscribed that was private, and getAlsoNotifiedSubscribers was
blowing up trying to access a forbidden attribute ("displayname") for
sorting.

This branch adds tests to ensure we can list subscribers when one is
private and fixes the couple spots in the code where this was a
problem. The fix I chose was to use lambda to get an object that was
not security proxied and then pass the displayname attribute as the
sort key. This allows the list that is returned to retain security
proxied objects.

Cheers,
deryck

To post a comment you must log in.
Revision history for this message
Abel Deuring (adeuring) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/bugs/model/bug.py'
--- lib/lp/bugs/model/bug.py 2010-09-22 08:48:24 +0000
+++ lib/lp/bugs/model/bug.py 2010-09-23 12:32:52 +0000
@@ -842,8 +842,11 @@
842 self.getAlsoNotifiedSubscribers(recipients, level) +842 self.getAlsoNotifiedSubscribers(recipients, level) +
843 self.getSubscribersFromDuplicates(recipients, level))843 self.getSubscribersFromDuplicates(recipients, level))
844844
845 # Remove security proxy for the sort key, but return
846 # the regular proxied object.
845 return sorted(847 return sorted(
846 indirect_subscribers, key=operator.attrgetter("displayname"))848 indirect_subscribers,
849 key=lambda x: removeSecurityProxy(x).displayname)
847850
848 def getSubscriptionsFromDuplicates(self, recipients=None):851 def getSubscriptionsFromDuplicates(self, recipients=None):
849 """See `IBug`."""852 """See `IBug`."""
@@ -999,9 +1002,11 @@
999 # Direct subscriptions always take precedence over indirect1002 # Direct subscriptions always take precedence over indirect
1000 # subscriptions.1003 # subscriptions.
1001 direct_subscribers = set(self.getDirectSubscribers())1004 direct_subscribers = set(self.getDirectSubscribers())
1005 # Remove security proxy for the sort key, but return
1006 # the regular proxied object.
1002 return sorted(1007 return sorted(
1003 (also_notified_subscribers - direct_subscribers),1008 (also_notified_subscribers - direct_subscribers),
1004 key=operator.attrgetter('displayname'))1009 key=lambda x: removeSecurityProxy(x).displayname)
10051010
1006 def getBugNotificationRecipients(self, duplicateof=None, old_bug=None,1011 def getBugNotificationRecipients(self, duplicateof=None, old_bug=None,
1007 level=None,1012 level=None,
10081013
=== modified file 'lib/lp/bugs/tests/test_bug.py'
--- lib/lp/bugs/tests/test_bug.py 2010-08-31 00:55:52 +0000
+++ lib/lp/bugs/tests/test_bug.py 2010-09-23 12:32:52 +0000
@@ -6,6 +6,8 @@
6__metaclass__ = type6__metaclass__ = type
77
8from canonical.testing import DatabaseFunctionalLayer8from canonical.testing import DatabaseFunctionalLayer
9from lp.registry.interfaces.person import PersonVisibility
10from lp.registry.model.structuralsubscription import StructuralSubscription
9from lp.testing import (11from lp.testing import (
10 person_logged_in,12 person_logged_in,
11 TestCaseWithFactory,13 TestCaseWithFactory,
@@ -65,3 +67,44 @@
65 set([person, team1, team2]),67 set([person, team1, team2]),
66 set(real_bug.getSubscribersForPerson(person)))68 set(real_bug.getSubscribersForPerson(person)))
6769
70 def test_get_also_notified_subscribers_with_private_team(self):
71 product = self.factory.makeProduct()
72 bug = self.factory.makeBug(product=product)
73 member = self.factory.makePerson()
74 team = self.factory.makeTeam(
75 owner=member, visibility=PersonVisibility.PRIVATE)
76 StructuralSubscription(
77 product=product, subscriber=team, subscribed_by=member)
78 self.assertTrue(team in bug.getAlsoNotifiedSubscribers())
79
80 def test_get_indirect_subscribers_with_private_team(self):
81 product = self.factory.makeProduct()
82 bug = self.factory.makeBug(product=product)
83 member = self.factory.makePerson()
84 team = self.factory.makeTeam(
85 owner=member, visibility=PersonVisibility.PRIVATE)
86 StructuralSubscription(
87 product=product, subscriber=team, subscribed_by=member)
88 self.assertTrue(team in bug.getIndirectSubscribers())
89
90 def test_get_direct_subscribers_with_private_team(self):
91 product = self.factory.makeProduct()
92 bug = self.factory.makeBug(product=product)
93 member = self.factory.makePerson()
94 team = self.factory.makeTeam(
95 owner=member, visibility=PersonVisibility.PRIVATE)
96 with person_logged_in(member):
97 bug.subscribe(team, member)
98 self.assertTrue(team in bug.getDirectSubscribers())
99
100 def test_get_subscribers_from_duplicates_with_private_team(self):
101 product = self.factory.makeProduct()
102 bug = self.factory.makeBug(product=product)
103 dupe_bug = self.factory.makeBug()
104 member = self.factory.makePerson()
105 team = self.factory.makeTeam(
106 owner=member, visibility=PersonVisibility.PRIVATE)
107 with person_logged_in(member):
108 dupe_bug.subscribe(team, member)
109 dupe_bug.markAsDuplicate(bug)
110 self.assertTrue(team in bug.getSubscribersFromDuplicates())