Merge lp:~bac/launchpad/bug-588773 into lp:launchpad

Proposed by Brad Crittenden
Status: Merged
Approved by: Aaron Bentley
Approved revision: no longer in the source branch.
Merged at revision: 10966
Proposed branch: lp:~bac/launchpad/bug-588773
Merge into: lp:launchpad
Diff against target: 1369 lines (+286/-17)
3 files modified
lib/canonical/launchpad/security.py (+186/-11)
lib/lp/registry/browser/tests/test_edit_permissions.py (+97/-0)
lib/lp/registry/stories/product/xx-product-edit.txt (+3/-6)
To merge this branch: bzr merge lp:~bac/launchpad/bug-588773
Reviewer Review Type Date Requested Status
Aaron Bentley (community) code Approve
Review via email: mp+26968@code.launchpad.net

Commit message

Allow registry admins to access +edit on persons and pillars.

Description of the change

= Summary =

We want registry experts (members of ~registry) to have more abilities
in order to perform CHR duties. Often things like person or pillar
editing has to be deferred to LP admins which is not an efficient use of
their nor the CHR's time.

== Proposed fix ==

Change the security settings for launchpad.Edit for pillars and persons
to include registry experts.

== Pre-implementation notes ==

Chats with Curtis.

== Implementation details ==

As above.

== Tests ==

bin/test -vvm lp.registry -t test_edit_permissions

== Demo and Q/A ==

In our sample data, only mark is in the registry admin team, but he is
also a LP admin, so using that account to test proves nothing. A
non-admin (such as bac) needs to be added to the registry team.

Once that is done, simply demonstrate that the registry expert can edit
people, project, project groups, and distros.

= Launchpad lint =

Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.

Linting changed files:
  lib/canonical/launchpad/security.py
  lib/lp/registry/browser/tests/test_edit_permissions.py

== Pyflakes notices ==

lib/canonical/launchpad/security.py
    1170: undefined name 'EditDistributionByDistroOwnersOrAdmins'

== Pylint notices ==

lib/canonical/launchpad/security.py
    1170: [E0602, AdminDistributionTranslations.checkAuthenticated]
Undefined variable 'EditDistributionByDistroOwnersOrAdmins'

I'll sort this out.

To post a comment you must log in.
Revision history for this message
Brad Crittenden (bac) wrote :

Here is the incremental to fix the lint issue:

=== modified file 'lib/canonical/launchpad/security.py'
--- lib/canonical/launchpad/security.py 2010-06-07 15:52:21 +0000
+++ lib/canonical/launchpad/security.py 2010-06-07 16:29:17 +0000
@@ -781,7 +781,7 @@
     usedfor = IDistribution

