← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~cjwatson/launchpad:archive-subscriber-view-files into launchpad:master

 

Colin Watson has proposed merging ~cjwatson/launchpad:archive-subscriber-view-files into launchpad:master.

Commit message:
Allow archive subscribers to use +sourcefiles and +files

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/438337

This will be needed by the `debuginfod` service, but it seems reasonable anyway.  It has the effect that subscribers will be able to see historical files in archives as well as current ones; but they can build much of that information up over time anyway, so this was never a robust boundary, and the in-progress archive snapshot service will soon allow equivalent access.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:archive-subscriber-view-files into launchpad:master.
diff --git a/lib/lp/services/librarian/browser.py b/lib/lp/services/librarian/browser.py
index d364b68..334636f 100644
--- a/lib/lp/services/librarian/browser.py
+++ b/lib/lp/services/librarian/browser.py
@@ -78,10 +78,12 @@ class FileNavigationMixin:
     with the same filename (product files or bug attachments).
     """
 
+    file_navigation_permission = "launchpad.View"
+
     @stepthrough("+files")
     def traverse_files(self, filename):
         """Traverse on filename in the archive domain."""
-        if not check_permission("launchpad.View", self.context):
+        if not check_permission(self.file_navigation_permission, self.context):
             raise Unauthorized()
         library_file = self.context.getFileByName(filename)
 
diff --git a/lib/lp/soyuz/browser/archive.py b/lib/lp/soyuz/browser/archive.py
index 63b9171..f785294 100644
--- a/lib/lp/soyuz/browser/archive.py
+++ b/lib/lp/soyuz/browser/archive.py
@@ -222,6 +222,7 @@ class ArchiveNavigation(Navigation, FileNavigationMixin):
     """Navigation methods for IArchive."""
 
     usedfor = IArchive
+    file_navigation_permission = "launchpad.SubscriberView"
 
     @stepthrough("+build")
     def traverse_build(self, name):
@@ -463,7 +464,7 @@ class ArchiveNavigation(Navigation, FileNavigationMixin):
         version = self.request.stepstogo.consume()
         filename = self.request.stepstogo.consume()
 
-        if not check_permission("launchpad.View", self.context):
+        if not check_permission("launchpad.SubscriberView", self.context):
             raise Unauthorized()
 
         library_file = self.context.getSourceFileByName(
diff --git a/lib/lp/soyuz/interfaces/archive.py b/lib/lp/soyuz/interfaces/archive.py
index f1e2c3f..c59e03f 100644
--- a/lib/lp/soyuz/interfaces/archive.py
+++ b/lib/lp/soyuz/interfaces/archive.py
@@ -794,9 +794,48 @@ class IArchiveSubscriberView(Interface):
         :return: A collection containing `BinaryPackagePublishingHistory`.
         """
 
+<<<<<<< lib/lp/soyuz/interfaces/archive.py
     def getPoolFileByPath(
         path: PurePath, live_at: typing.Optional[datetime] = None
     ):
+=======
+    def getFileByName(filename):
+        """Return the corresponding `ILibraryFileAlias` in this context.
+
+        The following file types (and extension) can be looked up in the
+        archive context:
+
+         * Source files: '.orig.tar.gz', 'tar.gz', '.diff.gz' and '.dsc';
+         * Binary files: '.deb' and '.udeb';
+         * Source changesfile: '_source.changes';
+         * Package diffs: '.diff.gz';
+
+        :param filename: exactly filename to be looked up.
+
+        :raises AssertionError if the given filename contains a unsupported
+            filename and/or extension, see the list above.
+        :raises NotFoundError if no file could not be found.
+
+        :return the corresponding `ILibraryFileAlias` is the file was found.
+        """
+
+    def getSourceFileByName(name, version, filename):
+        """Return the `ILibraryFileAlias` for a source name/version/filename.
+
+        This can be used to avoid ambiguities with `getFileByName` in
+        imported archives, where the upstream archive software may not
+        always have had robust historical filename uniqueness checks.
+
+        :param name: The name of the source package.
+        :param version: The version of the source package.
+        :param filename: The exact filename to look up.
+
+        :raises NotFoundError: if no matching file could be found.
+        :return: the corresponding `ILibraryFileAlias`.
+        """
+
+    def getPoolFileByPath(path):
+>>>>>>> lib/lp/soyuz/interfaces/archive.py
         """Return the `ILibraryFileAlias` for a path in this archive's pool.
 
         :param path: A `PurePath` for where a source or binary package file
@@ -1283,41 +1322,6 @@ class IArchiveView(IHasBuildRecords):
         queue admin permissions for this archive.
         """
 
