Merge lp:~jelmer/launchpad/refactor-permissions into lp:launchpad

Proposed by Jelmer Vernooij
Status: Merged
Approved by: Brad Crittenden
Approved revision: no longer in the source branch.
Merged at revision: 10869
Proposed branch: lp:~jelmer/launchpad/refactor-permissions
Merge into: lp:launchpad
Diff against target: 1138 lines (+356/-318)
19 files modified
lib/canonical/launchpad/security.py (+7/-8)
lib/lp/archiveuploader/nascentupload.py (+2/-3)
lib/lp/archiveuploader/permission.py (+0/-246)
lib/lp/archiveuploader/tests/nascentupload-packageset.txt (+2/-2)
lib/lp/archiveuploader/tests/test_permission.py (+12/-14)
lib/lp/bugs/model/bugnomination.py (+1/-1)
lib/lp/code/model/recipebuilder.py (+2/-3)
lib/lp/code/model/sourcepackagerecipe.py (+3/-4)
lib/lp/code/model/tests/test_sourcepackagerecipe.py (+2/-3)
lib/lp/code/tests/test_branch.py (+4/-5)
lib/lp/soyuz/browser/archive.py (+2/-1)
lib/lp/soyuz/doc/archive.txt (+11/-11)
lib/lp/soyuz/doc/archivepermission.txt (+1/-1)
lib/lp/soyuz/doc/nascentupload.txt (+2/-3)
lib/lp/soyuz/interfaces/archive.py (+136/-1)
lib/lp/soyuz/model/archive.py (+94/-5)
lib/lp/soyuz/model/archivepermission.py (+2/-2)
lib/lp/soyuz/model/binarypackagebuildbehavior.py (+2/-3)
lib/lp/soyuz/tests/test_archive.py (+71/-2)
To merge this branch: bzr merge lp:~jelmer/launchpad/refactor-permissions
Reviewer Review Type Date Requested Status
Brad Crittenden (community) code Approve
Review via email: mp+23216@code.launchpad.net

Commit message

Move archive permission checking functions onto Archive.

Description of the change

This move some of the permission checking functions for archives on the Archive method and removes a couple of duplicate functions in the process.

This is in preparation of exposing these methods over the web API, as requested by james_w.

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