-class EditDistroByOwnersRegistryExpertOrAdmins(
+class EditDistributionByOwnersRegistryExpertsOrAdmins(
     EditByOwnersRegistryExpertsOrAdmins):
     """The owner of a distribution should be able to edit its
     information; it is mainly administrative data, such as bug
@@ -1167,7 +1167,7 @@
             return True
         else:
             return (user.in_rosetta_experts or
- EditDistributionByDistroOwnersOrAdmins(
+ EditByOwnersOrAdmins(
                         self.obj).checkAuthenticated(user))

Revision history for this message
Aaron Bentley (abentley) wrote :

Looks good. As discussed on IRC, there are a few spots that could use an additional blank line.

review: Approve (code)
Revision history for this message
Brad Crittenden (bac) wrote :

Thanks Aaron. The file was a bit of a mess regarding blank lines between the class/docstring and the first line of code. I fixed them all.

http://pastebin.ubuntu.com/446250/

Revision history for this message
Brad Crittenden (bac) wrote :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/canonical/launchpad/security.py'
2--- lib/canonical/launchpad/security.py 2010-06-07 12:01:14 +0000
3+++ lib/canonical/launchpad/security.py 2010-06-08 13:20:48 +0000
4@@ -132,6 +132,7 @@
5
6
7 class AuthorizationBase:
8+
9 implements(IAuthorization)
10 permission = None
11 usedfor = None
12@@ -176,6 +177,7 @@
13 By default, any logged-in user can see anything. More restrictive
14 rulesets are defined in other IAuthorization implementations.
15 """
16+
17 permission = 'launchpad.View'
18 usedfor = Interface
19
20@@ -186,6 +188,7 @@
21
22 class AnonymousAuthorization(AuthorizationBase):
23 """Allow any authenticated and unauthenticated user access."""
24+
25 permission = 'launchpad.View'
26
27 def checkUnauthenticated(self):
28@@ -198,6 +201,7 @@
29
30
31 class AdminByAdminsTeam(AuthorizationBase):
32+
33 permission = 'launchpad.Admin'
34 usedfor = Interface
35
36@@ -206,6 +210,7 @@
37
38
39 class AdminByCommercialTeamOrAdmins(AuthorizationBase):
40+
41 permission = 'launchpad.Commercial'
42 usedfor = Interface
43
44@@ -214,6 +219,7 @@
45
46
47 class EditByRegistryExpertsOrAdmins(AuthorizationBase):
48+
49 permission = 'launchpad.Edit'
50 usedfor = ILaunchpadRoot
51
52@@ -222,6 +228,7 @@
53
54
55 class ReviewByRegistryExpertsOrAdmins(AuthorizationBase):
56+
57 permission = 'launchpad.ProjectReview'
58 usedfor = None
59
60@@ -230,27 +237,33 @@
61
62
63 class ReviewProduct(ReviewByRegistryExpertsOrAdmins):
64+
65 usedfor = IProduct
66
67
68 class ReviewProductSet(ReviewByRegistryExpertsOrAdmins):
69+
70 usedfor = IProductSet
71
72
73 class ReviewProject(ReviewByRegistryExpertsOrAdmins):
74+
75 usedfor = IProjectGroup
76
77
78 class ReviewProjectGroupSet(ReviewByRegistryExpertsOrAdmins):
79+
80 usedfor = IProjectGroupSet
81
82
83 class ModeratePerson(ReviewByRegistryExpertsOrAdmins):
84+
85 permission = 'launchpad.Moderate'
86 usedfor = IPerson
87
88
89 class ViewPillar(AuthorizationBase):
90+
91 usedfor = IPillar
92 permission = 'launchpad.View'
93
94@@ -268,6 +281,7 @@
95
96
97 class EditAccountBySelfOrAdmin(AuthorizationBase):
98+
99 permission = 'launchpad.Edit'
100 usedfor = IAccount
101
102@@ -282,10 +296,12 @@
103
104
105 class ViewAccount(EditAccountBySelfOrAdmin):
106+
107 permission = 'launchpad.View'
108
109
110 class SpecialAccount(EditAccountBySelfOrAdmin):
111+
112 permission = 'launchpad.Special'
113
114 def checkAuthenticated(self, user):
115@@ -294,14 +310,16 @@
116
117
118 class ModerateAccountByRegistryExpert(AuthorizationBase):
119+
120+ permission = 'launchpad.Moderate'
121 usedfor = IAccount
122- permission = 'launchpad.Moderate'
123
124 def checkAuthenticated(self, user):
125 return user.in_admin or user.in_registry_experts
126
127
128 class EditOAuthAccessToken(AuthorizationBase):
129+
130 permission = 'launchpad.Edit'
131 usedfor = IOAuthAccessToken
132
133@@ -310,11 +328,13 @@
134
135
136 class EditOAuthRequestToken(EditOAuthAccessToken):
137+
138 permission = 'launchpad.Edit'
139 usedfor = IOAuthRequestToken
140
141
142 class EditBugNominationStatus(AuthorizationBase):
143+
144 permission = 'launchpad.Driver'
145 usedfor = IBugNomination
146
147@@ -323,6 +343,7 @@
148
149
150 class EditByOwnersOrAdmins(AuthorizationBase):
151+
152 permission = 'launchpad.Edit'
153 usedfor = IHasOwner
154
155@@ -330,15 +351,28 @@
156 return user.isOwner(self.obj) or user.in_admin
157
158
159-class EditProduct(EditByOwnersOrAdmins):
160+class EditByOwnersRegistryExpertsOrAdmins(AuthorizationBase):
161+
162+ permission = 'launchpad.Edit'
163+
164+ def checkAuthenticated(self, user):
165+ return (user.isOwner(self.obj) or
166+ user.in_registry_experts or
167+ user.in_admin)
168+
169+
170+class EditProduct(EditByOwnersRegistryExpertsOrAdmins):
171+
172 usedfor = IProduct
173
174
175 class EditPackaging(EditByOwnersOrAdmins):
176+
177 usedfor = IPackaging
178
179
180 class EditProductReleaseFile(AuthorizationBase):
181+
182 permission = 'launchpad.Edit'
183 usedfor = IProductReleaseFile
184
185@@ -349,6 +383,7 @@
186
187 class AdminDistributionMirrorByDistroOwnerOrMirrorAdminsOrAdmins(
188 AuthorizationBase):
189+
190 permission = 'launchpad.Admin'
191 usedfor = IDistributionMirror
192
193@@ -360,6 +395,7 @@
194
195 class EditDistributionMirrorByOwnerOrDistroOwnerOrMirrorAdminsOrAdmins(
196 AuthorizationBase):
197+
198 permission = 'launchpad.Edit'
199 usedfor = IDistributionMirror
200
201@@ -371,11 +407,13 @@
202
203 class ViewDistributionMirror(AnonymousAuthorization):
204 """Anyone can view an IDistributionMirror."""
205+
206 usedfor = IDistributionMirror
207
208
209 class ViewMilestone(AnonymousAuthorization):
210 """Anyone can view an IMilestone."""
211+
212 usedfor = IMilestone
213
214
215@@ -426,6 +464,7 @@
216
217
218 class AdminSpecification(AuthorizationBase):
219+
220 permission = 'launchpad.Admin'
221 usedfor = ISpecification
222
223@@ -437,6 +476,7 @@
224
225
226 class DriverSpecification(AuthorizationBase):
227+
228 permission = 'launchpad.Driver'
229 usedfor = ISpecification
230
231@@ -455,6 +495,7 @@
232 """The sprint owner or driver can say what makes it onto the agenda for
233 the sprint.
234 """
235+
236 permission = 'launchpad.Driver'
237 usedfor = ISprintSpecification
238
239@@ -467,6 +508,7 @@
240 """The sprint owner or driver can say what makes it onto the agenda for
241 the sprint.
242 """
243+
244 permission = 'launchpad.Driver'
245 usedfor = ISprint
246
247@@ -478,6 +520,7 @@
248
249 class Sprint(AuthorizationBase):
250 """An attendee, owner, or driver of a sprint."""
251+
252 permission = 'launchpad.View'
253 usedfor = ISprint
254
255@@ -492,6 +535,7 @@
256 class EditSpecificationSubscription(AuthorizationBase):
257 """The subscriber, and people related to the spec or the target of the
258 spec can determine who is essential."""
259+
260 permission = 'launchpad.Edit'
261 usedfor = ISpecificationSubscription
262
263@@ -519,6 +563,7 @@
264
265
266 class AdminProjectTranslations(AuthorizationBase):
267+
268 permission = 'launchpad.TranslationsAdmin'
269 usedfor = IProjectGroup
270
271@@ -534,6 +579,7 @@
272
273
274 class AdminProductTranslations(AuthorizationBase):
275+
276 permission = 'launchpad.TranslationsAdmin'
277 usedfor = IProduct
278
279@@ -548,7 +594,13 @@
280 user.in_admin)
281
282
283+class EditProject(EditByOwnersRegistryExpertsOrAdmins):
284+
285+ usedfor = IProjectGroup
286+
287+
288 class EditProjectMilestoneNever(AuthorizationBase):
289+
290 permission = 'launchpad.Edit'
291 usedfor = IProjectGroupMilestone
292
293@@ -558,6 +610,7 @@
294
295
296 class EditMilestoneByTargetOwnerOrAdmins(AuthorizationBase):
297+
298 permission = 'launchpad.Edit'
299 usedfor = IMilestone
300
301@@ -575,6 +628,7 @@
302
303
304 class AdminMilestoneByLaunchpadAdmins(AuthorizationBase):
305+
306 permission = 'launchpad.Admin'
307 usedfor = IMilestone
308
309@@ -586,11 +640,13 @@
310
311
312 class ModeratePersonSetByExpertsOrAdmins(ReviewByRegistryExpertsOrAdmins):
313+
314 permission = 'launchpad.Moderate'
315 usedfor = IPersonSet
316
317
318 class EditTeamByTeamOwnerOrLaunchpadAdmins(AuthorizationBase):
319+
320 permission = 'launchpad.Owner'
321 usedfor = ITeam
322
323@@ -601,6 +657,7 @@
324
325
326 class EditTeamByTeamOwnerOrTeamAdminsOrAdmins(AuthorizationBase):
327+
328 permission = 'launchpad.Edit'
329 usedfor = ITeam
330
331@@ -613,6 +670,7 @@
332
333
334 class ModerateTeam(ReviewByRegistryExpertsOrAdmins):
335+
336 permission = 'launchpad.Moderate'
337 usedfor = ITeam
338
339@@ -628,6 +686,7 @@
340
341
342 class EditTeamMembershipByTeamOwnerOrTeamAdminsOrAdmins(AuthorizationBase):
343+
344 permission = 'launchpad.Edit'
345 usedfor = ITeamMembership
346
347@@ -640,6 +699,7 @@
348 # going to be used only on the webservice (which explicitly checks for
349 # launchpad.View) so that we don't leak memberships of private teams.
350 class ViewTeamMembership(AuthorizationBase):
351+
352 permission = 'launchpad.View'
353 usedfor = ITeamMembership
354
355@@ -660,19 +720,23 @@
356 return False
357
358
359-class EditPersonBySelfOrAdmins(AuthorizationBase):
360+class EditPersonBySelfRegistryExpertsOrAdmins(AuthorizationBase):
361+
362 permission = 'launchpad.Edit'
363 usedfor = IPerson
364
365 def checkAuthenticated(self, user):
366- """A user can edit the Person who is herself.
367+ """A user can edit his or her own object.
368
369- The admin team can also edit any Person.
370+ The registry experts or admin teams can also edit any Person.
371 """
372- return self.obj.id == user.person.id or user.in_admin
373+ return (self.obj.id == user.person.id or
374+ user.in_registry_experts or
375+ user.in_admin)
376
377
378 class EditTranslationsPersonByPerson(AuthorizationBase):
379+
380 permission = 'launchpad.Edit'
381 usedfor = ITranslationsPerson
382
383@@ -682,6 +746,7 @@
384
385
386 class ViewPersonLocation(AuthorizationBase):
387+
388 permission = 'launchpad.View'
389 usedfor = IPersonLocation
390
391@@ -696,6 +761,7 @@
392
393
394 class EditPersonBySelf(AuthorizationBase):
395+
396 permission = 'launchpad.Special'
397 usedfor = IPerson
398
399@@ -710,6 +776,7 @@
400 Only members of a team with a private membership can view the
401 membership list.
402 """
403+
404 permission = 'launchpad.View'
405 usedfor = IPerson
406
407@@ -740,17 +807,20 @@
408
409 class EditPollByTeamOwnerOrTeamAdminsOrAdmins(
410 EditTeamMembershipByTeamOwnerOrTeamAdminsOrAdmins):
411+
412 permission = 'launchpad.Edit'
413 usedfor = IPoll
414
415
416 class EditPollSubsetByTeamOwnerOrTeamAdminsOrAdmins(
417 EditPollByTeamOwnerOrTeamAdminsOrAdmins):
418+
419 permission = 'launchpad.Edit'
420 usedfor = IPollSubset
421
422
423 class EditPollOptionByTeamOwnerOrTeamAdminsOrAdmins(AuthorizationBase):
424+
425 permission = 'launchpad.Edit'
426 usedfor = IPollOption
427
428@@ -762,21 +832,21 @@
429 """Soyuz involves huge chunks of data in the archive and librarian,
430 so for the moment we are locking down admin and edit on distributions
431 and distroseriess to the Launchpad admin team."""
432+
433 permission = 'launchpad.Admin'
434 usedfor = IDistribution
435
436
437-class EditDistributionByDistroOwnersOrAdmins(AuthorizationBase):
438+class EditDistributionByOwnersRegistryExpertsOrAdmins(
439+ EditByOwnersRegistryExpertsOrAdmins):
440 """The owner of a distribution should be able to edit its
441 information; it is mainly administrative data, such as bug
442 contacts. Note that creation of new distributions and distribution
443 series is still protected with launchpad.Admin"""
444+
445 permission = 'launchpad.Edit'
446 usedfor = IDistribution
447
448- def checkAuthenticated(self, user):
449- return user.isOwner(self.obj) or user.in_admin
450-
451
452 class AppendDistributionByDriversOrOwnersOrAdmins(AuthorizationBase):
453 """Distribution drivers, owners, and admins may plan releases.
454@@ -784,6 +854,7 @@
455 Drivers of `IDerivativeDistribution`s can create series. Owners and
456 admins can create series for all `IDistribution`s.
457 """
458+
459 permission = 'launchpad.Append'
460 usedfor = IDistribution
461
462@@ -798,6 +869,7 @@
463 class EditDistributionSourcePackageByDistroOwnersOrAdmins(AuthorizationBase):
464 """The owner of a distribution should be able to edit its source
465 package information"""
466+
467 permission = 'launchpad.Edit'
468 usedfor = IDistributionSourcePackage
469
470@@ -816,6 +888,7 @@
471 files moved to the new namespace, and mirrors would get very very
472 upset. Then James T would be on your case.
473 """
474+
475 permission = 'launchpad.Admin'
476 usedfor = IDistroSeries
477
478@@ -828,6 +901,7 @@
479 please consult with Kiko and MDZ on the mailing list before modifying
480 these permissions.
481 """
482+
483 permission = 'launchpad.Edit'
484 usedfor = IDistroSeries
485
486@@ -844,11 +918,13 @@
487
488 class ViewDistroSeries(AnonymousAuthorization):
489 """Anyone can view a DistroSeries."""
490+
491 usedfor = IDistroSeries
492
493
494 class ViewCountry(AnonymousAuthorization):
495 """Anyone can view a Country."""
496+
497 usedfor = ICountry
498
499
500@@ -858,6 +934,7 @@
501 Drivers exist for distribution and product series. Distribution and
502 product owners are implicitly drivers too.
503 """
504+
505 permission = 'launchpad.Driver'
506 usedfor = IHasDrivers
507
508@@ -873,6 +950,7 @@
509
510
511 class EditProductSeries(EditByOwnersOrAdmins):
512+
513 usedfor = IProductSeries
514
515 def checkAuthenticated(self, user):
516@@ -898,6 +976,7 @@
517 bugs. Allow only explicit subscribers to edit objects linked to
518 private bugs.
519 """
520+
521 permission = 'launchpad.Edit'
522 usedfor = IHasBug
523
524@@ -923,6 +1002,7 @@
525
526
527 class PublicToAllOrPrivateToExplicitSubscribersForBugTask(AuthorizationBase):
528+
529 permission = 'launchpad.View'
530 usedfor = IHasBug
531
532@@ -936,6 +1016,7 @@
533
534 class EditPublicByLoggedInUserAndPrivateByExplicitSubscribers(
535 AuthorizationBase):
536+
537 permission = 'launchpad.Edit'
538 usedfor = IBug
539
540@@ -963,6 +1044,7 @@
541
542
543 class PublicToAllOrPrivateToExplicitSubscribersForBug(AuthorizationBase):
544+
545 permission = 'launchpad.View'
546 usedfor = IBug
547
548@@ -978,6 +1060,7 @@
549
550
551 class EditBugBranch(EditPublicByLoggedInUserAndPrivateByExplicitSubscribers):
552+
553 permission = 'launchpad.Edit'
554 usedfor = IBugBranch
555
556@@ -994,6 +1077,7 @@
557 If the user is authorized to view the bug, he's allowed to view the
558 attachment.
559 """
560+
561 permission = 'launchpad.View'
562 usedfor = IBugAttachment
563
564@@ -1009,6 +1093,7 @@
565 If the user is authorized to view the bug, he's allowed to edit the
566 attachment.
567 """
568+
569 permission = 'launchpad.Edit'
570 usedfor = IBugAttachment
571
572@@ -1028,6 +1113,7 @@
573
574
575 class ViewAnnouncement(AuthorizationBase):
576+
577 permission = 'launchpad.View'
578 usedfor = IAnnouncement
579
580@@ -1054,6 +1140,7 @@
581
582
583 class EditAnnouncement(AuthorizationBase):
584+
585 permission = 'launchpad.Edit'
586 usedfor = IAnnouncement
587
588@@ -1067,6 +1154,7 @@
589
590
591 class UseApiDoc(AuthorizationBase):
592+
593 permission = 'zope.app.apidoc.UseAPIDoc'
594 usedfor = Interface
595
596@@ -1096,6 +1184,7 @@
597 Currently, we restrict the visibility of the new code import
598 system to members of ~vcs-imports and Launchpad admins.
599 """
600+
601 permission = 'launchpad.Edit'
602 usedfor = ICodeImport
603
604@@ -1106,6 +1195,7 @@
605 Currently, we restrict the visibility of the new code import
606 system to members of ~vcs-imports and Launchpad admins.
607 """
608+
609 permission = 'launchpad.View'
610 usedfor = ICodeImportJobSet
611
612@@ -1116,6 +1206,7 @@
613 Currently, we restrict the visibility of the new code import
614 system to members of ~vcs-imports and Launchpad admins.
615 """
616+
617 permission = 'launchpad.Edit'
618 usedfor = ICodeImportJobWorkflow
619
620@@ -1125,6 +1216,7 @@
621
622 Access is restricted to members of ~bazaar-experts and Launchpad admins.
623 """
624+
625 permission = 'launchpad.Edit'
626 usedfor = ICodeImportMachine
627
628@@ -1137,6 +1229,7 @@
629 and limits access to Rosetta experts, Launchpad admins and distribution
630 translation group owner.
631 """
632+
633 permission = 'launchpad.TranslationsAdmin'
634 usedfor = IDistribution
635
636@@ -1154,12 +1247,13 @@
637 return True
638 else:
639 return (user.in_rosetta_experts or
640- EditDistributionByDistroOwnersOrAdmins(
641+ EditByOwnersOrAdmins(
642 self.obj).checkAuthenticated(user))
643
644
645 class ViewPOTemplates(AnonymousAuthorization):
646 """Anyone can view an IPOTemplate."""
647+
648 usedfor = IPOTemplate
649
650
651@@ -1191,6 +1285,7 @@
652
653
654 class EditPOTemplateDetails(AdminPOTemplateDetails, EditByOwnersOrAdmins):
655+
656 permission = 'launchpad.Edit'
657 usedfor = IPOTemplate
658
659@@ -1211,16 +1306,19 @@
660 # XXX: Carlos Perello Marin 2005-05-24 bug=753:
661 # This should be using SuperSpecialPermissions when implemented.
662 class AddPOTemplate(OnlyRosettaExpertsAndAdmins):
663+
664 permission = 'launchpad.Append'
665 usedfor = IProductSeries
666
667
668 class ViewPOFile(AnonymousAuthorization):
669 """Anyone can view an IPOFile."""
670+
671 usedfor = IPOFile
672
673
674 class EditPOFileDetails(EditByOwnersOrAdmins):
675+
676 usedfor = IPOFile
677
678 def checkAuthenticated(self, user):
679@@ -1233,6 +1331,7 @@
680
681
682 class AdminTranslator(OnlyRosettaExpertsAndAdmins):
683+
684 permission = 'launchpad.Admin'
685 usedfor = ITranslator
686
687@@ -1244,6 +1343,7 @@
688
689
690 class EditTranslator(OnlyRosettaExpertsAndAdmins):
691+
692 permission = 'launchpad.Edit'
693 usedfor = IEditTranslator
694
695@@ -1256,6 +1356,7 @@
696
697
698 class EditTranslationGroup(OnlyRosettaExpertsAndAdmins):
699+
700 permission = 'launchpad.Edit'
701 usedfor = ITranslationGroup
702
703@@ -1267,6 +1368,7 @@
704
705
706 class EditTranslationGroupSet(OnlyRosettaExpertsAndAdmins):
707+
708 permission = 'launchpad.Admin'
709 usedfor = ITranslationGroupSet
710
711@@ -1312,10 +1414,12 @@
712
713 class ViewBugTracker(AnonymousAuthorization):
714 """Anyone can view a bug tracker."""
715+
716 usedfor = IBugTracker
717
718
719 class EditBugTracker(AuthorizationBase):
720+
721 permission = 'launchpad.Edit'
722 usedfor = IBugTracker
723
724@@ -1325,6 +1429,7 @@
725
726
727 class EditProductRelease(EditByOwnersOrAdmins):
728+
729 permission = 'launchpad.Edit'
730 usedfor = IProductRelease
731
732@@ -1344,6 +1449,7 @@
733
734
735 class AdminTranslationImportQueueEntry(AuthorizationBase):
736+
737 permission = 'launchpad.Admin'
738 usedfor = ITranslationImportQueueEntry
739
740@@ -1352,6 +1458,7 @@
741
742
743 class EditTranslationImportQueueEntry(AuthorizationBase):
744+
745 permission = 'launchpad.Edit'
746 usedfor = ITranslationImportQueueEntry
747
748@@ -1363,11 +1470,13 @@
749
750
751 class AdminTranslationImportQueue(OnlyRosettaExpertsAndAdmins):
752+
753 permission = 'launchpad.Admin'
754 usedfor = ITranslationImportQueue
755
756
757 class EditPackageUploadQueue(AdminByAdminsTeam):
758+
759 permission = 'launchpad.Edit'
760 usedfor = IPackageUploadQueue
761
762@@ -1384,6 +1493,7 @@
763
764
765 class EditPackageUpload(AdminByAdminsTeam):
766+
767 permission = 'launchpad.Edit'
768 usedfor = IPackageUpload
769
770@@ -1416,6 +1526,7 @@
771
772
773 class AdminByBuilddAdmin(AuthorizationBase):
774+
775 permission = 'launchpad.Admin'
776
777 def checkAuthenticated(self, user):
778@@ -1424,10 +1535,12 @@
779
780
781 class AdminBuilderSet(AdminByBuilddAdmin):
782+
783 usedfor = IBuilderSet
784
785
786 class AdminBuilder(AdminByBuilddAdmin):
787+
788 usedfor = IBuilder
789
790
791@@ -1435,20 +1548,24 @@
792 # in the original plan, we should grant some rights to the owners and
793 # that's what Edit is for.
794 class EditBuilder(AdminByBuilddAdmin):
795+
796 permission = 'launchpad.Edit'
797 usedfor = IBuilder
798
799
800 class AdminBuildRecord(AdminByBuilddAdmin):
801+
802 usedfor = IBuildFarmJob
803
804
805 class EditBuildFarmJob(AdminByBuilddAdmin):
806+
807 permission = 'launchpad.Edit'
808 usedfor = IBuildFarmJob
809
810
811 class EditPackageBuild(EditBuildFarmJob):
812+
813 usedfor = IPackageBuild
814
815 def checkAuthenticated(self, user):
816@@ -1463,6 +1580,7 @@
817 user.inTeam(self.obj.archive.owner))
818
819 class EditBinaryPackageBuild(EditPackageBuild):
820+
821 permission = 'launchpad.Edit'
822 usedfor = IBinaryPackageBuild
823
824@@ -1492,6 +1610,7 @@
825
826
827 class ViewBinaryPackageBuild(EditBinaryPackageBuild):
828+
829 permission = 'launchpad.View'
830
831 # This code MUST match the logic in
832@@ -1539,6 +1658,7 @@
833 This permission is based entirely on permission to view the
834 associated `IBinaryPackageBuild` and/or `IBranch`.
835 """
836+
837 permission = 'launchpad.View'
838 usedfor = IBuildFarmJobOld
839
840@@ -1581,6 +1701,7 @@
841
842
843 class AdminQuestion(AdminByAdminsTeam):
844+
845 permission = 'launchpad.Admin'
846 usedfor = IQuestion
847
848@@ -1592,6 +1713,7 @@
849
850
851 class ModerateQuestion(AdminQuestion):
852+
853 permission = 'launchpad.Moderate'
854 usedfor = IQuestion
855
856@@ -1606,6 +1728,7 @@
857
858
859 class QuestionOwner(AuthorizationBase):
860+
861 permission = 'launchpad.Owner'
862 usedfor = IQuestion
863
864@@ -1615,6 +1738,7 @@
865
866
867 class ModerateFAQTarget(EditByOwnersOrAdmins):
868+
869 permission = 'launchpad.Moderate'
870 usedfor = IFAQTarget
871
872@@ -1630,6 +1754,7 @@
873
874
875 class EditFAQ(AuthorizationBase):
876+
877 permission = 'launchpad.Edit'
878 usedfor = IFAQ
879
880@@ -1649,20 +1774,24 @@
881
882 class ViewLanguageSet(AnonymousAuthorization):
883 """Anyone can view an ILangaugeSet."""
884+
885 usedfor = ILanguageSet
886
887
888 class AdminLanguageSet(OnlyRosettaExpertsAndAdmins):
889+
890 permission = 'launchpad.Admin'
891 usedfor = ILanguageSet
892
893
894 class ViewLanguage(AnonymousAuthorization):
895 """Anyone can view an ILangauge."""
896+
897 usedfor = ILanguage
898
899
900 class AdminLanguage(OnlyRosettaExpertsAndAdmins):
901+
902 permission = 'launchpad.Admin'
903 usedfor = ILanguage
904
905@@ -1674,6 +1803,7 @@
906 of the branch, they are in the team that owns the branch, subscribed to
907 the branch, or a launchpad administrator.
908 """
909+
910 permission = 'launchpad.View'
911 usedfor = IBranch
912
913@@ -1686,6 +1816,7 @@
914
915 class EditBranch(AuthorizationBase):
916 """The owner, bazaar experts or admins can edit branches."""
917+
918 permission = 'launchpad.Edit'
919 usedfor = IBranch
920
921@@ -1734,6 +1865,7 @@
922
923 class AdminBranch(AuthorizationBase):
924 """The bazaar experts or admins can administer branches."""
925+
926 permission = 'launchpad.Admin'
927 usedfor = IBranch
928
929@@ -1743,6 +1875,7 @@
930
931
932 class AdminDistroSeriesTranslations(AuthorizationBase):
933+
934 permission = 'launchpad.TranslationsAdmin'
935 usedfor = IDistroSeries
936
937@@ -1757,6 +1890,7 @@
938
939
940 class BranchMergeProposalView(AuthorizationBase):
941+
942 permission = 'launchpad.View'
943 usedfor = IBranchMergeProposal
944
945@@ -1781,6 +1915,7 @@
946
947
948 class PreviewDiffView(AuthorizationBase):
949+
950 permission = 'launchpad.View'
951 usedfor = IPreviewDiff
952
953@@ -1804,6 +1939,7 @@
954
955
956 class CodeReviewVoteReferenceEdit(AuthorizationBase):
957+
958 permission = 'launchpad.Edit'
959 usedfor = ICodeReviewVoteReference
960
961@@ -1825,6 +1961,7 @@
962
963
964 class CodeReviewCommentView(AuthorizationBase):
965+
966 permission = 'launchpad.View'
967 usedfor = ICodeReviewComment
968
969@@ -1848,6 +1985,7 @@
970
971
972 class CodeReviewCommentDelete(AuthorizationBase):
973+
974 permission = 'launchpad.Edit'
975 usedfor = ICodeReviewCommentDeletion
976
977@@ -1871,6 +2009,7 @@
978
979
980 class BranchMergeProposalEdit(AuthorizationBase):
981+
982 permission = 'launchpad.Edit'
983 usedfor = IBranchMergeProposal
984
985@@ -1896,6 +2035,7 @@
986 Allow the owner of the entitlement, the entitlement registrant,
987 or any member of the team or any admin to view the entitlement.
988 """
989+
990 permission = 'launchpad.View'
991 usedfor = IEntitlement
992
993@@ -1913,6 +2053,7 @@
994 class AdminDistroSeriesLanguagePacks(
995 OnlyRosettaExpertsAndAdmins,
996 EditDistroSeriesByOwnersOrDistroOwnersOrAdmins):
997+
998 permission = 'launchpad.LanguagePacksAdmin'
999 usedfor = IDistroSeries
1000
1001@@ -1931,11 +2072,13 @@
1002
1003
1004 class AdminLanguagePack(OnlyRosettaExpertsAndAdmins):
1005+
1006 permission = 'launchpad.LanguagePacksAdmin'
1007 usedfor = ILanguagePack
1008
1009
1010 class ViewHWSubmission(AuthorizationBase):
1011+
1012 permission = 'launchpad.View'
1013 usedfor = IHWSubmission
1014
1015@@ -1956,6 +2099,7 @@
1016
1017
1018 class EditHWSubmission(AdminByAdminsTeam):
1019+
1020 permission = 'launchpad.Edit'
1021 usedfor = IHWSubmission
1022
1023@@ -1963,6 +2107,7 @@
1024 class ViewHWDBBase(AuthorizationBase):
1025 """Base class to restrict access to HWDB data to members of the HWDB team.
1026 """
1027+
1028 permission = 'launchpad.View'
1029
1030 def checkAuthenticated(self, user):
1031@@ -1975,34 +2120,42 @@
1032
1033
1034 class ViewHWDriver(ViewHWDBBase):
1035+
1036 usedfor = IHWDriver
1037
1038
1039 class ViewHWDriverName(ViewHWDBBase):
1040+
1041 usedfor = IHWDriverName
1042
1043
1044 class ViewHWDriverPackageName(ViewHWDBBase):
1045+
1046 usedfor = IHWDriverPackageName
1047
1048
1049 class ViewHWVendorID(ViewHWDBBase):
1050+
1051 usedfor = IHWVendorID
1052
1053
1054 class ViewHWDevice(ViewHWDBBase):
1055+
1056 usedfor = IHWDevice
1057
1058
1059 class ViewHWSubmissionDevice(ViewHWDBBase):
1060+
1061 usedfor = IHWSubmissionDevice
1062
1063
1064 class ViewHWDBApplication(ViewHWDBBase):
1065+
1066 usedfor = IHWDBApplication
1067
1068
1069 class ViewHWDeviceClass(ViewHWDBBase):
1070+
1071 usedfor = IHWDeviceClass
1072
1073
1074@@ -2012,6 +2165,7 @@
1075 Only admins or members of a team with a private membership can
1076 view the archive.
1077 """
1078+
1079 permission = 'launchpad.View'
1080 usedfor = IArchive
1081
1082@@ -2059,6 +2213,7 @@
1083 Appending to ubuntu main archives can also be done by the
1084 'ubuntu-security' celebrity.
1085 """
1086+
1087 permission = 'launchpad.Append'
1088 usedfor = IArchive
1089
1090@@ -2087,6 +2242,7 @@
1091 The user just needs to be mentioned in the token, have append privilege
1092 to the archive or be an admin.
1093 """
1094+
1095 permission = "launchpad.View"
1096 usedfor = IArchiveAuthToken
1097
1098@@ -2103,6 +2259,7 @@
1099 The user should have append privileges to the context archive, or be an
1100 admin.
1101 """
1102+
1103 permission = "launchpad.Edit"
1104 usedfor = IArchiveAuthToken
1105
1106@@ -2119,6 +2276,7 @@
1107 The user should be the subscriber, have append privilege to the archive
1108 or be an admin.
1109 """
1110+
1111 permission = "launchpad.View"
1112 usedfor = IPersonalArchiveSubscription
1113
1114@@ -2139,6 +2297,7 @@
1115 The user should be the subscriber, have append privilege to the
1116 archive or be an admin.
1117 """
1118+
1119 permission = "launchpad.View"
1120 usedfor = IArchiveSubscriber
1121
1122@@ -2154,6 +2313,7 @@
1123
1124 The user should have append privilege to the archive or be an admin.
1125 """
1126+
1127 permission = "launchpad.Edit"
1128 usedfor = IArchiveSubscriber
1129
1130@@ -2212,6 +2372,7 @@
1131
1132 class ViewSourcePackagePublishingHistory(ViewArchive):
1133 """Restrict viewing of source publications."""
1134+
1135 permission = "launchpad.View"
1136 usedfor = ISourcePackagePublishingHistory
1137
1138@@ -2221,6 +2382,7 @@
1139
1140 class EditPublishing(AuthorizationBase):
1141 """Restrict editing of source and binary packages.."""
1142+
1143 permission = "launchpad.Edit"
1144 usedfor = IPublishingEdit
1145
1146@@ -2230,12 +2392,14 @@
1147
1148 class ViewBinaryPackagePublishingHistory(ViewSourcePackagePublishingHistory):
1149 """Restrict viewing of binary publications."""
1150+
1151 usedfor = IBinaryPackagePublishingHistory
1152
1153
1154 class ViewBinaryPackageReleaseDownloadCount(
1155 ViewSourcePackagePublishingHistory):
1156 """Restrict viewing of binary package download counts."""
1157+
1158 usedfor = IBinaryPackageReleaseDownloadCount
1159
1160
1161@@ -2249,6 +2413,7 @@
1162 automatically viewable even if the package is also published in
1163 a private archive.
1164 """
1165+
1166 permission = 'launchpad.View'
1167 usedfor = ISourcePackageRelease
1168
1169@@ -2273,6 +2438,7 @@
1170
1171
1172 class MailingListApprovalByExperts(AuthorizationBase):
1173+
1174 permission = 'launchpad.Admin'
1175 usedfor = IMailingListSet
1176
1177@@ -2281,6 +2447,7 @@
1178
1179
1180 class ConfigureTeamMailingList(AuthorizationBase):
1181+
1182 permission = 'launchpad.MailingListManager'
1183 usedfor = ITeam
1184
1185@@ -2305,6 +2472,7 @@
1186
1187
1188 class ViewEmailAddress(AuthorizationBase):
1189+
1190 permission = 'launchpad.View'
1191 usedfor = IEmailAddress
1192
1193@@ -2342,6 +2510,7 @@
1194
1195
1196 class EditEmailAddress(EditByOwnersOrAdmins):
1197+
1198 permission = 'launchpad.Edit'
1199 usedfor = IEmailAddress
1200
1201@@ -2354,18 +2523,22 @@
1202
1203
1204 class ViewGPGKey(AnonymousAuthorization):
1205+
1206 usedfor = IGPGKey
1207
1208
1209 class ViewIrcID(AnonymousAuthorization):
1210+
1211 usedfor = IIrcID
1212
1213
1214 class ViewWikiName(AnonymousAuthorization):
1215+
1216 usedfor = IWikiName
1217
1218
1219 class EditArchivePermissionSet(AuthorizationBase):
1220+
1221 permission = 'launchpad.Edit'
1222 usedfor = IArchivePermissionSet
1223
1224@@ -2407,6 +2580,7 @@
1225
1226
1227 class EditPackageset(AuthorizationBase):
1228+
1229 permission = 'launchpad.Edit'
1230 usedfor = IPackageset
1231
1232@@ -2416,6 +2590,7 @@
1233
1234
1235 class EditPackagesetSet(AuthorizationBase):
1236+
1237 permission = 'launchpad.Edit'
1238 usedfor = IPackagesetSet
1239
1240
1241=== added file 'lib/lp/registry/browser/tests/test_edit_permissions.py'
1242--- lib/lp/registry/browser/tests/test_edit_permissions.py 1970-01-01 00:00:00 +0000
1243+++ lib/lp/registry/browser/tests/test_edit_permissions.py 2010-06-08 13:20:48 +0000
1244@@ -0,0 +1,97 @@
1245+# Copyright 2010 Canonical Ltd. This software is licensed under the
1246+# GNU Affero General Public License version 3 (see the file LICENSE).
1247+
1248+"""Test harness for edit view permissions unit tests."""
1249+
1250+__metaclass__ = type
1251+
1252+
1253+import unittest
1254+
1255+from zope.component import getUtility
1256+from canonical.testing.layers import DatabaseFunctionalLayer
1257+from canonical.launchpad.ftests import ANONYMOUS, login, login_person
1258+from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
1259+from canonical.launchpad.webapp.authorization import check_permission
1260+from canonical.launchpad.webapp.servers import LaunchpadTestRequest
1261+from lp.registry.interfaces.person import IPersonSet
1262+from lp.testing import TestCaseWithFactory
1263+from lp.testing.views import create_initialized_view
1264+
1265+
1266+class EditViewPermissionBase(TestCaseWithFactory):
1267+ """Tests for permissions access the +edit page on the target."""
1268+
1269+ layer = DatabaseFunctionalLayer
1270+
1271+ def setUp(self):
1272+ super(EditViewPermissionBase, self).setUp()
1273+ self.setupTarget()
1274+ self.registry_admin = self.factory.makePerson(name='registry-admin')
1275+ celebs = getUtility(ILaunchpadCelebrities)
1276+ login_person(celebs.registry_experts.teamowner)
1277+ celebs.registry_experts.addMember(self.registry_admin,
1278+ self.registry_admin)
1279+ self.request = LaunchpadTestRequest()
1280+
1281+ def setupTarget(self):
1282+ """Set up the target context for the test suite."""
1283+ self.target = self.factory.makePerson(name='target-person')
1284+
1285+ def test_anon_cannot_edit(self):
1286+ login(ANONYMOUS)
1287+ view = create_initialized_view(self.target, '+edit')
1288+ self.assertFalse(check_permission('launchpad.Edit', view))
1289+
1290+ def test_arbitrary_user_cannot_edit(self):
1291+ person = self.factory.makePerson(name='the-dude')
1292+ login_person(person)
1293+ view = create_initialized_view(self.target, '+edit')
1294+ self.assertFalse(check_permission('launchpad.Edit', view))
1295+
1296+ def test_admin_can_edit(self):
1297+ admin = getUtility(IPersonSet).getByEmail('foo.bar@canonical.com')
1298+ login_person(admin)
1299+ view = create_initialized_view(self.target, '+edit')
1300+ self.assertTrue(check_permission('launchpad.Edit', view))
1301+
1302+ def test_registry_expert_can_edit(self):
1303+ login_person(self.registry_admin)
1304+ view = create_initialized_view(self.target, '+edit')
1305+ self.assertTrue(check_permission('launchpad.Edit', view))
1306+
1307+
1308+class PersonEditViewPermissionTestCase(EditViewPermissionBase):
1309+ """Tests for permissions to access person +edit page."""
1310+ def test_arbitrary_user_can_edit_her_own_data(self):
1311+ login_person(self.target)
1312+ view = create_initialized_view(self.target, '+edit')
1313+ self.assertTrue(check_permission('launchpad.Edit', view))
1314+
1315+
1316+class ProductEditViewPermissionTestCase(EditViewPermissionBase):
1317+ """Tests for permissions to access prodcut +edit page."""
1318+ def setupTarget(self):
1319+ self.target = self.factory.makeProduct()
1320+
1321+
1322+class ProjectEditViewPermissionTestCase(EditViewPermissionBase):
1323+ """Tests for permissions to access prodcut +edit page."""
1324+ def setupTarget(self):
1325+ self.target = self.factory.makeProject()
1326+
1327+
1328+class DistributionEditViewPermissionTestCase(EditViewPermissionBase):
1329+ """Tests for permissions to access prodcut +edit page."""
1330+ def setupTarget(self):
1331+ self.target = self.factory.makeDistribution()
1332+
1333+
1334+def test_suite():
1335+ suite = unittest.TestSuite()
1336+ suite.addTest(unittest.TestLoader().loadTestsFromName(__name__))
1337+ return suite
1338+
1339+
1340+if __name__ == '__main__':
1341+ unittest.main()
1342
1343=== modified file 'lib/lp/registry/stories/product/xx-product-edit.txt'
1344--- lib/lp/registry/stories/product/xx-product-edit.txt 2010-05-24 20:23:19 +0000
1345+++ lib/lp/registry/stories/product/xx-product-edit.txt 2010-06-08 13:20:48 +0000
1346@@ -235,20 +235,17 @@
1347 ...
1348 Unauthorized:...
1349
1350-Even if we add them to the Registry Experts team:
1351+If we add them to the Registry Experts team:
1352
1353 >>> admin_browser.open("http://launchpad.dev/~registry/+addmember")
1354 >>> admin_browser.getControl('New member').value = 'no-priv'
1355 >>> admin_browser.getControl('Add Member').click()
1356
1357-They still cannot edit projects.
1358+They now can edit projects.
1359
1360 >>> browser.open('http://launchpad.dev/firefox/+edit')
1361- Traceback (most recent call last):
1362- ...
1363- Unauthorized:...
1364
1365-And they still can't access +admin.
1366+But they still can't access +admin.
1367
1368 >>> browser.open('http://launchpad.dev/firefox/+admin')
1369 Traceback (most recent call last):