Merge lp:~sinzui/launchpad/rollback-private-traversal into lp:launchpad
- rollback-private-traversal
- Merge into devel
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 |
Related bugs: |
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.
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 |
Self-approving to unblock the next release.