Merge lp:~sinzui/launchpad/rollback-private-traversal into lp:launchpad

Proposed by Curtis Hovey
Status: Merged
Approved by: Curtis Hovey
Approved revision: no longer in the source branch.
Merged at revision: 14500
Proposed branch: lp:~sinzui/launchpad/rollback-private-traversal
Merge into: lp:launchpad
Diff against target: 1271 lines (+201/-453)
26 files modified
lib/canonical/launchpad/interfaces/_schema_circular_imports.py (+5/-5)
lib/canonical/launchpad/security.py (+27/-38)
lib/canonical/launchpad/webapp/authorization.py (+1/-1)
lib/canonical/launchpad/webapp/interaction.py (+1/-2)
lib/lp/app/browser/launchpad.py (+2/-2)
lib/lp/app/browser/tales.py (+0/-8)
lib/lp/app/tests/test_tales.py (+9/-16)
lib/lp/bugs/browser/tests/test_bugsubscription_views.py (+1/-1)
lib/lp/code/browser/branch.py (+1/-9)
lib/lp/code/browser/branchsubscription.py (+2/-14)
lib/lp/code/browser/tests/test_branch.py (+0/-100)
lib/lp/code/browser/tests/test_branchlisting.py (+3/-4)
lib/lp/code/stories/branches/xx-subscribing-branches.txt (+20/-14)
lib/lp/registry/browser/tests/test_mailinglists.py (+6/-8)
lib/lp/registry/browser/tests/test_person_view.py (+8/-12)
lib/lp/registry/configure.zcml (+4/-6)
lib/lp/registry/doc/person.txt (+1/-2)
lib/lp/registry/doc/private-team-visibility.txt (+0/-61)
lib/lp/registry/interfaces/person.py (+85/-85)
lib/lp/registry/interfaces/role.py (+1/-2)
lib/lp/registry/model/personroles.py (+8/-10)
lib/lp/registry/stories/webservice/xx-derivedistroseries.txt (+1/-2)
lib/lp/registry/tests/test_person_vocabularies.py (+2/-3)
lib/lp/registry/tests/test_team_webservice.py (+5/-4)
lib/lp/soyuz/tests/test_archive_subscriptions.py (+3/-33)
lib/lp/testing/factory.py (+5/-11)
To merge this branch: bzr merge lp:~sinzui/launchpad/rollback-private-traversal
Reviewer Review Type Date Requested Status
Curtis Hovey (community) code Approve
Review via email: mp+85492@code.launchpad.net

Commit message

[r=sinzui] Rollback revisions 14489, 14490, and 14493 because they block p3a subscribers.

Description of the change

Rollback revisions 14489, 14490, and 14493 because they block p3a subscribers.

To post a comment you must log in.
Revision history for this message
Curtis Hovey (sinzui) wrote :