-    def getFileByName(filename):
-        """Return the corresponding `ILibraryFileAlias` in this context.
-
-        The following file types (and extension) can be looked up in the
-        archive context:
-
-         * Source files: '.orig.tar.gz', 'tar.gz', '.diff.gz' and '.dsc';
-         * Binary files: '.deb' and '.udeb';
-         * Source changesfile: '_source.changes';
-         * Package diffs: '.diff.gz';
-
-        :param filename: exactly filename to be looked up.
-
-        :raises AssertionError if the given filename contains a unsupported
-            filename and/or extension, see the list above.
-        :raises NotFoundError if no file could not be found.
-
-        :return the corresponding `ILibraryFileAlias` is the file was found.
-        """
-
-    def getSourceFileByName(name, version, filename):
-        """Return the `ILibraryFileAlias` for a source name/version/filename.
-
-        This can be used to avoid ambiguities with `getFileByName` in
-        imported archives, where the upstream archive software may not
-        always have had robust historical filename uniqueness checks.
-
-        :param name: The name of the source package.
-        :param version: The version of the source package.
-        :param filename: The exact filename to look up.
-
-        :raises NotFoundError: if no matching file could be found.
-        :return: the corresponding `ILibraryFileAlias`.
-        """
-
     def getBinaryPackageRelease(name, version, archtag):
         """Find the specified `IBinaryPackageRelease` in the archive.
 
diff --git a/lib/lp/soyuz/stories/ppa/xx-ppa-files.rst b/lib/lp/soyuz/stories/ppa/xx-ppa-files.rst
index 7e0ce09..8eead97 100644
--- a/lib/lp/soyuz/stories/ppa/xx-ppa-files.rst
+++ b/lib/lp/soyuz/stories/ppa/xx-ppa-files.rst
@@ -27,8 +27,19 @@ Create a private PPA for no-priv.
     ...     owner=no_priv, private=True, name="p3a", distribution=ubuntu
     ... )
 
+Add a subscriber to this private PPA.
+
+    >>> from zope.security.proxy import removeSecurityProxy
+    >>> _ = login_person(no_priv)
+    >>> subscriber = factory.makePerson()
+    >>> subscriber_email = removeSecurityProxy(
+    ...     subscriber
+    ... ).preferredemail.email
+    >>> _ = no_priv_private_ppa.newSubscription(subscriber, no_priv)
+
 Initialize SoyuzTestPublisher.
 
+    >>> login("foo.bar@xxxxxxxxxxxxx")
     >>> test_publisher = SoyuzTestPublisher()
     >>> test_publisher.prepareBreezyAutotest()
     >>> test_publisher.addFakeChroots()
@@ -317,6 +328,25 @@ The 'No Privileges' user, the PPA owner, can download the DSC file.
     Location: https://...restricted.../test-pkg_1.0.dsc?token=...
     ...
 
+A subscriber can download the DSC file.
+
+    >>> print(
+    ...     http(
+    ...         r"""
+    ... GET %s HTTP/1.1
+    ... Authorization: Basic %s:test
+    ... """
+    ...         % (
+    ...             dsc_file_lp_url.replace("http://launchpad.test";, ""),
+    ...             subscriber_email,
+    ...         )
+    ...     )
+    ... )
+    HTTP/1.1 303 See Other
+    ...
+    Location: https://...restricted.../test-pkg_1.0.dsc?token=...
+    ...
+
 Binary files are served via '+files' rather than '+sourcefiles'.
 
     >>> login("foo.bar@xxxxxxxxxxxxx")
@@ -342,6 +372,22 @@ Binary files are served via '+files' rather than '+sourcefiles'.
     ...
     Location: https://...restricted.../test-bin_1.0_all.deb?token=...
     ...
+    >>> print(
+    ...     http(
+    ...         r"""
+    ... GET %s HTTP/1.1
+    ... Authorization: Basic %s:test
+    ... """
+    ...         % (
+    ...             deb_file_lp_url.replace("http://launchpad.test";, ""),
+    ...             subscriber_email,
+    ...         )
+    ...     )
+    ... )
+    HTTP/1.1 303 See Other
+    ...
+    Location: https://...restricted.../test-bin_1.0_all.deb?token=...
+    ...
 
 If the associated PPA and the `LibraryFileAlias` are public, the +files/
 proxy redirects to the public http url. We'll copy the test sources and
@@ -465,7 +511,6 @@ immediately deleted in case of reported ToS violation.
     >>> from lp.services.database.interfaces import IPrimaryStore
     >>> login("foo.bar@xxxxxxxxxxxxx")
     >>> IPrimaryStore(Archive).commit()
-    >>> from zope.security.proxy import removeSecurityProxy
     >>> removeSecurityProxy(dsc_file).content = None
     >>> transaction.commit()