Hi Jelmer this refactoring looks good. In test_archive.py please end all comments with punctuation.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/canonical/launchpad/security.py'
--- lib/canonical/launchpad/security.py 2010-04-26 15:02:03 +0000
+++ lib/canonical/launchpad/security.py 2010-05-11 14:12:39 +0000
@@ -12,8 +12,6 @@
12from zope.component import getAdapter, getUtility12from zope.component import getAdapter, getUtility
1313
14from canonical.launchpad.interfaces.account import IAccount14from canonical.launchpad.interfaces.account import IAccount
15from lp.archiveuploader.permission import (
16 can_upload_to_archive, check_upload_to_archive)
17from canonical.launchpad.interfaces.emailaddress import IEmailAddress15from canonical.launchpad.interfaces.emailaddress import IEmailAddress
18from lp.registry.interfaces.announcement import IAnnouncement16from lp.registry.interfaces.announcement import IAnnouncement
19from lp.soyuz.interfaces.archive import IArchive17from lp.soyuz.interfaces.archive import IArchive
@@ -1475,9 +1473,9 @@
1475 # user to retry build if so.1473 # user to retry build if so.
1476 # strict_component is True because the source package already exists,1474 # strict_component is True because the source package already exists,
1477 # otherwise, how can they give it back?1475 # otherwise, how can they give it back?
1478 check_perms = check_upload_to_archive(1476 check_perms = self.obj.archive.checkUpload(
1479 user.person, self.obj.distroseries,1477 user.person, self.obj.distroseries,
1480 self.obj.sourcepackagerelease.sourcepackagename, self.obj.archive,1478 self.obj.sourcepackagerelease.sourcepackagename,
1481 self.obj.current_component, self.obj.pocket,1479 self.obj.current_component, self.obj.pocket,
1482 strict_component=True)1480 strict_component=True)
1483 return check_perms == None1481 return check_perms == None
@@ -1718,7 +1716,8 @@
1718 # one combination that allows us to upload the corresponding source1716 # one combination that allows us to upload the corresponding source
1719 # package.1717 # package.
1720 for ssp in ssp_list:1718 for ssp in ssp_list:
1721 if can_upload_to_archive(person_role.person, ssp):1719 archive = ssp.sourcepackage.get_default_archive()
1720 if archive.canUploadSuiteSourcePackage(person_role.person, ssp):
1722 return True1721 return True
1723 return False1722 return False
17241723
@@ -2047,7 +2046,7 @@
2047 return True2046 return True
20482047
2049 # Uploaders can view private PPAs.2048 # Uploaders can view private PPAs.
2050 if self.obj.is_ppa and self.obj.canUpload(user.person):2049 if self.obj.is_ppa and self.obj.checkArchivePermission(user.person):
2051 return True2050 return True
20522051
2053 return False2052 return False
@@ -2062,7 +2061,7 @@
20622061
2063 No one can upload to disabled archives.2062 No one can upload to disabled archives.
20642063
2065 PPA upload rights are managed via `IArchive.canUpload`;2064 PPA upload rights are managed via `IArchive.checkArchivePermission`;
20662065
2067 Appending to PRIMARY, PARTNER or COPY archives is restricted to owners.2066 Appending to PRIMARY, PARTNER or COPY archives is restricted to owners.
20682067
@@ -2079,7 +2078,7 @@
2079 if user.inTeam(self.obj.owner):2078 if user.inTeam(self.obj.owner):
2080 return True2079 return True
20812080
2082 if self.obj.is_ppa and self.obj.canUpload(user.person):2081 if self.obj.is_ppa and self.obj.checkArchivePermission(user.person):
2083 return True2082 return True
20842083
2085 celebrities = getUtility(ILaunchpadCelebrities)2084 celebrities = getUtility(ILaunchpadCelebrities)
20862085
=== modified file 'lib/lp/archiveuploader/nascentupload.py'
--- lib/lp/archiveuploader/nascentupload.py 2010-02-26 16:52:46 +0000
+++ lib/lp/archiveuploader/nascentupload.py 2010-05-11 14:12:39 +0000
@@ -28,7 +28,6 @@
28from lp.archiveuploader.nascentuploadfile import (28from lp.archiveuploader.nascentuploadfile import (
29 UploadError, UploadWarning, CustomUploadFile, SourceUploadFile,29 UploadError, UploadWarning, CustomUploadFile, SourceUploadFile,
30 BaseBinaryUploadFile)30 BaseBinaryUploadFile)
31from lp.archiveuploader.permission import check_upload_to_archive
32from lp.archiveuploader.utils import determine_source_file_type31from lp.archiveuploader.utils import determine_source_file_type
33from lp.registry.interfaces.pocket import PackagePublishingPocket32from lp.registry.interfaces.pocket import PackagePublishingPocket
34from lp.registry.interfaces.sourcepackage import SourcePackageFileType33from lp.registry.interfaces.sourcepackage import SourcePackageFileType
@@ -489,8 +488,8 @@
489 source_name = getUtility(488 source_name = getUtility(
490 ISourcePackageNameSet).queryByName(self.changes.dsc.package)489 ISourcePackageNameSet).queryByName(self.changes.dsc.package)
491490
492 rejection_reason = check_upload_to_archive(491 rejection_reason = archive.checkUpload(
493 uploader, self.policy.distroseries, source_name, archive,492 uploader, self.policy.distroseries, source_name,
494 self.changes.dsc.component, self.policy.pocket, not self.is_new)493 self.changes.dsc.component, self.policy.pocket, not self.is_new)
495494
496 if rejection_reason is not None:495 if rejection_reason is not None:
497496
=== removed file 'lib/lp/archiveuploader/permission.py'
--- lib/lp/archiveuploader/permission.py 2010-02-02 14:01:02 +0000
+++ lib/lp/archiveuploader/permission.py 1970-01-01 00:00:00 +0000
@@ -1,246 +0,0 @@
1# Copyright 2009 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Permissions for uploading a package to an archive."""
5
6__metaclass__ = type
7__all__ = [
8 'CannotUploadToArchive',
9 'CannotUploadToPPA',
10 'can_upload_to_archive',
11 'check_upload_to_archive',
12 'check_upload_to_pocket',
13 'components_valid_for',
14 'verify_upload',
15 ]
16
17from zope.component import getUtility
18
19from lp.registry.interfaces.pocket import PackagePublishingPocket
20from lp.soyuz.interfaces.archivepermission import IArchivePermissionSet
21from lp.soyuz.interfaces.archive import ArchivePurpose
22
23
24class CannotUploadToArchive(Exception):
25 """A reason for not being able to upload to an archive."""
26
27 _fmt = '%(person)s has no upload rights to %(archive)s.'
28
29 def __init__(self, **args):
30 """Construct a `CannotUploadToArchive`."""
31 Exception.__init__(self, self._fmt % args)
32
33
34class CannotUploadToPocket(Exception):
35 """Returned when a pocket is closed for uploads."""
36
37 def __init__(self, distroseries, pocket):
38 Exception.__init__(self,
39 "Not permitted to upload to the %s pocket in a series in the "
40 "'%s' state." % (pocket.name, distroseries.status.name))
41
42
43class CannotUploadToPPA(CannotUploadToArchive):
44 """Raised when a person cannot upload to a PPA."""
45
46 _fmt = 'Signer has no upload rights to this PPA.'
47
48
49class NoRightsForArchive(CannotUploadToArchive):
50 """Raised when a person has absolutely no upload rights to an archive."""
51
52 _fmt = (
53 "The signer of this package has no upload rights to this "
54 "distribution's primary archive. Did you mean to upload to "
55 "a PPA?")
56
57
58class InsufficientUploadRights(CannotUploadToArchive):
59 """Raised when a person has insufficient upload rights."""
60 _fmt = (
61 "The signer of this package is lacking the upload rights for "
62 "the source package, component or package set in question.")
63
64
65class NoRightsForComponent(CannotUploadToArchive):
66 """Raised when a person tries to upload to a component without permission.
67 """
68
69 _fmt = (
70 "Signer is not permitted to upload to the component '%(component)s'.")
71
72 def __init__(self, component):
73 CannotUploadToArchive.__init__(self, component=component.name)
74
75
76class InvalidPocketForPPA(CannotUploadToArchive):
77 """PPAs only support some pockets."""
78
79 _fmt = "PPA uploads must be for the RELEASE pocket."
80
81
82class InvalidPocketForPartnerArchive(CannotUploadToArchive):
83 """Partner archives only support some pockets."""
84
85 _fmt = "Partner uploads must be for the RELEASE or PROPOSED pocket."
86
87
88class ArchiveDisabled(CannotUploadToArchive):
89 """Uploading to a disabled archive is not allowed."""
90
91 _fmt = ("%(archive_name)s is disabled.")
92
93 def __init__(self, archive_name):
94 CannotUploadToArchive.__init__(self, archive_name=archive_name)
95
96
97def components_valid_for(archive, person):
98 """Return the components that 'person' can upload to 'archive'.
99
100 :param archive: The `IArchive` than 'person' wishes to upload to.
101 :param person: An `IPerson` wishing to upload to an archive.
102 :return: A `set` of `IComponent`s that 'person' can upload to.
103 """
104 permission_set = getUtility(IArchivePermissionSet)
105 permissions = permission_set.componentsForUploader(archive, person)
106 return set(permission.component for permission in permissions)
107
108
109def can_upload_to_archive(person, suitesourcepackage, archive=None):
110 """Check if 'person' upload 'suitesourcepackage' to 'archive'.
111
112 :param person: An `IPerson` who might be uploading.
113 :param suitesourcepackage: An `ISuiteSourcePackage` to be uploaded.
114 :param archive: The `IArchive` to upload to. If not provided, defaults
115 to the default archive for the source package. (See
116 `ISourcePackage.get_default_archive`).
117 :return: True if they can, False if they cannot.
118 """
119 sourcepackage = suitesourcepackage.sourcepackage
120 if archive is None:
121 archive = sourcepackage.get_default_archive()
122 pocket = suitesourcepackage.pocket
123 distroseries = sourcepackage.distroseries
124 sourcepackagename = sourcepackage.sourcepackagename
125 component = sourcepackage.latest_published_component
126 # strict_component is True because the source package already exists
127 # (otherwise we couldn't have a suitesourcepackage object) and
128 # nascentupload passes True as a matter of policy when the package exists.
129 reason = check_upload_to_archive(
130 person, distroseries, sourcepackagename, archive, component, pocket,
131 strict_component=True)
132 return reason is None
133
134
135def check_upload_to_pocket(archive, distroseries, pocket):
136 """Check if uploading to a particular pocket in an archive is possible.
137
138 :param archive: A `IArchive`
139 :param distroseries: A `IDistroSeries`
140 :param pocket: A `PackagePublishingPocket`
141 """
142 if archive.purpose == ArchivePurpose.PARTNER:
143 if pocket not in (
144 PackagePublishingPocket.RELEASE,
145 PackagePublishingPocket.PROPOSED):
146 return InvalidPocketForPartnerArchive()
147 elif archive.is_ppa:
148 if pocket != PackagePublishingPocket.RELEASE:
149 return InvalidPocketForPPA()
150 else:
151 # Uploads to the partner archive are allowed in any distroseries
152 # state.
153 # XXX julian 2005-05-29 bug=117557:
154 # This is a greasy hack until bug #117557 is fixed.
155 if not distroseries.canUploadToPocket(pocket):
156 return CannotUploadToPocket(distroseries, pocket)
157
158
159def check_upload_to_archive(person, distroseries, sourcepackagename, archive,
160 component, pocket, strict_component=True):
161 """Check if 'person' upload 'suitesourcepackage' to 'archive'.
162
163 :param person: An `IPerson` who might be uploading.
164 :param distroseries: The `IDistroSeries` being uploaded to.
165 :param sourcepackagename: The `ISourcePackageName` being uploaded.
166 :param archive: The `IArchive` to upload to. If not provided, defaults
167 to the default archive for the source package. (See
168 `ISourcePackage.get_default_archive`).
169 :param component: The `Component` being uploaded to.
170 :param pocket: The `PackagePublishingPocket` of 'distroseries' being
171 uploaded to.
172 :return: The reason for not being able to upload, None otherwise.
173 """
174 reason = check_upload_to_pocket(archive, distroseries, pocket)
175 if reason is not None:
176 return reason
177 return verify_upload(
178 person, sourcepackagename, archive, component, distroseries,
179 strict_component)
180
181
182def packagesets_valid_for(archive, person):
183 """Return the package sets that 'person' can upload to 'archive'.
184
185 :param archive: The `IArchive` than 'person' wishes to upload to.
186 :param person: An `IPerson` wishing to upload to an archive.
187 :return: A `set` of `IPackageset`s that 'person' can upload to.
188 """
189 permission_set = getUtility(IArchivePermissionSet)
190 permissions = permission_set.packagesetsForUploader(archive, person)
191 return set(permission.packageset for permission in permissions)
192
193
194def verify_upload(person, sourcepackagename, archive, component,
195 distroseries, strict_component=True):
196 """Can 'person' upload 'sourcepackagename' to 'archive'?
197
198 :param person: The `IPerson` trying to upload to the package. Referred to
199 as 'the signer' in upload code.
200 :param sourcepackagename: The source package being uploaded. None if the
201 package is new.
202 :param archive: The `IArchive` being uploaded to.
203 :param component: The `IComponent` that the source package belongs to.
204 :param distroseries: The upload's target distro series.
205 :param strict_component: True if access to the specific component for the
206 package is needed to upload to it. If False, then access to any
207 package will do.
208 :return: CannotUploadToArchive if 'person' cannot upload to the archive,
209 None otherwise.
210 """
211 if not archive.enabled:
212 return ArchiveDisabled(archive.displayname)
213
214 # For PPAs...
215 if archive.is_ppa:
216 if not archive.canUpload(person):
217 return CannotUploadToPPA()
218 else:
219 return None
220
221 # For any other archive...
222 ap_set = getUtility(IArchivePermissionSet)
223
224 if sourcepackagename is not None:
225 # Check whether user may upload because he holds a permission for
226 # - the given source package directly
227 # - a package set in the correct distro series that includes the
228 # given source package
229 source_allowed = archive.canUpload(person, sourcepackagename)
230 set_allowed = ap_set.isSourceUploadAllowed(
231 archive, sourcepackagename, person, distroseries)
232 if source_allowed or set_allowed:
233 return None
234
235 if not components_valid_for(archive, person):
236 if not packagesets_valid_for(archive, person):
237 return NoRightsForArchive()
238 else:
239 return InsufficientUploadRights()
240
241 if (component is not None
242 and strict_component
243 and not archive.canUpload(person, component)):
244 return NoRightsForComponent(component)
245
246 return None
2470
=== modified file 'lib/lp/archiveuploader/tests/nascentupload-packageset.txt'
--- lib/lp/archiveuploader/tests/nascentupload-packageset.txt 2009-10-02 15:00:09 +0000
+++ lib/lp/archiveuploader/tests/nascentupload-packageset.txt 2010-05-11 14:12:39 +0000
@@ -39,8 +39,8 @@
39 >>> bar_failed.is_rejected39 >>> bar_failed.is_rejected
40 True40 True
41 >>> print bar_failed.rejection_message41 >>> print bar_failed.rejection_message
42 The signer of this package has no upload rights to this distribution's42 The signer of this package is lacking the upload rights for the source
43 primary archive. Did you mean to upload to a PPA?43 package, component or package set in question.
4444
4545
46We can grant selective, package set based upload permissions to the user46We can grant selective, package set based upload permissions to the user
4747
=== modified file 'lib/lp/archiveuploader/tests/test_permission.py'
--- lib/lp/archiveuploader/tests/test_permission.py 2010-02-08 10:56:18 +0000
+++ lib/lp/archiveuploader/tests/test_permission.py 2010-05-11 14:12:39 +0000
@@ -12,8 +12,6 @@
1212
13from canonical.testing import DatabaseFunctionalLayer13from canonical.testing import DatabaseFunctionalLayer
1414
15from lp.archiveuploader.permission import (
16 check_upload_to_archive, components_valid_for)
17from lp.registry.interfaces.series import SeriesStatus15from lp.registry.interfaces.series import SeriesStatus
18from lp.registry.interfaces.pocket import PackagePublishingPocket16from lp.registry.interfaces.pocket import PackagePublishingPocket
19from lp.soyuz.interfaces.archive import ArchivePurpose17from lp.soyuz.interfaces.archive import ArchivePurpose
@@ -29,21 +27,22 @@
29 # By default, a person cannot upload to any component of an archive.27 # By default, a person cannot upload to any component of an archive.
30 archive = self.factory.makeArchive()28 archive = self.factory.makeArchive()
31 person = self.factory.makePerson()29 person = self.factory.makePerson()
32 self.assertEqual(set(), components_valid_for(archive, person))30 self.assertEqual(set(),
31 set(archive.getComponentsForUploader(person)))
3332
34 def test_components_for_person_with_permissions(self):33 def test_components_for_person_with_permissions(self):
35 # If a person has been explicitly granted upload permissions to a34 # If a person has been explicitly granted upload permissions to a
36 # particular component, then those components are included in35 # particular component, then those components are included in
37 # components_valid_for.36 # IArchive.getComponentsForUploader.
38 archive = self.factory.makeArchive()37 archive = self.factory.makeArchive()
39 component = self.factory.makeComponent()38 component = self.factory.makeComponent()
40 person = self.factory.makePerson()39 person = self.factory.makePerson()
41 # Only admins or techboard members can add permissions normally. That40 # Only admins or techboard members can add permissions normally. That
42 # restriction isn't relevant to this test.41 # restriction isn't relevant to this test.
43 ap_set = removeSecurityProxy(getUtility(IArchivePermissionSet))42 ap_set = removeSecurityProxy(getUtility(IArchivePermissionSet))
44 ap_set.newComponentUploader(archive, person, component)43 ap = ap_set.newComponentUploader(archive, person, component)
45 self.assertEqual(44 self.assertEqual(set([ap]),
46 set([component]), components_valid_for(archive, person))45 set(archive.getComponentsForUploader(person)))
4746
4847
49class TestPermission(TestCaseWithFactory):48class TestPermission(TestCaseWithFactory):
@@ -67,8 +66,8 @@
67 pocket = PackagePublishingPocket.RELEASE66 pocket = PackagePublishingPocket.RELEASE
68 self.assertIs(67 self.assertIs(
69 None,68 None,
70 check_upload_to_archive(69 archive.checkUpload(
71 person, distroseries, spn, archive, component, pocket,70 person, distroseries, spn, component, pocket,
72 strict_component))71 strict_component))
7372
74 def assertCannotUpload(self, reason, person, spn, archive, component,73 def assertCannotUpload(self, reason, person, spn, archive, component,
@@ -87,8 +86,8 @@
87 if distroseries is None:86 if distroseries is None:
88 distroseries = self.factory.makeDistroSeries()87 distroseries = self.factory.makeDistroSeries()
89 pocket = PackagePublishingPocket.RELEASE88 pocket = PackagePublishingPocket.RELEASE
90 exception = check_upload_to_archive(89 exception = archive.checkUpload(
91 person, distroseries, spn, archive, component, pocket)90 person, distroseries, spn, component, pocket)
92 self.assertEqual(reason, str(exception))91 self.assertEqual(reason, str(exception))
9392
94 def test_random_person_cannot_upload_to_ppa(self):93 def test_random_person_cannot_upload_to_ppa(self):
@@ -137,9 +136,8 @@
137 archive = self.factory.makeArchive(purpose=ArchivePurpose.PRIMARY)136 archive = self.factory.makeArchive(purpose=ArchivePurpose.PRIMARY)
138 spn = self.factory.makeSourcePackageName()137 spn = self.factory.makeSourcePackageName()
139 self.assertCannotUpload(138 self.assertCannotUpload(
140 ("The signer of this package has no upload rights to this "139 ("The signer of this package is lacking the upload rights for "
141 "distribution's primary archive. Did you mean to upload to "140 "the source package, component or package set in question."),
142 "a PPA?"),
143 person, spn, archive, None)141 person, spn, archive, None)
144142
145 def test_package_specific_rights(self):143 def test_package_specific_rights(self):
146144
=== modified file 'lib/lp/bugs/model/bugnomination.py'
--- lib/lp/bugs/model/bugnomination.py 2009-08-26 09:31:57 +0000
+++ lib/lp/bugs/model/bugnomination.py 2010-05-11 14:12:39 +0000
@@ -144,7 +144,7 @@
144 upload_component.component144 upload_component.component
145 for upload_component in distribution.uploaders)145 for upload_component in distribution.uploaders)
146 for packagename_or_component in bug_packagenames_and_components:146 for packagename_or_component in bug_packagenames_and_components:
147 if distribution.main_archive.canUpload(147 if distribution.main_archive.checkArchivePermission(
148 person, packagename_or_component):148 person, packagename_or_component):
149 return True149 return True
150150
151151
=== modified file 'lib/lp/code/model/recipebuilder.py'
--- lib/lp/code/model/recipebuilder.py 2010-04-29 14:54:05 +0000
+++ lib/lp/code/model/recipebuilder.py 2010-05-11 14:12:39 +0000
@@ -15,7 +15,6 @@
1515
16from canonical.config import config16from canonical.config import config
1717
18from lp.archiveuploader.permission import check_upload_to_pocket
19from lp.buildmaster.interfaces.buildfarmjobbehavior import (18from lp.buildmaster.interfaces.buildfarmjobbehavior import (
20 IBuildFarmJobBehavior)19 IBuildFarmJobBehavior)
21from lp.buildmaster.interfaces.builder import CannotBuild20from lp.buildmaster.interfaces.builder import CannotBuild
@@ -158,8 +157,8 @@
158157
159 # This should already have been checked earlier, but just check again158 # This should already have been checked earlier, but just check again
160 # here in case of programmer errors.159 # here in case of programmer errors.
161 reason = check_upload_to_pocket(160 reason = build.archive.checkUploadToPocket(
162 build.archive, build.distroseries, build.pocket)161 build.distroseries, build.pocket)
163 assert reason is None, (162 assert reason is None, (
164 "%s (%s) can not be built for pocket %s: invalid pocket due "163 "%s (%s) can not be built for pocket %s: invalid pocket due "
165 "to the series status of %s." %164 "to the series status of %s." %
166165
=== modified file 'lib/lp/code/model/sourcepackagerecipe.py'
--- lib/lp/code/model/sourcepackagerecipe.py 2010-04-30 12:33:16 +0000
+++ lib/lp/code/model/sourcepackagerecipe.py 2010-05-11 14:12:39 +0000
@@ -22,7 +22,6 @@
22from canonical.database.datetimecol import UtcDateTimeCol22from canonical.database.datetimecol import UtcDateTimeCol
23from canonical.launchpad.interfaces.lpstorm import IMasterStore23from canonical.launchpad.interfaces.lpstorm import IMasterStore
2424
25from lp.archiveuploader.permission import check_upload_to_archive
26from lp.code.interfaces.sourcepackagerecipe import (25from lp.code.interfaces.sourcepackagerecipe import (
27 ISourcePackageRecipe, ISourcePackageRecipeSource,26 ISourcePackageRecipe, ISourcePackageRecipeSource,
28 ISourcePackageRecipeData)27 ISourcePackageRecipeData)
@@ -153,9 +152,9 @@
153 if archive.purpose != ArchivePurpose.PPA:152 if archive.purpose != ArchivePurpose.PPA:
154 raise NonPPABuildRequest153 raise NonPPABuildRequest
155 component = getUtility(IComponentSet)["multiverse"]154 component = getUtility(IComponentSet)["multiverse"]
156 reject_reason = check_upload_to_archive(155 reject_reason = archive.checkUpload(
157 requester, distroseries, self.sourcepackagename,156 requester, self.distroseries, self.sourcepackagename,
158 archive, component, pocket)157 component, pocket)
159 if reject_reason is not None:158 if reject_reason is not None:
160 raise reject_reason159 raise reject_reason
161160
162161
=== modified file 'lib/lp/code/model/tests/test_sourcepackagerecipe.py'
--- lib/lp/code/model/tests/test_sourcepackagerecipe.py 2010-04-30 12:33:16 +0000
+++ lib/lp/code/model/tests/test_sourcepackagerecipe.py 2010-05-11 14:12:39 +0000
@@ -24,8 +24,8 @@
24from canonical.testing.layers import DatabaseFunctionalLayer, AppServerLayer24from canonical.testing.layers import DatabaseFunctionalLayer, AppServerLayer
2525
26from canonical.launchpad.webapp.authorization import check_permission26from canonical.launchpad.webapp.authorization import check_permission
27from lp.archiveuploader.permission import (27from lp.soyuz.interfaces.archive import (
28 ArchiveDisabled, CannotUploadToArchive, InvalidPocketForPPA)28 ArchiveDisabled, ArchivePurpose, CannotUploadToArchive, InvalidPocketForPPA)
29from lp.buildmaster.interfaces.buildqueue import IBuildQueue29from lp.buildmaster.interfaces.buildqueue import IBuildQueue
30from lp.buildmaster.model.buildqueue import BuildQueue30from lp.buildmaster.model.buildqueue import BuildQueue
31from lp.code.interfaces.sourcepackagerecipe import (31from lp.code.interfaces.sourcepackagerecipe import (
@@ -40,7 +40,6 @@
40from lp.registry.interfaces.pocket import PackagePublishingPocket40from lp.registry.interfaces.pocket import PackagePublishingPocket
41from lp.services.job.interfaces.job import (41from lp.services.job.interfaces.job import (
42 IJob, JobStatus)42 IJob, JobStatus)
43from lp.soyuz.interfaces.archive import ArchivePurpose
44from lp.testing import (43from lp.testing import (
45 ANONYMOUS, launchpadlib_for, login, login_person, person_logged_in,44 ANONYMOUS, launchpadlib_for, login, login_person, person_logged_in,
46 TestCaseWithFactory, ws_object)45 TestCaseWithFactory, ws_object)
4746
=== modified file 'lib/lp/code/tests/test_branch.py'
--- lib/lp/code/tests/test_branch.py 2010-03-18 17:49:21 +0000
+++ lib/lp/code/tests/test_branch.py 2010-05-11 14:12:39 +0000
@@ -12,7 +12,6 @@
12from canonical.launchpad.webapp.authorization import check_permission12from canonical.launchpad.webapp.authorization import check_permission
13from canonical.testing import DatabaseFunctionalLayer13from canonical.testing import DatabaseFunctionalLayer
1414
15from lp.archiveuploader.permission import verify_upload
16from lp.code.enums import (15from lp.code.enums import (
17 BranchSubscriptionDiffSize, BranchSubscriptionNotificationLevel,16 BranchSubscriptionDiffSize, BranchSubscriptionNotificationLevel,
18 CodeReviewNotificationLevel)17 CodeReviewNotificationLevel)
@@ -277,8 +276,8 @@
277 distroseries = archive.distribution.currentseries276 distroseries = archive.distribution.currentseries
278 self.assertIs(277 self.assertIs(
279 None,278 None,
280 verify_upload(279 archive.verifyUpload(
281 person, spn, archive, component, distroseries,280 person, spn, component, distroseries,
282 strict_component))281 strict_component))
283282
284 def assertCannotUpload(283 def assertCannotUpload(
@@ -295,8 +294,8 @@
295 """294 """
296 if distroseries is None:295 if distroseries is None:
297 distroseries = archive.distribution.currentseries296 distroseries = archive.distribution.currentseries
298 exception = verify_upload(297 exception = archive.verifyUpload(
299 person, spn, archive, component, distroseries)298 person, spn, component, distroseries)
300 self.assertEqual(reason, str(exception))299 self.assertEqual(reason, str(exception))
301300
302 def test_package_upload_permissions_grant_branch_edit(self):301 def test_package_upload_permissions_grant_branch_edit(self):
303302
=== modified file 'lib/lp/soyuz/browser/archive.py'
--- lib/lp/soyuz/browser/archive.py 2010-04-30 03:10:17 +0000
+++ lib/lp/soyuz/browser/archive.py 2010-05-11 14:12:39 +0000
@@ -1236,7 +1236,8 @@
1236 # XXX cprov 2009-07-17 bug=385503: copies cannot be properly traced1236 # XXX cprov 2009-07-17 bug=385503: copies cannot be properly traced
1237 # that's why we explicitly don't allow them do be done via the UI1237 # that's why we explicitly don't allow them do be done via the UI
1238 # in main archives, only PPAs.1238 # in main archives, only PPAs.
1239 return self.context.is_ppa and self.context.canUpload(self.user)1239 return (self.context.is_ppa and
1240 self.context.checkArchivePermission(self.user))
12401241
1241 def createDestinationArchiveField(self):1242 def createDestinationArchiveField(self):
1242 """Create the 'destination_archive' field."""1243 """Create the 'destination_archive' field."""
12431244
=== modified file 'lib/lp/soyuz/doc/archive.txt'
--- lib/lp/soyuz/doc/archive.txt 2010-05-06 10:05:49 +0000
+++ lib/lp/soyuz/doc/archive.txt 2010-05-11 14:12:39 +0000
@@ -276,7 +276,7 @@
276276
277Uploads to copy archives are not allowed.277Uploads to copy archives are not allowed.
278278
279 >>> rebuild_archive.canUpload(cprov)279 >>> rebuild_archive.checkArchivePermission(cprov)
280 Traceback (most recent call last):280 Traceback (most recent call last):
281 ...281 ...
282 AssertionError: Uploads to copy archives are not allowed.282 AssertionError: Uploads to copy archives are not allowed.
@@ -2106,7 +2106,7 @@
21062106
2107== Archive Permission Checking ==2107== Archive Permission Checking ==
21082108
2109IArchive has two public methods, canUpload() and canAdministerQueue()2109IArchive has two public methods, checkArchivePermission() and canAdministerQueue()
2110that check a user's permission to upload and/or administer a2110that check a user's permission to upload and/or administer a
2111distroseries upload queue respectively. See archivepermission.txt2111distroseries upload queue respectively. See archivepermission.txt
2112for more details.2112for more details.
@@ -2114,40 +2114,40 @@
2114 >>> ubuntu_team = getUtility(IPersonSet).getByName('ubuntu-team')2114 >>> ubuntu_team = getUtility(IPersonSet).getByName('ubuntu-team')
2115 >>> carlos = getUtility(IPersonSet).getByName('carlos')2115 >>> carlos = getUtility(IPersonSet).getByName('carlos')
21162116
2117 >>> ubuntu.main_archive.canUpload(carlos, main_component)2117 >>> ubuntu.main_archive.checkArchivePermission(carlos, main_component)
2118 False2118 False
21192119
2120 >>> ubuntu.main_archive.canAdministerQueue(carlos, main_component)2120 >>> ubuntu.main_archive.canAdministerQueue(carlos, main_component)
2121 False2121 False
21222122
2123 >>> ubuntu.main_archive.canUpload(ubuntu_team, main_component)2123 >>> ubuntu.main_archive.checkArchivePermission(ubuntu_team, main_component)
2124 True2124 True
21252125
2126 >>> ubuntu.main_archive.canAdministerQueue(ubuntu_team, main_component)2126 >>> ubuntu.main_archive.canAdministerQueue(ubuntu_team, main_component)
2127 True2127 True
21282128
2129canUpload() can also check someone's permission to upload a specific2129checkArchivePermission() can also check someone's permission to upload a specific
2130source package. Carlos, who does not have permission to upload to any2130source package. Carlos, who does not have permission to upload to any
2131Ubuntu components, has permission to upload "mozilla-firefox".2131Ubuntu components, has permission to upload "mozilla-firefox".
21322132
2133 >>> from canonical.launchpad.interfaces import ISourcePackageNameSet2133 >>> from canonical.launchpad.interfaces import ISourcePackageNameSet
2134 >>> mozilla = getUtility(2134 >>> mozilla = getUtility(
2135 ... ISourcePackageNameSet).queryByName("mozilla-firefox")2135 ... ISourcePackageNameSet).queryByName("mozilla-firefox")
2136 >>> ubuntu.main_archive.canUpload(carlos, mozilla)2136 >>> ubuntu.main_archive.checkArchivePermission(carlos, mozilla)
2137 True2137 True
21382138
2139Cprov does not have permission, however.2139Cprov does not have permission, however.
21402140
2141 >>> ubuntu.main_archive.canUpload(cprov, mozilla)2141 >>> ubuntu.main_archive.checkArchivePermission(cprov, mozilla)
2142 False2142 False
21432143
2144canUpload() also works in the same way for PPAs. By default, it allows2144checkArchivePermission() also works in the same way for PPAs. By default, it allows
2145anyone in the PPA owning team to upload.2145anyone in the PPA owning team to upload.
21462146
2147 >>> cprov_archive.canUpload(cprov)2147 >>> cprov_archive.checkArchivePermission(cprov)
2148 True2148 True
21492149
2150 >>> cprov_archive.canUpload(carlos)2150 >>> cprov_archive.checkArchivePermission(carlos)
2151 False2151 False
21522152
2153We can also create an ArchivePermission entry for carlos to be able to upload2153We can also create an ArchivePermission entry for carlos to be able to upload
@@ -2159,7 +2159,7 @@
21592159
2160Carlos can now upload to Joe's PPA:2160Carlos can now upload to Joe's PPA:
21612161
2162 >>> joes_ppa.canUpload(carlos)2162 >>> joes_ppa.checkArchivePermission(carlos)
2163 True2163 True
21642164
2165Note that when creating a new permission, trying to specify a component other2165Note that when creating a new permission, trying to specify a component other
21662166
=== modified file 'lib/lp/soyuz/doc/archivepermission.txt'
--- lib/lp/soyuz/doc/archivepermission.txt 2010-03-08 17:06:41 +0000
+++ lib/lp/soyuz/doc/archivepermission.txt 2010-05-11 14:12:39 +0000
@@ -108,7 +108,7 @@
108 ... ubuntu)108 ... ubuntu)
109 Traceback (most recent call last):109 Traceback (most recent call last):
110 ...110 ...
111 AssertionError: 'item' is not an IComponent, IPackageset or an111 AssertionError: 'item' ... is not an IComponent, IPackageset or an
112 ISourcePackageName112 ISourcePackageName
113113
114IArchivePermissionSet also has some helpers to make it very easy to114IArchivePermissionSet also has some helpers to make it very easy to
115115
=== modified file 'lib/lp/soyuz/doc/nascentupload.txt'
--- lib/lp/soyuz/doc/nascentupload.txt 2010-03-11 16:09:29 +0000
+++ lib/lp/soyuz/doc/nascentupload.txt 2010-05-11 14:12:39 +0000
@@ -879,9 +879,8 @@
879 >>> bar_failed.is_rejected879 >>> bar_failed.is_rejected
880 True880 True
881 >>> print bar_failed.rejection_message881 >>> print bar_failed.rejection_message
882 The signer of this package has no upload rights to this distribution's882 The signer of this package is lacking the upload rights for the source
883 primary archive. Did you mean to upload to a PPA?883 package, component or package set in question.
884
885884
886Even in a rejected upload using 'insecure' policy, the DSC signing key885Even in a rejected upload using 'insecure' policy, the DSC signing key
887and the changesfile sigining key are stored in NascentUpload instance886and the changesfile sigining key are stored in NascentUpload instance
888887
=== modified file 'lib/lp/soyuz/interfaces/archive.py'
--- lib/lp/soyuz/interfaces/archive.py 2010-03-26 17:30:33 +0000
+++ lib/lp/soyuz/interfaces/archive.py 2010-05-11 14:12:39 +0000
@@ -11,12 +11,15 @@
11 'ALLOW_RELEASE_BUILDS',11 'ALLOW_RELEASE_BUILDS',
12 'AlreadySubscribed',12 'AlreadySubscribed',
13 'ArchiveDependencyError',13 'ArchiveDependencyError',
14 'ArchiveDisabled',
14 'ArchiveNotPrivate',15 'ArchiveNotPrivate',
15 'ArchivePurpose',16 'ArchivePurpose',
16 'ArchiveStatus',17 'ArchiveStatus',
17 'CannotCopy',18 'CannotCopy',
18 'CannotSwitchPrivacy',19 'CannotSwitchPrivacy',
19 'ComponentNotFound',20 'ComponentNotFound',
21 'CannotUploadToPPA',
22 'CannotUploadToPocket',
20 'DistroSeriesNotFound',23 'DistroSeriesNotFound',
21 'IArchive',24 'IArchive',
22 'IArchiveAppend',25 'IArchiveAppend',
@@ -26,10 +29,15 @@
26 'IArchivePublic',29 'IArchivePublic',
27 'IArchiveSet',30 'IArchiveSet',
28 'IDistributionArchive',31 'IDistributionArchive',
32 'InsufficientUploadRights',
29 'InvalidComponent',33 'InvalidComponent',
34 'InvalidPocketForPartnerArchive',
35 'InvalidPocketForPPA',
30 'IPPA',36 'IPPA',
31 'IPPAActivateForm',37 'IPPAActivateForm',
32 'MAIN_ARCHIVE_PURPOSES',38 'MAIN_ARCHIVE_PURPOSES',
39 'NoRightsForArchive',
40 'NoRightsForComponent',
33 'NoSuchPPA',41 'NoSuchPPA',
34 'NoTokensForTeams',42 'NoTokensForTeams',
35 'PocketNotFound',43 'PocketNotFound',
@@ -133,6 +141,79 @@
133 webservice_error(400) # Bad request.141 webservice_error(400) # Bad request.
134142
135143
144class CannotUploadToArchive(Exception):
145 """A reason for not being able to upload to an archive."""
146
147 _fmt = '%(person)s has no upload rights to %(archive)s.'
148
149 def __init__(self, **args):
150 """Construct a `CannotUploadToArchive`."""
151 Exception.__init__(self, self._fmt % args)
152
153
154class InvalidPocketForPartnerArchive(CannotUploadToArchive):
155 """Partner archives only support some pockets."""
156
157 _fmt = "Partner uploads must be for the RELEASE or PROPOSED pocket."
158
159
160class CannotUploadToPocket(Exception):
161 """Returned when a pocket is closed for uploads."""
162
163 def __init__(self, distroseries, pocket):
164 Exception.__init__(self,
165 "Not permitted to upload to the %s pocket in a series in the "
166 "'%s' state." % (pocket.name, distroseries.status.name))
167
168
169class CannotUploadToPPA(CannotUploadToArchive):
170 """Raised when a person cannot upload to a PPA."""
171
172 _fmt = 'Signer has no upload rights to this PPA.'
173
174
175class NoRightsForArchive(CannotUploadToArchive):
176 """Raised when a person has absolutely no upload rights to an archive."""
177
178 _fmt = (
179 "The signer of this package has no upload rights to this "
180 "distribution's primary archive. Did you mean to upload to "
181 "a PPA?")
182
183
184class InsufficientUploadRights(CannotUploadToArchive):
185 """Raised when a person has insufficient upload rights."""
186 _fmt = (
187 "The signer of this package is lacking the upload rights for "
188 "the source package, component or package set in question.")
189
190
191class NoRightsForComponent(CannotUploadToArchive):
192 """Raised when a person tries to upload to a component without permission.
193 """
194
195 _fmt = (
196 "Signer is not permitted to upload to the component '%(component)s'.")
197
198 def __init__(self, component):
199 CannotUploadToArchive.__init__(self, component=component.name)
200
201
202class InvalidPocketForPPA(CannotUploadToArchive):
203 """PPAs only support some pockets."""
204
205 _fmt = "PPA uploads must be for the RELEASE pocket."
206
207
208class ArchiveDisabled(CannotUploadToArchive):
209 """Uploading to a disabled archive is not allowed."""
210
211 _fmt = ("%(archive_name)s is disabled.")
212
213 def __init__(self, archive_name):
214 CannotUploadToArchive.__init__(self, archive_name=archive_name)
215
216
136class IArchivePublic(IHasOwner, IPrivacy):217class IArchivePublic(IHasOwner, IPrivacy):
137 """An Archive interface for publicly available operations."""218 """An Archive interface for publicly available operations."""
138 id = Attribute("The archive ID.")219 id = Attribute("The archive ID.")
@@ -380,7 +461,7 @@
380 :return: A list of `IArchivePermission` records.461 :return: A list of `IArchivePermission` records.
381 """462 """
382463
383 def canUpload(person, component_or_package=None):464 def checkArchivePermission(person, component_or_package=None):
384 """Check to see if person is allowed to upload to component.465 """Check to see if person is allowed to upload to component.
385466
386 :param person: An `IPerson` whom should be checked for authentication.467 :param person: An `IPerson` whom should be checked for authentication.
@@ -395,6 +476,53 @@
395476
396 """477 """
397478
479 def canUploadSuiteSourcePackage(person, suitesourcepackage):
480 """Check if 'person' upload 'suitesourcepackage' to 'archive'.
481
482 :param person: An `IPerson` who might be uploading.
483 :param suitesourcepackage: An `ISuiteSourcePackage` to be uploaded.
484 :return: True if they can, False if they cannot.
485 """
486
487 def checkUploadToPocket(distroseries, pocket):
488 """Check if uploading to a particular pocket in an archive is possible.
489
490 :param distroseries: A `IDistroSeries`
491 :param pocket: A `PackagePublishingPocket`
492 :return: Reason why uploading is not possible or None
493 """
494
495 def checkUpload(person, distroseries, sourcepackagename, component,
496 pocket, strict_component=True):
497 """Check if 'person' upload 'suitesourcepackage' to 'archive'.
498
499 :param person: An `IPerson` who might be uploading.
500 :param distroseries: The `IDistroSeries` being uploaded to.
501 :param sourcepackagename: The `ISourcePackageName` being uploaded.
502 :param component: The `Component` being uploaded to.
503 :param pocket: The `PackagePublishingPocket` of 'distroseries' being
504 uploaded to.
505 :return: The reason for not being able to upload, None otherwise.
506 """
507
508 def verifyUpload(person, sourcepackagename, component,
509 distroseries, strict_component=True):
510 """Can 'person' upload 'sourcepackagename' to this archive ?
511
512 :param person: The `IPerson` trying to upload to the package. Referred to
513 as 'the signer' in upload code.
514 :param sourcepackagename: The source package being uploaded. None if the
515 package is new.
516 :param archive: The `IArchive` being uploaded to.
517 :param component: The `IComponent` that the source package belongs to.
518 :param distroseries: The upload's target distro series.
519 :param strict_component: True if access to the specific component for the
520 package is needed to upload to it. If False, then access to any
521 package will do.
522 :return: CannotUploadToArchive if 'person' cannot upload to the archive,
523 None otherwise.
524 """
525
398 def canAdministerQueue(person, component):526 def canAdministerQueue(person, component):
399 """Check to see if person is allowed to administer queue items.527 """Check to see if person is allowed to administer queue items.
400528
@@ -564,6 +692,13 @@
564 'person' is allowed to upload to.692 'person' is allowed to upload to.
565 """693 """
566694
695 def getComponentsForUploader(person):
696 """Return the components that 'person' can upload to this archive.
697
698 :param person: An `IPerson` wishing to upload to an archive.
699 :return: A `set` of `IComponent`s that 'person' can upload to.
700 """
701
567 @operation_parameters(702 @operation_parameters(
568 sourcepackagename=TextLine(703 sourcepackagename=TextLine(
569 title=_("Source package name"), required=True),704 title=_("Source package name"), required=True),
570705
=== modified file 'lib/lp/soyuz/model/archive.py'
--- lib/lp/soyuz/model/archive.py 2010-05-06 10:05:49 +0000
+++ lib/lp/soyuz/model/archive.py 2010-05-11 14:12:39 +0000
@@ -58,11 +58,14 @@
58from lp.soyuz.model.sourcepackagerelease import SourcePackageRelease58from lp.soyuz.model.sourcepackagerelease import SourcePackageRelease
59from lp.registry.model.teammembership import TeamParticipation59from lp.registry.model.teammembership import TeamParticipation
60from lp.soyuz.interfaces.archive import (60from lp.soyuz.interfaces.archive import (
61 AlreadySubscribed, ArchiveDependencyError, ArchiveNotPrivate,61 AlreadySubscribed, ArchiveDependencyError, ArchiveDisabled,
62 ArchivePurpose, ArchiveStatus, CannotCopy, CannotSwitchPrivacy,62 ArchiveNotPrivate, ArchivePurpose, ArchiveStatus, CannotCopy,
63 CannotSwitchPrivacy, CannotUploadToPPA, CannotUploadToPocket,
63 DistroSeriesNotFound, IArchive, IArchiveSet, IDistributionArchive,64 DistroSeriesNotFound, IArchive, IArchiveSet, IDistributionArchive,
64 InvalidComponent, IPPA, MAIN_ARCHIVE_PURPOSES, NoSuchPPA,65 InsufficientUploadRights, InvalidPocketForPPA,
65 NoTokensForTeams, PocketNotFound, VersionRequiresName,66 InvalidPocketForPartnerArchive, InvalidComponent, IPPA,
67 MAIN_ARCHIVE_PURPOSES, NoRightsForArchive, NoRightsForComponent,
68 NoSuchPPA, NoTokensForTeams, PocketNotFound, VersionRequiresName,
66 default_name_by_purpose)69 default_name_by_purpose)
67from lp.soyuz.interfaces.archiveauthtoken import IArchiveAuthTokenSet70from lp.soyuz.interfaces.archiveauthtoken import IArchiveAuthTokenSet
68from lp.soyuz.interfaces.archivearch import IArchiveArchSet71from lp.soyuz.interfaces.archivearch import IArchiveArchSet
@@ -954,7 +957,7 @@
954 source_ids,957 source_ids,
955 archive=self)958 archive=self)
956959
957 def canUpload(self, user, component_or_package=None):960 def checkArchivePermission(self, user, component_or_package=None):
958 """See `IArchive`."""961 """See `IArchive`."""
959 assert not self.is_copy, "Uploads to copy archives are not allowed."962 assert not self.is_copy, "Uploads to copy archives are not allowed."
960 # PPA access is immediately granted if the user is in the PPA963 # PPA access is immediately granted if the user is in the PPA
@@ -977,6 +980,87 @@
977 return self._authenticate(980 return self._authenticate(
978 user, component_or_package, ArchivePermissionType.UPLOAD)981 user, component_or_package, ArchivePermissionType.UPLOAD)
979982
983 def canUploadSuiteSourcePackage(self, person, suitesourcepackage):
984 """See `IArchive`."""
985 sourcepackage = suitesourcepackage.sourcepackage
986 pocket = suitesourcepackage.pocket
987 distroseries = sourcepackage.distroseries
988 sourcepackagename = sourcepackage.sourcepackagename
989 component = sourcepackage.latest_published_component
990 # strict_component is True because the source package already exists
991 # (otherwise we couldn't have a suitesourcepackage object) and
992 # nascentupload passes True as a matter of policy when the package exists.
993 reason = self.checkUpload(
994 person, distroseries, sourcepackagename, component, pocket,
995 strict_component=True)
996 return reason is None
997
998 def checkUploadToPocket(self, distroseries, pocket):
999 """See `IArchive`."""
1000 if self.purpose == ArchivePurpose.PARTNER:
1001 if pocket not in (
1002 PackagePublishingPocket.RELEASE,
1003 PackagePublishingPocket.PROPOSED):
1004 return InvalidPocketForPartnerArchive()
1005 elif self.is_ppa:
1006 if pocket != PackagePublishingPocket.RELEASE:
1007 return InvalidPocketForPPA()
1008 else:
1009 # Uploads to the partner archive are allowed in any distroseries
1010 # state.
1011 # XXX julian 2005-05-29 bug=117557:
1012 # This is a greasy hack until bug #117557 is fixed.
1013 if not distroseries.canUploadToPocket(pocket):
1014 return CannotUploadToPocket(distroseries, pocket)
1015
1016 def checkUpload(self, person, distroseries, sourcepackagename, component,
1017 pocket, strict_component=True):
1018 """See `IArchive`."""
1019 reason = self.checkUploadToPocket(distroseries, pocket)
1020 if reason is not None:
1021 return reason
1022 return self.verifyUpload(
1023 person, sourcepackagename, component, distroseries,
1024 strict_component)
1025
1026 def verifyUpload(self, person, sourcepackagename, component,
1027 distroseries, strict_component=True):
1028 """See `IArchive`."""
1029 if not self.enabled:
1030 return ArchiveDisabled(self.displayname)
1031
1032 # For PPAs...
1033 if self.is_ppa:
1034 if not self.checkArchivePermission(person):
1035 return CannotUploadToPPA()
1036 else:
1037 return None
1038
1039 if sourcepackagename is not None:
1040 # Check whether user may upload because they hold a permission for
1041 # - the given source package directly
1042 # - a package set in the correct distro series that includes the
1043 # given source package
1044 source_allowed = self.checkArchivePermission(person,
1045 sourcepackagename)
1046 set_allowed = self.isSourceUploadAllowed(
1047 sourcepackagename, person, distroseries)
1048 if source_allowed or set_allowed:
1049 return None
1050
1051 if not self.getComponentsForUploader(person):
1052 if not self.getPackagesetsForUploader(person):
1053 return NoRightsForArchive()
1054 else:
1055 return InsufficientUploadRights()
1056
1057 if (component is not None
1058 and strict_component
1059 and not self.checkArchivePermission(person, component)):
1060 return NoRightsForComponent(component)
1061
1062 return None
1063
980 def canAdministerQueue(self, user, component):1064 def canAdministerQueue(self, user, component):
981 """See `IArchive`."""1065 """See `IArchive`."""
982 return self._authenticate(1066 return self._authenticate(
@@ -1050,6 +1134,11 @@
1050 return permission_set.deletePackagesetUploader(1134 return permission_set.deletePackagesetUploader(
1051 self, person, packageset, explicit)1135 self, person, packageset, explicit)
10521136
1137 def getComponentsForUploader(self, person):
1138 """See `IArchive`."""
1139 permission_set = getUtility(IArchivePermissionSet)
1140 return permission_set.componentsForUploader(self, person)
1141
1053 def getPackagesetsForUploader(self, person):1142 def getPackagesetsForUploader(self, person):
1054 """See `IArchive`."""1143 """See `IArchive`."""
1055 permission_set = getUtility(IArchivePermissionSet)1144 permission_set = getUtility(IArchivePermissionSet)
10561145
=== modified file 'lib/lp/soyuz/model/archivepermission.py'
--- lib/lp/soyuz/model/archivepermission.py 2010-03-08 17:06:41 +0000
+++ lib/lp/soyuz/model/archivepermission.py 2010-05-11 14:12:39 +0000
@@ -151,8 +151,8 @@
151 prejoins.append("packageset")151 prejoins.append("packageset")
152 else:152 else:
153 raise AssertionError(153 raise AssertionError(
154 "'item' is not an IComponent, IPackageset or an "154 "'item' %r is not an IComponent, IPackageset or an "
155 "ISourcePackageName")155 "ISourcePackageName" % item)
156156
157 query = " AND ".join(clauses)157 query = " AND ".join(clauses)
158 auth = ArchivePermission.select(158 auth = ArchivePermission.select(
159159
=== modified file 'lib/lp/soyuz/model/binarypackagebuildbehavior.py'
--- lib/lp/soyuz/model/binarypackagebuildbehavior.py 2010-04-12 05:52:01 +0000
+++ lib/lp/soyuz/model/binarypackagebuildbehavior.py 2010-05-11 14:12:39 +0000
@@ -15,7 +15,6 @@
1515
16from canonical.launchpad.webapp import urlappend16from canonical.launchpad.webapp import urlappend
1717
18from lp.archiveuploader.permission import check_upload_to_pocket
19from lp.buildmaster.interfaces.buildfarmjobbehavior import (18from lp.buildmaster.interfaces.buildfarmjobbehavior import (
20 IBuildFarmJobBehavior)19 IBuildFarmJobBehavior)
21from lp.buildmaster.model.buildfarmjobbehavior import (20from lp.buildmaster.model.buildfarmjobbehavior import (
@@ -123,8 +122,8 @@
123122
124 # This should already have been checked earlier, but just check again 123 # This should already have been checked earlier, but just check again
125 # here in case of programmer errors.124 # here in case of programmer errors.
126 reason = check_upload_to_pocket(125 reason = build.archive.checkUploadToPocket(build.distroseries,
127 build.archive, build.distroseries, build.pocket)126 build.pocket)
128 assert reason is None, (127 assert reason is None, (
129 "%s (%s) can not be built for pocket %s: invalid pocket due "128 "%s (%s) can not be built for pocket %s: invalid pocket due "
130 "to the series status of %s." %129 "to the series status of %s." %
131130
=== modified file 'lib/lp/soyuz/tests/test_archive.py'
--- lib/lp/soyuz/tests/test_archive.py 2010-05-03 07:48:54 +0000
+++ lib/lp/soyuz/tests/test_archive.py 2010-05-11 14:12:39 +0000
@@ -18,13 +18,16 @@
18from lp.buildmaster.interfaces.buildbase import BuildStatus18from lp.buildmaster.interfaces.buildbase import BuildStatus
19from lp.registry.interfaces.distribution import IDistributionSet19from lp.registry.interfaces.distribution import IDistributionSet
20from lp.registry.interfaces.person import IPersonSet20from lp.registry.interfaces.person import IPersonSet
21from lp.registry.interfaces.pocket import PackagePublishingPocket
21from lp.services.job.interfaces.job import JobStatus22from lp.services.job.interfaces.job import JobStatus
23from lp.soyuz.interfaces.archive import (IArchiveSet, ArchivePurpose,
24 ArchiveStatus, CannotSwitchPrivacy, InvalidPocketForPartnerArchive,
25 InvalidPocketForPPA)
22from lp.services.worlddata.interfaces.country import ICountrySet26from lp.services.worlddata.interfaces.country import ICountrySet
23from lp.soyuz.interfaces.archive import (
24 IArchiveSet, ArchivePurpose, ArchiveStatus, CannotSwitchPrivacy)
25from lp.soyuz.interfaces.archivearch import IArchiveArchSet27from lp.soyuz.interfaces.archivearch import IArchiveArchSet
26from lp.soyuz.interfaces.binarypackagename import IBinaryPackageNameSet28from lp.soyuz.interfaces.binarypackagename import IBinaryPackageNameSet
27from lp.soyuz.interfaces.binarypackagerelease import BinaryPackageFormat29from lp.soyuz.interfaces.binarypackagerelease import BinaryPackageFormat
30from lp.soyuz.interfaces.component import IComponentSet
28from lp.soyuz.interfaces.processor import IProcessorFamilySet31from lp.soyuz.interfaces.processor import IProcessorFamilySet
29from lp.soyuz.interfaces.publishing import PackagePublishingStatus32from lp.soyuz.interfaces.publishing import PackagePublishingStatus
30from lp.soyuz.model.binarypackagebuild import BinaryPackageBuild33from lp.soyuz.model.binarypackagebuild import BinaryPackageBuild
@@ -345,6 +348,7 @@
345 self.failUnlessEqual(348 self.failUnlessEqual(
346 self.sourcepackagereleases[0], result[0])349 self.sourcepackagereleases[0], result[0])
347350
351
348class TestCorrespondingDebugArchive(TestCaseWithFactory):352class TestCorrespondingDebugArchive(TestCaseWithFactory):
349353
350 layer = LaunchpadZopelessLayer354 layer = LaunchpadZopelessLayer
@@ -520,6 +524,7 @@
520 self.archive.disable()524 self.archive.disable()
521 self.assertRaises(AssertionError, self.archive.disable)525 self.assertRaises(AssertionError, self.archive.disable)
522526
527
523class TestCollectLatestPublishedSources(TestCaseWithFactory):528class TestCollectLatestPublishedSources(TestCaseWithFactory):
524 """Ensure that the private helper method works as expected."""529 """Ensure that the private helper method works as expected."""
525530
@@ -566,6 +571,70 @@
566 self.assertEqual('0.5.11~ppa1', pubs[0].source_package_version)571 self.assertEqual('0.5.11~ppa1', pubs[0].source_package_version)
567572
568573
574class TestArchiveCanUpload(TestCaseWithFactory):
575 """Test the various methods that verify whether uploads are allowed to
576 happen."""
577
578 layer = LaunchpadZopelessLayer
579
580 def test_checkArchivePermission_by_PPA_owner(self):
581 # Uploading to a PPA should be allowed for a user that is the owner
582 owner = self.factory.makePerson(name="somebody")
583 archive = self.factory.makeArchive(owner=owner)
584 self.assertEquals(True, archive.checkArchivePermission(owner))
585 someone_unrelated = self.factory.makePerson(name="somebody-unrelated")
586 self.assertEquals(False,
587 archive.checkArchivePermission(someone_unrelated))
588
589 def test_checkArchivePermission_distro_archive(self):
590 # Regular users can not upload to ubuntu
591 ubuntu = getUtility(IDistributionSet).getByName('ubuntu')
592 archive = self.factory.makeArchive(purpose=ArchivePurpose.PRIMARY,
593 distribution=ubuntu)
594 main = getUtility(IComponentSet)["main"]
595 # A regular user doesn't have access
596 somebody = self.factory.makePerson(name="somebody")
597 self.assertEquals(False,
598 archive.checkArchivePermission(somebody, main))
599 # An ubuntu core developer does have access
600 kamion = getUtility(IPersonSet).getByName('kamion')
601 self.assertEquals(True, archive.checkArchivePermission(kamion, main))
602
603 def test_checkArchivePermission_ppa(self):
604 ubuntu = getUtility(IDistributionSet).getByName('ubuntu')
605 owner = self.factory.makePerson(name="eigenaar")
606 archive = self.factory.makeArchive(purpose=ArchivePurpose.PPA,
607 distribution=ubuntu,
608 owner=owner)
609 somebody = self.factory.makePerson(name="somebody")
610 # The owner has access
611 self.assertEquals(True, archive.checkArchivePermission(owner))
612 # Somebody unrelated does not
613 self.assertEquals(False, archive.checkArchivePermission(somebody))
614
615 def test_checkUpload_partner_invalid_pocket(self):
616 # Partner archives only have release and proposed pockets
617 archive = self.factory.makeArchive(purpose=ArchivePurpose.PARTNER)
618 self.assertIsInstance(archive.checkUpload(self.factory.makePerson(),
619 self.factory.makeDistroSeries(),
620 self.factory.makeSourcePackageName(),
621 self.factory.makeComponent(),
622 PackagePublishingPocket.UPDATES),
623 InvalidPocketForPartnerArchive)
624
625 def test_checkUpload_ppa_invalid_pocket(self):
626 # PPA archives only have release pockets
627 archive = self.factory.makeArchive(purpose=ArchivePurpose.PPA)
628 self.assertIsInstance(archive.checkUpload(self.factory.makePerson(),
629 self.factory.makeDistroSeries(),
630 self.factory.makeSourcePackageName(),
631 self.factory.makeComponent(),
632 PackagePublishingPocket.PROPOSED),
633 InvalidPocketForPPA)
634
635 # XXX: JRV 20100511: IArchive.canUploadSuiteSourcePackage needs tests
636
637
569class TestUpdatePackageDownloadCount(TestCaseWithFactory):638class TestUpdatePackageDownloadCount(TestCaseWithFactory):
570 """Ensure that updatePackageDownloadCount works as expected."""639 """Ensure that updatePackageDownloadCount works as expected."""
571640