Self-approving to unblock the next release.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/canonical/launchpad/interfaces/_schema_circular_imports.py'
2--- lib/canonical/launchpad/interfaces/_schema_circular_imports.py 2011-12-12 04:47:21 +0000
3+++ lib/canonical/launchpad/interfaces/_schema_circular_imports.py 2011-12-13 14:07:37 +0000
4@@ -135,7 +135,7 @@
5 )
6 from lp.registry.interfaces.person import (
7 IPerson,
8- IPersonViewRestricted,
9+ IPersonPublic,
10 ITeam,
11 )
12 from lp.registry.interfaces.pillar import (
13@@ -310,10 +310,10 @@
14
15 IPreviewDiff['branch_merge_proposal'].schema = IBranchMergeProposal
16
17-patch_reference_property(IPersonViewRestricted, 'archive', IArchive)
18-patch_collection_property(IPersonViewRestricted, 'ppas', IArchive)
19-patch_entry_return_type(IPersonViewRestricted, 'getPPAByName', IArchive)
20-patch_entry_return_type(IPersonViewRestricted, 'createPPA', IArchive)
21+patch_reference_property(IPersonPublic, 'archive', IArchive)
22+patch_collection_property(IPersonPublic, 'ppas', IArchive)
23+patch_entry_return_type(IPersonPublic, 'getPPAByName', IArchive)
24+patch_entry_return_type(IPersonPublic, 'createPPA', IArchive)
25
26 IHasBuildRecords['getBuildRecords'].queryTaggedValue(
27 LAZR_WEBSERVICE_EXPORTED)[
28
29=== modified file 'lib/canonical/launchpad/security.py'
30--- lib/canonical/launchpad/security.py 2011-12-12 04:47:21 +0000
31+++ lib/canonical/launchpad/security.py 2011-12-13 14:07:37 +0000
32@@ -63,7 +63,6 @@
33 IBranch,
34 user_has_special_branch_access,
35 )
36-from lp.code.interfaces.branchcollection import IBranchCollection
37 from lp.code.interfaces.branchmergeproposal import IBranchMergeProposal
38 from lp.code.interfaces.branchmergequeue import IBranchMergeQueue
39 from lp.code.interfaces.codeimport import ICodeImport
40@@ -801,35 +800,6 @@
41 if (invitee.is_team and
42 invitee in user.person.getAdministratedTeams()):
43 return True
44- return False
45-
46-
47-class PublicOrPrivateTeamsExistence(AuthorizationBase):
48- """Restrict knowing about private teams' existence.
49-
50- Knowing the existence of a private team allow traversing to its URL and
51- displaying basic information like name, displayname.
52- """
53- permission = 'launchpad.LimitedView'
54- usedfor = IPersonLimitedView
55-
56- def checkUnauthenticated(self):
57- """Unauthenticated users can only view public teams."""
58- if self.obj.visibility == PersonVisibility.PUBLIC:
59- return True
60- return False
61-
62- def checkAuthenticated(self, user):
63- """By default, we simply perform a View permission check.
64-
65- We also grant limited viewability to users who can see PPAs and
66- branches owned by the team. In other scenarios, the context in which
67- the permission is required is responsible for pre-caching the
68- launchpad.LimitedView permission on each team which requires it.
69- """
70- if self.forwardCheckAuthenticated(
71- user, self.obj, 'launchpad.View'):
72- return True
73
74 if (self.obj.is_team
75 and self.obj.visibility == PersonVisibility.PRIVATE):
76@@ -843,14 +813,33 @@
77 ppa.id for ppa in self.obj.ppas if ppa.private)
78 if len(subscriber_archive_ids.intersection(team_ppa_ids)) > 0:
79 return True
80-
81- # Grant visibility to people with subscriptions to branches owned
82- # by the private team.
83- owned_branches = getUtility(IBranchCollection).ownedBy(self.obj)
84- if owned_branches.visibleByUser(user.person).count() > 0:
85- return True
86-
87- return False
88+ return False
89+
90+
91+class PublicOrPrivateTeamsExistence(AuthorizationBase):
92+ """Restrict knowing about private teams' existence.
93+
94+ Knowing the existence of a private team allow traversing to its URL and
95+ displaying basic information like name, displayname.
96+ """
97+ permission = 'launchpad.LimitedView'
98+ usedfor = IPersonLimitedView
99+
100+ def checkUnauthenticated(self):
101+ """Unauthenticated users can only view public teams."""
102+ if self.obj.visibility == PersonVisibility.PUBLIC:
103+ return True
104+ return False
105+
106+ def checkAuthenticated(self, user):
107+ """By default, we simply perform a View permission check.
108+
109+ The context in which the permission is required is
110+ responsible for pre-caching the launchpad.LimitedView permission on
111+ each team which requires it.
112+ """
113+ return self.forwardCheckAuthenticated(
114+ user, self.obj, 'launchpad.View')
115
116
117 class EditPollByTeamOwnerOrTeamAdminsOrAdmins(
118
119=== modified file 'lib/canonical/launchpad/webapp/authorization.py'
120--- lib/canonical/launchpad/webapp/authorization.py 2011-12-12 04:47:21 +0000
121+++ lib/canonical/launchpad/webapp/authorization.py 2011-12-13 14:07:37 +0000
122@@ -192,7 +192,7 @@
123 objecttoauthorize, {})
124 if permission in object_cache:
125 return object_cache[permission]
126- principal = removeAllProxies(participation.principal)
127+ principal = participation.principal
128
129 if (principal is not None and
130 not isinstance(principal, UnauthenticatedPrincipal)):
131
132=== modified file 'lib/canonical/launchpad/webapp/interaction.py'
133--- lib/canonical/launchpad/webapp/interaction.py 2011-12-12 04:47:21 +0000
134+++ lib/canonical/launchpad/webapp/interaction.py 2011-12-13 14:07:37 +0000
135@@ -163,8 +163,7 @@
136 return setupInteraction(ANONYMOUS, participation)
137 else:
138 # Bypass zope's security because IEmailAddress.email is not public.
139- naked_person = removeSecurityProxy(person)
140- naked_email = removeSecurityProxy(naked_person.preferredemail)
141+ naked_email = removeSecurityProxy(person.preferredemail)
142 return setupInteractionByEmail(naked_email.email, participation)
143
144
145
146=== modified file 'lib/lp/app/browser/launchpad.py'
147--- lib/lp/app/browser/launchpad.py 2011-12-12 18:46:47 +0000
148+++ lib/lp/app/browser/launchpad.py 2011-12-13 14:07:37 +0000
149@@ -724,8 +724,8 @@
150 # Check to see if this is a team, and if so, whether the
151 # logged in user is allowed to view the team, by virtue of
152 # team membership or Launchpad administration.
153- if (person.is_team and
154- not check_permission('launchpad.LimitedView', person)):
155+ if (person.is_team
156+ and not check_permission('launchpad.View', person)):
157 raise NotFound(self.context, name)
158 # Only admins are permitted to see suspended users.
159 if person.account_status == AccountStatus.SUSPENDED:
160
161=== modified file 'lib/lp/app/browser/tales.py'
162--- lib/lp/app/browser/tales.py 2011-12-12 04:47:21 +0000
163+++ lib/lp/app/browser/tales.py 2011-12-13 14:07:37 +0000
164@@ -1266,14 +1266,6 @@
165 self.hidden)
166 return super(TeamFormatterAPI, self).link(view_name, rootsite)
167
168- def icon(self, view_name):
169- team = self._context
170- if not check_permission('launchpad.LimitedView', team):
171- css_class = ObjectImageDisplayAPI(team).sprite_css()
172- return '<span class="' + css_class + '"></span>'
173- else:
174- return super(TeamFormatterAPI, self).icon(view_name)
175-
176 def displayname(self, view_name, rootsite=None):
177 """See `PersonFormatterAPI`."""
178 person = self._context
179
180=== modified file 'lib/lp/app/tests/test_tales.py'
181--- lib/lp/app/tests/test_tales.py 2011-12-12 04:47:21 +0000
182+++ lib/lp/app/tests/test_tales.py 2011-12-13 14:07:37 +0000
183@@ -149,20 +149,18 @@
184 self.assertEqual(expected, result)
185
186
187-class TestTeamFormatterAPI(TestCaseWithFactory):
188- """ Test permissions required to access TeamFormatterAPI methods.
189+class TestTalesFormatterAPI(TestCaseWithFactory):
190+ """ Test permissions required to access TalesFormatterAPI methods.
191
192 A user must have launchpad.LimitedView permission to use
193- TeamFormatterAPI with private teams.
194+ TestTalesFormatterAPI with private teams.
195 """
196- layer = LaunchpadFunctionalLayer
197+ layer = DatabaseFunctionalLayer
198
199 def setUp(self):
200- super(TestTeamFormatterAPI, self).setUp()
201- icon = self.factory.makeLibraryFileAlias(
202- filename='smurf.png', content_type='image/png')
203+ super(TestTalesFormatterAPI, self).setUp()
204 self.team = self.factory.makeTeam(
205- name='team', displayname='a team', icon=icon,
206+ name='team', displayname='a team',
207 visibility=PersonVisibility.PRIVATE)
208
209 def _make_formatter(self, cache_permission=False):
210@@ -177,11 +175,10 @@
211 request, 'launchpad.LimitedView', [self.team])
212 return formatter, request, any_person
213
214- def _tales_value(self, attr, request, path='fmt'):
215+ def _tales_value(self, attr, request):
216 # Evaluate the given formatted attribute value on team.
217- result = test_tales(
218- "team/%s:%s" % (path, attr), team=self.team, request=request)
219- return result
220+ return test_tales(
221+ "team/fmt:%s" % attr, team=self.team, request=request)
222
223 def _test_can_view_attribute_no_login(self, attr, hidden=None):
224 # Test attribute access with no login.
225@@ -231,10 +228,6 @@
226 def test_can_view_url(self):
227 self._test_can_view_attribute('url')
228
229- def test_can_view_icon(self):
230- self._test_can_view_attribute(
231- 'icon', '<span class="sprite team"></span>')
232-
233
234 class TestObjectFormatterAPI(TestCaseWithFactory):
235 """Tests for ObjectFormatterAPI"""
236
237=== modified file 'lib/lp/bugs/browser/tests/test_bugsubscription_views.py'
238--- lib/lp/bugs/browser/tests/test_bugsubscription_views.py 2011-12-12 04:47:21 +0000
239+++ lib/lp/bugs/browser/tests/test_bugsubscription_views.py 2011-12-13 14:07:37 +0000
240@@ -629,7 +629,7 @@
241 direct_subscriber = self.factory.makeTeam(
242 name='team', displayname='Team Name', owner=teamowner,
243 visibility=PersonVisibility.PRIVATE)
244- with person_logged_in(teamowner):
245+ with person_logged_in(direct_subscriber.teamowner):
246 bug.subscribe(direct_subscriber, direct_subscriber.teamowner,
247 level=BugNotificationLevel.LIFECYCLE)
248
249
250=== modified file 'lib/lp/code/browser/branch.py'
251--- lib/lp/code/browser/branch.py 2011-12-01 11:39:30 +0000
252+++ lib/lp/code/browser/branch.py 2011-12-13 14:07:37 +0000
253@@ -96,10 +96,7 @@
254 stepthrough,
255 stepto,
256 )
257-from canonical.launchpad.webapp.authorization import (
258- check_permission,
259- precache_permission_for_objects,
260- )
261+from canonical.launchpad.webapp.authorization import check_permission
262 from canonical.launchpad.webapp.interfaces import ICanonicalUrlData
263 from canonical.launchpad.webapp.menu import structured
264 from lp.app.browser.launchpad import Hierarchy
265@@ -445,11 +442,6 @@
266 def initialize(self):
267 self.branch = self.context
268 self.notices = []
269- # Cache permission so private team owner can be rendered.
270- authorised_people = [self.branch.owner]
271- if self.user is not None:
272- precache_permission_for_objects(
273- self.request, "launchpad.LimitedView", authorised_people)
274 # Replace our context with a decorated branch, if it is not already
275 # decorated.
276 if not isinstance(self.context, DecoratedBranch):
277
278=== modified file 'lib/lp/code/browser/branchsubscription.py'
279--- lib/lp/code/browser/branchsubscription.py 2011-12-01 11:27:29 +0000
280+++ lib/lp/code/browser/branchsubscription.py 2011-12-13 14:07:37 +0000
281@@ -19,10 +19,7 @@
282 canonical_url,
283 LaunchpadView,
284 )
285-from canonical.launchpad.webapp.authorization import (
286- check_permission,
287- precache_permission_for_objects,
288- )
289+from canonical.launchpad.webapp.authorization import check_permission
290 from canonical.launchpad.webapp.interfaces import IPrimaryContext
291 from canonical.launchpad.webapp.menu import structured
292 from lp.app.browser.launchpadform import (
293@@ -51,18 +48,9 @@
294
295 def subscriptions(self):
296 """Return a decorated list of branch subscriptions."""
297-
298- # Cache permissions so private subscribers can be rendered.
299- if self.user is not None:
300- subscribers = [
301- subscription.person
302- for subscription in self.context.subscriptions]
303- precache_permission_for_objects(
304- self.request, "launchpad.LimitedView", subscribers)
305-
306 visible_subscriptions = [
307 subscription for subscription in self.context.subscriptions
308- if check_permission('launchpad.LimitedView', subscription.person)]
309+ if check_permission('launchpad.View', subscription.person)]
310 return sorted(
311 visible_subscriptions,
312 key=lambda subscription: subscription.person.displayname)
313
314=== modified file 'lib/lp/code/browser/tests/test_branch.py'
315--- lib/lp/code/browser/tests/test_branch.py 2011-12-12 18:22:50 +0000
316+++ lib/lp/code/browser/tests/test_branch.py 2011-12-13 14:07:37 +0000
317@@ -5,14 +5,12 @@
318
319 __metaclass__ = type
320
321-from BeautifulSoup import BeautifulSoup
322 from datetime import (
323 datetime,
324 )
325 from textwrap import dedent
326
327 import pytz
328-from zope.publisher.interfaces import NotFound
329 from zope.security.proxy import removeSecurityProxy
330
331 from canonical.config import config
332@@ -28,7 +26,6 @@
333 extract_text,
334 find_tag_by_id,
335 setupBrowser,
336- setupBrowserForUser
337 )
338 from lp.app.interfaces.headings import IRootContext
339 from lp.bugs.interfaces.bugtask import (
340@@ -53,7 +50,6 @@
341 BranchVisibilityRule,
342 )
343 from lp.code.interfaces.branchtarget import IBranchTarget
344-from lp.registry.interfaces.person import PersonVisibility
345 from lp.testing import (
346 BrowserTestCase,
347 login,
348@@ -545,102 +541,6 @@
349 self.assertEqual(linked_bug_urls[1], links[4]['href'])
350
351
352-class TestBranchViewPrivateArtifacts(BrowserTestCase):
353- """ Tests that branches with private team artifacts can be viewed.
354-
355- A Branch may be associated with a private team as follows:
356- - the owner is a private team
357- - a subscriber is a private team
358-
359- A logged in user who is not authorised to see the private team(s) still
360- needs to be able to view the branch. The private team will be rendered in
361- the normal way, displaying the team name and Launchpad URL.
362- """
363-
364- layer = DatabaseFunctionalLayer
365-
366- def _getBrowser(self, user=None):
367- if user is None:
368- browser = setupBrowser()
369- logout()
370- return browser
371- else:
372- login_person(user)
373- return setupBrowserForUser(user=user)
374-
375- def test_view_branch_with_private_owner(self):
376- # A branch with a private owner is rendered.
377- private_owner = self.factory.makeTeam(
378- displayname="PrivateTeam", visibility=PersonVisibility.PRIVATE)
379- branch = self.factory.makeAnyBranch(owner=private_owner)
380- # Ensure the branch owner is rendered.
381- url = canonical_url(branch, rootsite='code')
382- user = self.factory.makePerson()
383- browser = self._getBrowser(user)
384- browser.open(url)
385- soup = BeautifulSoup(browser.contents)
386- self.assertIsNotNone(soup.find('a', text="PrivateTeam"))
387-
388- def test_view_private_branch_with_private_owner(self):
389- # A private branch with a private owner is rendered.
390- private_owner = self.factory.makeTeam(
391- displayname="PrivateTeam", visibility=PersonVisibility.PRIVATE)
392- branch = self.factory.makeAnyBranch(owner=private_owner)
393- # Ensure the branch owner is rendered.
394- url = canonical_url(branch, rootsite='code')
395- user = self.factory.makePerson()
396- # Subscribe the user so they can see the branch.
397- with person_logged_in(private_owner):
398- self.factory.makeBranchSubscription(branch, user, private_owner)
399- browser = self._getBrowser(user)
400- browser.open(url)
401- soup = BeautifulSoup(browser.contents)
402- self.assertIsNotNone(soup.find('a', text="PrivateTeam"))
403-
404- def test_anonymous_view_branch_with_private_owner(self):
405- # A branch with a private owner is not rendered for anon users.
406- private_owner = self.factory.makeTeam(
407- visibility=PersonVisibility.PRIVATE)
408- branch = self.factory.makeAnyBranch(owner=private_owner)
409- # Viewing the branch results in an error.
410- url = canonical_url(branch, rootsite='code')
411- browser = self._getBrowser()
412- self.assertRaises(NotFound, browser.open, url)
413-
414- def test_view_branch_with_private_subscriber(self):
415- # A branch with a private subscriber is rendered.
416- private_subscriber = self.factory.makeTeam(
417- name="privateteam", visibility=PersonVisibility.PRIVATE)
418- branch = self.factory.makeAnyBranch()
419- with person_logged_in(branch.owner):
420- self.factory.makeBranchSubscription(
421- branch, private_subscriber, branch.owner)
422- # Ensure the branch subscriber is rendered.
423- url = canonical_url(branch, rootsite='code')
424- user = self.factory.makePerson()
425- browser = self._getBrowser(user)
426- browser.open(url)
427- soup = BeautifulSoup(browser.contents)
428- self.assertIsNotNone(
429- soup.find('div', attrs={'id': 'subscriber-privateteam'}))
430-
431- def test_anonymous_view_branch_with_private_subscriber(self):
432- # A branch with a private subscriber is not rendered for anon users.
433- private_subscriber = self.factory.makeTeam(
434- name="privateteam", visibility=PersonVisibility.PRIVATE)
435- branch = self.factory.makeAnyBranch()
436- with person_logged_in(branch.owner):
437- self.factory.makeBranchSubscription(
438- branch, private_subscriber, branch.owner)
439- # Viewing the branch results in an error.
440- url = canonical_url(branch, rootsite='code')
441- browser = self._getBrowser()
442- browser.open(url)
443- soup = BeautifulSoup(browser.contents)
444- self.assertIsNone(
445- soup.find('div', attrs={'id': 'subscriber-privateteam'}))
446-
447-
448 class TestBranchAddView(TestCaseWithFactory):
449 """Test the BranchAddView view."""
450
451
452=== modified file 'lib/lp/code/browser/tests/test_branchlisting.py'
453--- lib/lp/code/browser/tests/test_branchlisting.py 2011-12-12 04:47:21 +0000
454+++ lib/lp/code/browser/tests/test_branchlisting.py 2011-12-13 14:07:37 +0000
455@@ -613,14 +613,13 @@
456 layer = DatabaseFunctionalLayer
457
458 def _make_branch_for_private_team(self):
459- owner = self.factory.makePerson()
460 private_team = self.factory.makeTeam(
461- name='shh', displayname='Shh', owner=owner,
462+ name='shh', displayname='Shh',
463 visibility=PersonVisibility.PRIVATE)
464 member = self.factory.makePerson(
465 email='member@example.com', password='test')
466- with person_logged_in(owner):
467- private_team.addMember(member, owner)
468+ with person_logged_in(private_team.teamowner):
469+ private_team.addMember(member, private_team.teamowner)
470 branch = self.factory.makeProductBranch(owner=private_team)
471 return private_team, member, branch
472
473
474=== modified file 'lib/lp/code/stories/branches/xx-subscribing-branches.txt'
475--- lib/lp/code/stories/branches/xx-subscribing-branches.txt 2011-12-12 18:29:00 +0000
476+++ lib/lp/code/stories/branches/xx-subscribing-branches.txt 2011-12-13 14:07:37 +0000
477@@ -254,19 +254,15 @@
478 Mark Shuttleworth
479
480
481-Private team's in public subscriptions
482-======================================
483-
484-If a private team is subscribed to a publicthe branch, it is visible
485-to everyone.
486-
487- >>> from lp.testing import login, logout
488+Private team exclusion
489+======================
490+
491+If a private team is subscribed to the branch, it is excluded from the list of
492+subscribers if the user is not an admin or a member of the team.
493+
494+ >>> from lp.testing import ANONYMOUS, login, logout
495+ >>> login('admin@canonical.com')
496 >>> from lp.registry.interfaces.person import PersonVisibility
497- >>> from lp.code.enums import (
498- ... BranchSubscriptionNotificationLevel,
499- ... CodeReviewNotificationLevel)
500-
501- >>> login('admin@canonical.com')
502 >>> private_team = factory.makeTeam(
503 ... name='shh', displayname='Shh',
504 ... visibility=PersonVisibility.PRIVATE)
505@@ -275,16 +271,26 @@
506 >>> ignored = private_team.addMember(member, private_team.teamowner)
507 >>> owner = factory.makePerson(name='branch-owner')
508 >>> branch = factory.makeAnyBranch(owner=owner)
509+ >>> from lp.code.enums import (
510+ ... BranchSubscriptionNotificationLevel,
511+ ... CodeReviewNotificationLevel)
512 >>> ignored = branch.subscribe(
513 ... private_team, BranchSubscriptionNotificationLevel.NOEMAIL, None,
514 ... CodeReviewNotificationLevel.NOEMAIL, private_team.teamowner)
515 >>> url = canonical_url(branch)
516 >>> logout()
517
518-No-priv is not a member of the private team, but he can see the team's
519-display name in the subscriber list.
520+No-priv is not a member of the private team, so the private team is not shown
521+as a subscriber.
522
523 >>> browser.open(url)
524 >>> print_subscribers(browser.contents)
525 Branch-owner
526+
527+A team member looking at the branch will see the private team subscribed.
528+
529+ >>> private_browser = setupBrowser(auth='Basic shh@example.com:test')
530+ >>> private_browser.open(url)
531+ >>> print_subscribers(private_browser.contents)
532+ Branch-owner
533 Shh
534
535=== modified file 'lib/lp/registry/browser/tests/test_mailinglists.py'
536--- lib/lp/registry/browser/tests/test_mailinglists.py 2011-12-12 04:47:21 +0000
537+++ lib/lp/registry/browser/tests/test_mailinglists.py 2011-12-13 14:07:37 +0000
538@@ -28,12 +28,11 @@
539 """Verify the content in +mailing-list-portlet."""
540
541 def makeTeamWithMailingList(self, name=None, owner=None, visibility=None):
542- if owner is None:
543- owner = self.factory.makePerson()
544 team = self.factory.makeTeam(
545 name=name, owner=owner, visibility=visibility)
546- login_person(owner)
547- self.factory.makeMailingList(team=team, owner=owner)
548+ login_person(team.teamowner)
549+ team_list = self.factory.makeMailingList(
550+ team=team, owner=team.teamowner)
551 return team
552
553
554@@ -130,11 +129,10 @@
555
556 def test_private_message_message_without_list(self):
557 # Private teams have private archives.
558- owner = self.factory.makePerson()
559 team = self.factory.makeTeam(
560- owner=owner, visibility=PersonVisibility.PRIVATE)
561- login_person(owner)
562+ visibility=PersonVisibility.PRIVATE)
563+ login_person(team.teamowner)
564 view = create_initialized_view(
565- team, name='+mailinglist', principal=owner)
566+ team, name='+mailinglist', principal=team.teamowner)
567 element = find_tag_by_id(view(), 'mailing-list-archive')
568 self.assertEqual('private', extract_text(element))
569
570=== modified file 'lib/lp/registry/browser/tests/test_person_view.py'
571--- lib/lp/registry/browser/tests/test_person_view.py 2011-12-13 10:35:57 +0000
572+++ lib/lp/registry/browser/tests/test_person_view.py 2011-12-13 14:07:37 +0000
573@@ -534,11 +534,9 @@
574
575 def test_active_participations_with_direct_private_team(self):
576 # Users cannot see private teams that they are not members of.
577- owner = self.factory.makePerson()
578- team = self.factory.makeTeam(
579- owner=owner, visibility=PersonVisibility.PRIVATE)
580- login_person(owner)
581- team.addMember(self.user, owner)
582+ team = self.factory.makeTeam(visibility=PersonVisibility.PRIVATE)
583+ login_person(team.teamowner)
584+ team.addMember(self.user, team.teamowner)
585 # The team is included in active_participations.
586 login_person(self.user)
587 view = create_view(
588@@ -553,13 +551,11 @@
589
590 def test_active_participations_with_indirect_private_team(self):
591 # Users cannot see private teams that they are not members of.
592- owner = self.factory.makePerson()
593- team = self.factory.makeTeam(
594- owner=owner, visibility=PersonVisibility.PRIVATE)
595- direct_team = self.factory.makeTeam(owner=owner)
596- login_person(owner)
597- direct_team.addMember(self.user, owner)
598- team.addMember(direct_team, owner)
599+ team = self.factory.makeTeam(visibility=PersonVisibility.PRIVATE)
600+ direct_team = self.factory.makeTeam(owner=team.teamowner)
601+ login_person(team.teamowner)
602+ direct_team.addMember(self.user, team.teamowner)
603+ team.addMember(direct_team, team.teamowner)
604 # The team is included in active_participations.
605 login_person(self.user)
606 view = create_view(
607
608=== modified file 'lib/lp/registry/configure.zcml'
609--- lib/lp/registry/configure.zcml 2011-12-12 04:47:21 +0000
610+++ lib/lp/registry/configure.zcml 2011-12-13 14:07:37 +0000
611@@ -912,6 +912,8 @@
612 <allow
613 interface="lp.registry.interfaces.person.IPersonPublic"/>
614 <allow
615+ interface="lp.registry.interfaces.person.ITeamPublic"/>
616+ <allow
617 interface="lp.registry.interfaces.person.IHasStanding"/>
618 <allow
619 interface="lp.registry.interfaces.person.IPersonCommAdminWriteRestricted"/>
620@@ -922,9 +924,6 @@
621 permission="launchpad.View"
622 interface="lp.registry.interfaces.person.IPersonViewRestricted"/>
623 <require
624- permission="launchpad.View"
625- interface="lp.registry.interfaces.person.ITeamPublic"/>
626- <require
627 permission="launchpad.Edit"
628 interface="lp.registry.interfaces.location.ISetLocation"/>
629 <require
630@@ -932,10 +931,9 @@
631 interface="lp.registry.interfaces.person.IPersonEditRestricted"/>
632 <require
633 permission="launchpad.Edit"
634- set_schema="lp.registry.interfaces.person.IPersonViewRestricted
635- lp.registry.interfaces.person.IPersonPublic
636+ set_schema="lp.registry.interfaces.person.IPersonPublic
637 lp.registry.interfaces.person.ITeamPublic"
638- set_attributes="displayname icon logo"/>
639+ set_attributes="displayname"/>
640 <require
641 permission="launchpad.Moderate"
642 set_attributes="name"/>
643
644=== modified file 'lib/lp/registry/doc/person.txt'
645--- lib/lp/registry/doc/person.txt 2011-12-12 04:47:21 +0000
646+++ lib/lp/registry/doc/person.txt 2011-12-13 14:07:37 +0000
647@@ -696,7 +696,6 @@
648 >>> private_team = factory.makeTeam(salgado, name='private-team',
649 ... displayname='Private Team',
650 ... visibility=PersonVisibility.PRIVATE)
651- >>> private_team_owner = private_team.teamowner
652 >>> bug_subscription = BugSubscription(bug=bug, person=private_team,
653 ... subscribed_by=guadamen)
654
655@@ -807,7 +806,7 @@
656
657 But Owner, a member of that team, will see it in the results.
658
659- >>> login_person(private_team_owner)
660+ >>> login_person(private_team.teamowner)
661 >>> print_people(personset.find('team'))
662 Another a new team (new3): []
663 Hoary Gnome Team (name21): []
664
665=== modified file 'lib/lp/registry/doc/private-team-visibility.txt'
666--- lib/lp/registry/doc/private-team-visibility.txt 2011-12-12 04:47:21 +0000
667+++ lib/lp/registry/doc/private-team-visibility.txt 2011-12-13 14:07:37 +0000
668@@ -107,64 +107,3 @@
669 ...
670 Unauthorized: (<Person at ... priv-team (Priv Team)>,
671 'name', 'launchpad.LimitedView')
672-
673-A person with visibility to any of the branches owned by the private team will
674-be granted limited view permission on the team.
675-
676-For private branches, a user needs to be subscribed to the branch for the
677-branch (and hence team) to be visible.
678-
679- >>> login_person(priv_owner)
680- >>> private_team_branch = factory.makeBranch(
681- ... owner=priv_team, private=True)
682- >>> login_person(pub_member)
683- >>> check_permission('launchpad.LimitedView', priv_team)
684- False
685-
686- >>> login_person(priv_owner)
687- >>> sub = factory.makeBranchSubscription(
688- ... branch=private_team_branch, person=pub_member,
689- ... subscribed_by=priv_owner)
690-
691- >>> login_person(pub_member)
692- >>> check_permission('launchpad.LimitedView', priv_team)
693- True
694-
695-Subscribers to the teams private PPA have limited view permission.
696-
697- >>> login_person(priv_owner)
698- >>> archive = factory.makeArchive(private=True, owner=priv_team)
699- >>> archive_subscriber = factory.makePerson()
700- >>> sub = archive.newSubscription(
701- ... archive_subscriber, registrant=archive.owner)
702-
703- >>> login_person(archive_subscriber)
704- >>> check_permission('launchpad.LimitedView', priv_team)
705- True
706-
707-Users with LimitedView can know identifying information like name,
708-displayname, and unique_name, but cannot know other information like
709-teamowner.
710-
711- >>> print priv_team.name
712- priv-team
713-
714- >>> print priv_team.displayname
715- Priv Team
716-
717- >>> print priv_team.unique_displayname
718- Priv Team (priv-team)
719-
720- >>> print priv_team.icon
721- None
722-
723- >>> print priv_team.allmembers
724- Traceback (most recent call last):
725- ...
726- Unauthorized: (<Person at ... priv-team (Priv Team)>,
727- 'allmembers', 'launchpad.View')
728-
729-Anonymous users do not have permission.
730- >>> login(ANONYMOUS)
731- >>> check_permission('launchpad.LimitedView', priv_team)
732- False
733
734=== modified file 'lib/lp/registry/interfaces/person.py'
735--- lib/lp/registry/interfaces/person.py 2011-12-13 00:32:04 +0000
736+++ lib/lp/registry/interfaces/person.py 2011-12-13 14:07:37 +0000
737@@ -15,7 +15,7 @@
738 'IObjectReassignment',
739 'IPerson',
740 'IPersonClaim',
741- 'IPersonPublic',
742+ 'IPersonPublic', # Required for a monkey patch in interfaces/archive.py
743 'IPersonSet',
744 'IPersonSettings',
745 'ISoftwareCenterAgentAPI',
746@@ -657,61 +657,26 @@
747 required=False, default=False)
748
749
750-class IPersonPublic(IPrivacy):
751- """Public attributes for a Person.
752-
753- Very few attributes on a person can be public because private teams
754- are also persons. The public attributes are generally information
755- needed by the system to determine if the principal in the current
756- interaction can work with the object.
757- """
758+class IPersonPublic(IHasBranches, IHasSpecifications,
759+ IHasMergeProposals, IHasLogo, IHasMugshot, IHasIcon,
760+ IHasLocation, IHasRequestedReviews, IObjectWithLocation,
761+ IPrivacy, IHasBugs, IHasRecipes, IHasTranslationImports,
762+ IPersonSettings, IQuestionsPerson):
763+ """Public attributes for a Person."""
764
765 id = Int(title=_('ID'), required=True, readonly=True)
766- # This is redefined from IPrivacy.private because the attribute is
767- # read-only. It is a summary of the team's visibility.
768- private = exported(Bool(
769- title=_("This team is private"),
770- readonly=True, required=False,
771- description=_("Private teams are visible only to "
772- "their members.")))
773- is_valid_person = Bool(
774- title=_("This is an active user and not a team."), readonly=True)
775- is_valid_person_or_team = exported(
776- Bool(title=_("This is an active user or a team."), readonly=True),
777- exported_as='is_valid')
778- is_merge_pending = exported(Bool(
779- title=_("Is this person due to be merged with another?"),
780- required=False, default=False))
781- is_team = exported(
782- Bool(title=_('Is this object a team?'), readonly=True))
783- account_status = Choice(
784- title=_("The status of this person's account"), required=False,
785- readonly=True, vocabulary=AccountStatus)
786- account_status_comment = Text(
787- title=_("Why are you deactivating your account?"), required=False,
788- readonly=True)
789-
790-
791-class IPersonLimitedView(IHasIcon, IHasLogo):
792- """IPerson attributes that require launchpad.LimitedView permission."""
793-
794- name = exported(
795- PersonNameField(
796- title=_('Name'), required=True, readonly=False,
797- constraint=name_validator,
798- description=_(
799- "A short unique name, beginning with a lower-case "
800- "letter or number, and containing only letters, "
801- "numbers, dots, hyphens, or plus signs.")))
802- displayname = exported(
803- StrippedTextLine(
804- title=_('Display Name'), required=True, readonly=False,
805- description=_(
806- "Your name as you would like it displayed throughout "
807- "Launchpad. Most people use their full name here.")),
808- exported_as='display_name')
809- unique_displayname = TextLine(
810- title=_('Return a string of the form $displayname ($name).'))
811+ account = Object(schema=IAccount)
812+ accountID = Int(title=_('Account ID'), required=True, readonly=True)
813+ password = PasswordField(
814+ title=_('Password'), required=True, readonly=False)
815+ karma = exported(
816+ Int(title=_('Karma'), readonly=True,
817+ description=_('The cached total karma for this person.')))
818+ homepage_content = exported(
819+ Text(title=_("Homepage Content"), required=False,
820+ description=_(
821+ "The content of your profile page. Use plain text, "
822+ "paragraphs are preserved and URLs are linked in pages.")))
823 # NB at this stage we do not allow individual people to have their own
824 # icon, only teams get that. People can however have a logo and mugshot
825 # The icon is only used for teams; that's why we use /@@/team as the
826@@ -725,6 +690,7 @@
827 "displayed whenever the team name is listed - for example "
828 "in listings of bugs or on a person's membership table."))
829 iconID = Int(title=_('Icon ID'), required=True, readonly=True)
830+
831 logo = exported(
832 LogoImageUpload(
833 title=_("Logo"), required=False,
834@@ -735,30 +701,6 @@
835 "is a logo, a small picture or a personal mascot. It should "
836 "be no bigger than 50kb in size.")))
837 logoID = Int(title=_('Logo ID'), required=True, readonly=True)
838- # title is required for the Launchpad Page Layout main template
839- title = Attribute('Person Page Title')
840- is_probationary = exported(
841- Bool(title=_("Is this a probationary user?"), readonly=True))
842-
843-
844-class IPersonViewRestricted(IHasBranches, IHasSpecifications,
845- IHasMergeProposals, IHasMugshot,
846- IHasLocation, IHasRequestedReviews, IObjectWithLocation,
847- IHasBugs, IHasRecipes, IHasTranslationImports,
848- IPersonSettings, IQuestionsPerson):
849- """IPerson attributes that require launchpad.View permission."""
850- account = Object(schema=IAccount)
851- accountID = Int(title=_('Account ID'), required=True, readonly=True)
852- password = PasswordField(
853- title=_('Password'), required=True, readonly=False)
854- karma = exported(
855- Int(title=_('Karma'), readonly=True,
856- description=_('The cached total karma for this person.')))
857- homepage_content = exported(
858- Text(title=_("Homepage Content"), required=False,
859- description=_(
860- "The content of your profile page. Use plain text, "
861- "paragraphs are preserved and URLs are linked in pages.")))
862
863 mugshot = exported(MugshotImageUpload(
864 title=_("Mugshot"), required=False,
865@@ -813,9 +755,26 @@
866 readonly=False, required=False,
867 value_type=Reference(schema=ISSHKey)))
868
869+ account_status = Choice(
870+ title=_("The status of this person's account"), required=False,
871+ readonly=True, vocabulary=AccountStatus)
872+
873+ account_status_comment = Text(
874+ title=_("Why are you deactivating your account?"), required=False,
875+ readonly=True)
876+
877 # Properties of the Person object.
878 karma_category_caches = Attribute(
879 'The caches of karma scores, by karma category.')
880+ is_team = exported(
881+ Bool(title=_('Is this object a team?'), readonly=True))
882+ is_valid_person = Bool(
883+ title=_("This is an active user and not a team."), readonly=True)
884+ is_valid_person_or_team = exported(
885+ Bool(title=_("This is an active user or a team."), readonly=True),
886+ exported_as='is_valid')
887+ is_probationary = exported(
888+ Bool(title=_("Is this a probationary user?"), readonly=True))
889 is_ubuntu_coc_signer = exported(
890 Bool(title=_("Signed Ubuntu Code of Conduct"),
891 readonly=True))
892@@ -920,6 +879,7 @@
893 exported_as='team_owner')
894 teamownerID = Int(title=_("The Team Owner's ID or None"), required=False,
895 readonly=True)
896+
897 preferredemail = exported(
898 Reference(title=_("Preferred email address"),
899 description=_("The preferred email address for this person. "
900@@ -956,6 +916,9 @@
901 "this is set to None, then this Person has not been merged "
902 "into another and is still valid"))
903
904+ # title is required for the Launchpad Page Layout main template
905+ title = Attribute('Person Page Title')
906+
907 archive = exported(
908 Reference(
909 title=_("Default PPA"),
910@@ -1024,6 +987,18 @@
911 readonly=True, required=False,
912 value_type=Reference(schema=Interface))) # HWSubmission
913
914+ # This is redefined from IPrivacy.private because the attribute is
915+ # read-only. It is a summary of the team's visibility.
916+ private = exported(Bool(
917+ title=_("This team is private"),
918+ readonly=True, required=False,
919+ description=_("Private teams are visible only to "
920+ "their members.")))
921+
922+ is_merge_pending = exported(Bool(
923+ title=_("Is this person due to be merged with another?"),
924+ required=False, default=False))
925+
926 administrated_teams = Attribute(
927 u"the teams that this person/team is an administrator of.")
928
929@@ -1488,6 +1463,32 @@
930 :return: a boolean.
931 """
932
933+
934+class IPersonLimitedView(Interface):
935+ """IPerson attributes that require launchpad.LimitedView permission."""
936+
937+ name = exported(
938+ PersonNameField(
939+ title=_('Name'), required=True, readonly=False,
940+ constraint=name_validator,
941+ description=_(
942+ "A short unique name, beginning with a lower-case "
943+ "letter or number, and containing only letters, "
944+ "numbers, dots, hyphens, or plus signs.")))
945+ displayname = exported(
946+ StrippedTextLine(
947+ title=_('Display Name'), required=True, readonly=False,
948+ description=_(
949+ "Your name as you would like it displayed throughout "
950+ "Launchpad. Most people use their full name here.")),
951+ exported_as='display_name')
952+ unique_displayname = TextLine(
953+ title=_('Return a string of the form $displayname ($name).'))
954+
955+
956+class IPersonViewRestricted(Interface):
957+ """IPerson attributes that require launchpad.View permission."""
958+
959 active_member_count = Attribute(
960 "The number of real people who are members of this team.")
961 # activemembers.value_type.schema will be set to IPerson once
962@@ -2564,19 +2565,18 @@
963 ]:
964 IPersonViewRestricted[name].value_type.schema = IPerson
965
966-IPersonViewRestricted['sub_teams'].value_type.schema = ITeam
967-IPersonViewRestricted['super_teams'].value_type.schema = ITeam
968+IPersonPublic['sub_teams'].value_type.schema = ITeam
969+IPersonPublic['super_teams'].value_type.schema = ITeam
970 # XXX: salgado, 2008-08-01: Uncomment these when teams_*participated_in are
971 # exported again.
972-# IPersonViewRestricted['teams_participated_in'].value_type.schema = ITeam
973-# IPersonViewRestricted[
974-# 'teams_indirectly_participated_in'].value_type.schema = ITeam
975+# IPersonPublic['teams_participated_in'].value_type.schema = ITeam
976+# IPersonPublic['teams_indirectly_participated_in'].value_type.schema = ITeam
977
978 # Fix schema of operation parameters. We need zope.deferredimport!
979 params_to_fix = [
980 # XXX: salgado, 2008-08-01: Uncomment these when they are exported again.
981- # (IPersonViewRestricted['findPathToTeam'], 'team'),
982- # (IPersonViewRestricted['inTeam'], 'team'),
983+ # (IPersonPublic['findPathToTeam'], 'team'),
984+ # (IPersonPublic['inTeam'], 'team'),
985 (IPersonEditRestricted['join'], 'team'),
986 (IPersonEditRestricted['leave'], 'team'),
987 (IPersonEditRestricted['addMember'], 'person'),
988
989=== modified file 'lib/lp/registry/interfaces/role.py'
990--- lib/lp/registry/interfaces/role.py 2011-12-12 04:47:21 +0000
991+++ lib/lp/registry/interfaces/role.py 2011-12-13 14:07:37 +0000
992@@ -119,8 +119,7 @@
993 def inTeam(team):
994 """Is this person a member or the owner of `team`?
995
996- Passed through to the *unproxied* same method in
997- `IPersonViewRestricted`.
998+ Passed through to the same method in 'IPersonPublic'.
999 """
1000
1001 def isOwner(obj):
1002
1003=== modified file 'lib/lp/registry/model/personroles.py'
1004--- lib/lp/registry/model/personroles.py 2011-12-12 04:47:21 +0000
1005+++ lib/lp/registry/model/personroles.py 2011-12-13 14:07:37 +0000
1006@@ -11,7 +11,6 @@
1007 getUtility,
1008 )
1009 from zope.interface import implements
1010-from zope.security.proxy import removeSecurityProxy
1011
1012 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
1013 from lp.bugs.interfaces.bugsupervisor import IHasBugSupervisor
1014@@ -30,8 +29,7 @@
1015 def __init__(self, person):
1016 self.person = person
1017 self._celebrities = getUtility(ILaunchpadCelebrities)
1018- # Use an unproxied inTeam() method for security checks.
1019- self.inTeam = removeSecurityProxy(self.person).inTeam
1020+ self.inTeam = self.person.inTeam
1021
1022 def __getattr__(self, name):
1023 """Handle all in_* attributes."""
1024@@ -41,7 +39,7 @@
1025 raise AttributeError(errortext)
1026 attribute = name[len(prefix):]
1027 try:
1028- return self.inTeam(getattr(self._celebrities, attribute))
1029+ return self.person.inTeam(getattr(self._celebrities, attribute))
1030 except AttributeError:
1031 raise AttributeError(errortext)
1032
1033@@ -51,28 +49,28 @@
1034
1035 def isOwner(self, obj):
1036 """See IPersonRoles."""
1037- return self.inTeam(obj.owner)
1038+ return self.person.inTeam(obj.owner)
1039
1040 def isBugSupervisor(self, obj):
1041 """See IPersonRoles."""
1042 return (IHasBugSupervisor.providedBy(obj)
1043- and self.inTeam(obj.bug_supervisor))
1044+ and self.person.inTeam(obj.bug_supervisor))
1045
1046 def isSecurityContact(self, obj):
1047 """See IPersonRoles."""
1048 return (IHasSecurityContact.providedBy(obj)
1049- and self.inTeam(obj.security_contact))
1050+ and self.person.inTeam(obj.security_contact))
1051
1052 def isDriver(self, obj):
1053 """See IPersonRoles."""
1054- return self.inTeam(obj.driver)
1055+ return self.person.inTeam(obj.driver)
1056
1057 def isOneOfDrivers(self, obj):
1058 """See IPersonRoles."""
1059 if not IHasDrivers.providedBy(obj):
1060 return self.isDriver(obj)
1061 for driver in obj.drivers:
1062- if self.inTeam(driver):
1063+ if self.person.inTeam(driver):
1064 return True
1065 return False
1066
1067@@ -80,6 +78,6 @@
1068 """See IPersonRoles."""
1069 for attr in attributes:
1070 role = getattr(obj, attr)
1071- if self.inTeam(role):
1072+ if self.person.inTeam(role):
1073 return True
1074 return False
1075
1076=== modified file 'lib/lp/registry/stories/webservice/xx-derivedistroseries.txt'
1077--- lib/lp/registry/stories/webservice/xx-derivedistroseries.txt 2011-12-12 04:47:21 +0000
1078+++ lib/lp/registry/stories/webservice/xx-derivedistroseries.txt 2011-12-13 14:07:37 +0000
1079@@ -19,7 +19,6 @@
1080 >>> soyuz_team = factory.makeTeam(
1081 ... name='soyuz-team',
1082 ... subscription_policy=TeamSubscriptionPolicy.RESTRICTED)
1083- >>> soyuz_team_owner = soyuz_team.teamowner
1084 >>> parent_series = factory.makeDistroSeries(name="parentseries")
1085 >>> arch = factory.makeDistroArchSeries(distroseries=parent_series)
1086 >>> removeSecurityProxy(parent_series).nominatedarchindep = arch
1087@@ -42,7 +41,7 @@
1088 >>> logout()
1089
1090 >>> soyuz_team_webservice = webservice_for_person(
1091- ... soyuz_team_owner, permission=OAuthPermission.WRITE_PUBLIC)
1092+ ... soyuz_team.teamowner, permission=OAuthPermission.WRITE_PUBLIC)
1093
1094
1095 Calling
1096
1097=== modified file 'lib/lp/registry/tests/test_person_vocabularies.py'
1098--- lib/lp/registry/tests/test_person_vocabularies.py 2011-12-12 04:47:21 +0000
1099+++ lib/lp/registry/tests/test_person_vocabularies.py 2011-12-13 14:07:37 +0000
1100@@ -311,10 +311,9 @@
1101 def test_private_team_cannot_be_a_member_of_itself(self):
1102 # A private team should be filtered by the vocab.extra_clause
1103 # when provided a search term.
1104- owner = self.factory.makePerson()
1105 team = self.factory.makeTeam(
1106- owner=owner, visibility=PersonVisibility.PRIVATE)
1107- login_person(owner)
1108+ visibility=PersonVisibility.PRIVATE)
1109+ login_person(team.teamowner)
1110 self.assertNotIn(team, self.searchVocabulary(team, team.name))
1111
1112
1113
1114=== modified file 'lib/lp/registry/tests/test_team_webservice.py'
1115--- lib/lp/registry/tests/test_team_webservice.py 2011-12-12 04:47:21 +0000
1116+++ lib/lp/registry/tests/test_team_webservice.py 2011-12-13 14:07:37 +0000
1117@@ -5,6 +5,8 @@
1118
1119 import httplib
1120
1121+from zope.security.proxy import removeSecurityProxy
1122+
1123 from lazr.restfulclient.errors import HTTPError
1124
1125 from canonical.testing.layers import DatabaseFunctionalLayer
1126@@ -77,9 +79,8 @@
1127 # Calling person.join with a team that has an open membership
1128 # subscription policy should add that that user to the team.
1129 self.person = self.factory.makePerson(name='test-person')
1130- owner = self.factory.makePerson()
1131- self.team = self.factory.makeTeam(name='test-team', owner=owner)
1132- login_person(owner)
1133+ self.team = self.factory.makeTeam(name='test-team')
1134+ login_person(self.team.teamowner)
1135 self.team.subscriptionpolicy = TeamSubscriptionPolicy.OPEN
1136 logout()
1137
1138@@ -87,7 +88,7 @@
1139 test_person = launchpad.people['test-person']
1140 test_team = launchpad.people['test-team']
1141 test_person.join(team=test_team.self_link)
1142- login_person(owner)
1143+ login_person(self.team.teamowner)
1144 self.assertEqual(
1145 ['test-team'],
1146 [membership.team.name
1147
1148=== modified file 'lib/lp/soyuz/tests/test_archive_subscriptions.py'
1149--- lib/lp/soyuz/tests/test_archive_subscriptions.py 2011-12-12 05:21:01 +0000
1150+++ lib/lp/soyuz/tests/test_archive_subscriptions.py 2011-12-13 14:07:37 +0000
1151@@ -13,7 +13,6 @@
1152 TestCaseWithFactory,
1153 )
1154 from lp.testing.mail_helpers import pop_notifications
1155-from lp.testing.views import create_initialized_view
1156
1157
1158 class TestArchiveSubscriptions(TestCaseWithFactory):
1159@@ -27,11 +26,9 @@
1160 def setUp(self):
1161 """Create a test archive."""
1162 super(TestArchiveSubscriptions, self).setUp()
1163- self.owner = self.factory.makePerson()
1164 self.private_team = self.factory.makeTeam(
1165- visibility=PersonVisibility.PRIVATE,
1166- name="subscribertest", owner=self.owner)
1167- login_person(self.owner)
1168+ visibility=PersonVisibility.PRIVATE, name="subscribertest")
1169+ login_person(self.private_team.teamowner)
1170 self.archive = self.factory.makeArchive(
1171 private=True, owner=self.private_team)
1172 self.subscriber = self.factory.makePerson()
1173@@ -48,7 +45,7 @@
1174 login_person(self.subscriber)
1175 self.assertRaises(Unauthorized, get_name)
1176
1177- login_person(self.owner)
1178+ login_person(self.private_team.teamowner)
1179 self.archive.newSubscription(
1180 self.subscriber, registrant=self.archive.owner)
1181
1182@@ -56,33 +53,6 @@
1183 login_person(self.subscriber)
1184 self.assertEqual(self.archive.owner.name, "subscribertest")
1185
1186- def test_subscriber_can_browse_private_team_ppa(self):
1187- # As per bug 597783, we need to make sure a subscriber can see
1188- # a private team's PPA after they have been given a subscription.
1189- # This test ensures the subscriber can correctly load the PPA's view,
1190- # thus ensuring that all attributes necessary to render the view have
1191- # the necessary security permissions.
1192-
1193- # Before a subscription, accessing the view name will raise.
1194- login_person(self.subscriber)
1195- view = create_initialized_view(
1196- self.archive, '+index', principal=self.subscriber)
1197- self.assertRaises(Unauthorized, view.render)
1198-
1199- login_person(self.owner)
1200- self.archive.newSubscription(
1201- self.subscriber, registrant=self.archive.owner)
1202-
1203- # When a subscription exists, it's fine.
1204- login_person(self.subscriber)
1205- self.assertIn(self.archive.displayname, view.render())
1206-
1207- # Just to double check, by default, the subscriber still can't see the
1208- # +packages view which requires extra permissions.
1209- self.assertRaises(
1210- Unauthorized, create_initialized_view,
1211- self.archive, '+packages', principal=self.subscriber)
1212-
1213 def test_new_subscription_sends_email(self):
1214 # Creating a new subscription sends an email to all members
1215 # of the person or team subscribed.
1216
1217=== modified file 'lib/lp/testing/factory.py'
1218--- lib/lp/testing/factory.py 2011-12-12 04:47:21 +0000
1219+++ lib/lp/testing/factory.py 2011-12-13 14:07:37 +0000
1220@@ -772,7 +772,7 @@
1221 address, person, email_status, account)
1222
1223 def makeTeam(self, owner=None, displayname=None, email=None, name=None,
1224- description=None, icon=None, logo=None,
1225+ description=None,
1226 subscription_policy=TeamSubscriptionPolicy.OPEN,
1227 visibility=None, members=None):
1228 """Create and return a new, arbitrary Team.
1229@@ -783,11 +783,9 @@
1230 :param displayname: The team's display name. If not given we'll use
1231 the auto-generated name.
1232 :param description: Team team's description.
1233- :type description string:
1234+ :type string:
1235 :param email: The email address to use as the team's contact address.
1236 :type email: string
1237- :param icon: The team's icon.
1238- :param logo: The team's logo.
1239 :param subscription_policy: The subscription policy of the team.
1240 :type subscription_policy: `TeamSubscriptionPolicy`
1241 :param visibility: The team's visibility. If it's None, the default
1242@@ -812,19 +810,15 @@
1243 team = getUtility(IPersonSet).newTeam(
1244 owner, name, displayname, teamdescription=description,
1245 subscriptionpolicy=subscription_policy)
1246- naked_team = removeSecurityProxy(team)
1247 if visibility is not None:
1248 # Visibility is normally restricted to launchpad.Commercial, so
1249 # removing the security proxy as we don't care here.
1250- naked_team.visibility = visibility
1251+ removeSecurityProxy(team).visibility = visibility
1252 if email is not None:
1253 team.setContactAddress(
1254 getUtility(IEmailAddressSet).new(email, team))
1255- if icon is not None:
1256- naked_team.icon = icon
1257- if logo is not None:
1258- naked_team.logo = logo
1259 if members is not None:
1260+ naked_team = removeSecurityProxy(team)
1261 for member in members:
1262 naked_team.addMember(member, owner)
1263 return team
1264@@ -1130,7 +1124,7 @@
1265
1266 if registrant is None:
1267 if owner.is_team:
1268- registrant = removeSecurityProxy(owner).teamowner
1269+ registrant = owner.teamowner
1270 else:
1271 registrant = owner
1272