Merge ~cjwatson/launchpad:archive-translate-path-private-urls into launchpad:master

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: 006491f5932a6cf82ecc2c8251105427d71b596a
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~cjwatson/launchpad:archive-translate-path-private-urls
Merge into: launchpad:master
Diff against target: 183 lines (+118/-4)
2 files modified
lib/lp/soyuz/xmlrpc/archive.py (+3/-3)
lib/lp/soyuz/xmlrpc/tests/test_archive.py (+115/-1)
Reviewer Review Type Date Requested Status
Andrey Fedoseev (community) Approve
Review via email: mp+431535@code.launchpad.net

Commit message

Include tokens in URLs returned by ArchiveAPI.translatePath

Description of the change

Files in the restricted librarian can only be accessed with some kind of authentication token, either a `TimeLimitedToken` or a macaroon; without adding something like that to the URLs returned by `ArchiveAPI.translatePath`, there's no way to use it to access files in private PPAs. Macaroons don't offer any advantages in this case since we can't constrain them in any interesting way, but a simple `TimeLimitedToken` is fine.

To post a comment you must log in.
Revision history for this message
Andrey Fedoseev (andrey-fedoseev) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/lib/lp/soyuz/xmlrpc/archive.py b/lib/lp/soyuz/xmlrpc/archive.py
index a23b7fa..c0e7b25 100644
--- a/lib/lp/soyuz/xmlrpc/archive.py
+++ b/lib/lp/soyuz/xmlrpc/archive.py
@@ -144,7 +144,7 @@ class ArchiveAPI(LaunchpadXMLRPCView):
144 path.as_posix(),144 path.as_posix(),
145 archive_file.library_file.id,145 archive_file.library_file.id,
146 )146 )
147 return archive_file.library_file.getURL()147 return archive_file.library_file.getURL(include_token=True)
148148
149 def _translatePathNonPool(149 def _translatePathNonPool(
150 self, archive_reference: str, archive, path: PurePath150 self, archive_reference: str, archive, path: PurePath
@@ -163,7 +163,7 @@ class ArchiveAPI(LaunchpadXMLRPCView):
163 path.as_posix(),163 path.as_posix(),
164 archive_file.library_file.id,164 archive_file.library_file.id,
165 )165 )
166 return archive_file.library_file.getURL()166 return archive_file.library_file.getURL(include_token=True)
167167
168 def _translatePathPool(168 def _translatePathPool(
169 self, archive_reference: str, archive, path: PurePath169 self, archive_reference: str, archive, path: PurePath
@@ -178,7 +178,7 @@ class ArchiveAPI(LaunchpadXMLRPCView):
178 path.as_posix(),178 path.as_posix(),
179 lfa.id,179 lfa.id,
180 )180 )
181 return lfa.getURL()181 return lfa.getURL(include_token=True)
182182
183 @return_fault183 @return_fault
184 def _translatePath(self, archive_reference: str, path: PurePath) -> str:184 def _translatePath(self, archive_reference: str, path: PurePath) -> str:
diff --git a/lib/lp/soyuz/xmlrpc/tests/test_archive.py b/lib/lp/soyuz/xmlrpc/tests/test_archive.py
index cdb7d7d..c8ba612 100644
--- a/lib/lp/soyuz/xmlrpc/tests/test_archive.py
+++ b/lib/lp/soyuz/xmlrpc/tests/test_archive.py
@@ -14,7 +14,7 @@ from lp.services.macaroons.interfaces import IMacaroonIssuer
14from lp.soyuz.enums import ArchiveRepositoryFormat, PackagePublishingStatus14from lp.soyuz.enums import ArchiveRepositoryFormat, PackagePublishingStatus
15from lp.soyuz.interfaces.archive import NAMED_AUTH_TOKEN_FEATURE_FLAG15from lp.soyuz.interfaces.archive import NAMED_AUTH_TOKEN_FEATURE_FLAG
16from lp.soyuz.xmlrpc.archive import ArchiveAPI16from lp.soyuz.xmlrpc.archive import ArchiveAPI
17from lp.testing import TestCaseWithFactory17from lp.testing import TestCaseWithFactory, person_logged_in
18from lp.testing.layers import LaunchpadFunctionalLayer18from lp.testing.layers import LaunchpadFunctionalLayer
19from lp.xmlrpc import faults19from lp.xmlrpc import faults
2020
@@ -324,6 +324,31 @@ class TestArchiveAPI(TestCaseWithFactory):
324 % (archive.reference, path, archive_file.library_file.id)324 % (archive.reference, path, archive_file.library_file.id)
325 )325 )
326326
327 def test_translatePath_by_hash_checksum_found_private(self):
328 archive = removeSecurityProxy(self.factory.makeArchive(private=True))
329 self.factory.makeArchiveFile(
330 archive=archive,
331 container="release:jammy",
332 path="dists/jammy/InRelease",
333 )
334 archive_file = self.factory.makeArchiveFile(
335 archive=archive,
336 container="release:jammy",
337 path="dists/jammy/InRelease",
338 )
339 path = (
340 "dists/jammy/by-hash/SHA256/%s"
341 % archive_file.library_file.content.sha256
342 )
343 self.assertStartsWith(
344 archive_file.library_file.getURL() + "?token=",
345 self.archive_api.translatePath(archive.reference, path),
346 )
347 self.assertLogs(
348 "%s: %s (by-hash) -> LFA %d"
349 % (archive.reference, path, archive_file.library_file.id)
350 )
351
327 def test_translatePath_non_pool_not_found(self):352 def test_translatePath_non_pool_not_found(self):
328 archive = removeSecurityProxy(self.factory.makeArchive())353 archive = removeSecurityProxy(self.factory.makeArchive())
329 self.factory.makeArchiveFile(archive=archive)354 self.factory.makeArchiveFile(archive=archive)
@@ -354,6 +379,25 @@ class TestArchiveAPI(TestCaseWithFactory):
354 )379 )
355 )380 )
356381
382 def test_translatePath_non_pool_found_private(self):
383 archive = removeSecurityProxy(self.factory.makeArchive(private=True))
384 self.factory.makeArchiveFile(archive=archive)
385 archive_file = self.factory.makeArchiveFile(archive=archive)
386 self.assertStartsWith(
387 archive_file.library_file.getURL() + "?token=",
388 self.archive_api.translatePath(
389 archive.reference, archive_file.path
390 ),
391 )
392 self.assertLogs(
393 "%s: %s (non-pool) -> LFA %d"
394 % (
395 archive.reference,
396 archive_file.path,
397 archive_file.library_file.id,
398 )
399 )
400
357 def test_translatePath_pool_bad_file_name(self):401 def test_translatePath_pool_bad_file_name(self):
358 archive = removeSecurityProxy(self.factory.makeArchive())402 archive = removeSecurityProxy(self.factory.makeArchive())
359 path = "pool/nonexistent"403 path = "pool/nonexistent"
@@ -413,6 +457,38 @@ class TestArchiveAPI(TestCaseWithFactory):
413 % (archive.reference, path, sprf.libraryfile.id)457 % (archive.reference, path, sprf.libraryfile.id)
414 )458 )
415459
460 def test_translatePath_pool_source_found_private(self):
461 archive = removeSecurityProxy(self.factory.makeArchive(private=True))
462 with person_logged_in(archive.owner):
463 spph = self.factory.makeSourcePackagePublishingHistory(
464 archive=archive,
465 status=PackagePublishingStatus.PUBLISHED,
466 sourcepackagename="test-package",
467 component="main",
468 )
469 sprf = self.factory.makeSourcePackageReleaseFile(
470 sourcepackagerelease=spph.sourcepackagerelease,
471 library_file=self.factory.makeLibraryFileAlias(
472 filename="test-package_1.dsc", db_only=True
473 ),
474 )
475 self.factory.makeSourcePackageReleaseFile(
476 sourcepackagerelease=spph.sourcepackagerelease,
477 library_file=self.factory.makeLibraryFileAlias(
478 filename="test-package_1.tar.xz", db_only=True
479 ),
480 )
481 IStore(sprf).flush()
482 path = "pool/main/t/test-package/test-package_1.dsc"
483 self.assertStartsWith(
484 sprf.libraryfile.getURL() + "?token=",
485 self.archive_api.translatePath(archive.reference, path),
486 )
487 self.assertLogs(
488 "%s: %s (pool) -> LFA %d"
489 % (archive.reference, path, sprf.libraryfile.id)
490 )
491
416 def test_translatePath_pool_binary_not_found(self):492 def test_translatePath_pool_binary_not_found(self):
417 archive = removeSecurityProxy(self.factory.makeArchive())493 archive = removeSecurityProxy(self.factory.makeArchive())
418 self.factory.makeBinaryPackagePublishingHistory(494 self.factory.makeBinaryPackagePublishingHistory(
@@ -466,3 +542,41 @@ class TestArchiveAPI(TestCaseWithFactory):
466 "%s: %s (pool) -> LFA %d"542 "%s: %s (pool) -> LFA %d"
467 % (archive.reference, path, bpf.libraryfile.id)543 % (archive.reference, path, bpf.libraryfile.id)
468 )544 )
545
546 def test_translatePath_pool_binary_found_private(self):
547 archive = removeSecurityProxy(self.factory.makeArchive(private=True))
548 with person_logged_in(archive.owner):
549 bpph = self.factory.makeBinaryPackagePublishingHistory(
550 archive=archive,
551 status=PackagePublishingStatus.PUBLISHED,
552 sourcepackagename="test-package",
553 component="main",
554 )
555 bpf = self.factory.makeBinaryPackageFile(
556 binarypackagerelease=bpph.binarypackagerelease,
557 library_file=self.factory.makeLibraryFileAlias(
558 filename="test-package_1_amd64.deb", db_only=True
559 ),
560 )
561 bpph2 = self.factory.makeBinaryPackagePublishingHistory(
562 archive=archive,
563 status=PackagePublishingStatus.PUBLISHED,
564 sourcepackagename="test-package",
565 component="main",
566 )
567 self.factory.makeBinaryPackageFile(
568 binarypackagerelease=bpph2.binarypackagerelease,
569 library_file=self.factory.makeLibraryFileAlias(
570 filename="test-package_1_i386.deb", db_only=True
571 ),
572 )
573 IStore(bpf).flush()
574 path = "pool/main/t/test-package/test-package_1_amd64.deb"
575 self.assertStartsWith(
576 bpf.libraryfile.getURL() + "?token=",
577 self.archive_api.translatePath(archive.reference, path),
578 )
579 self.assertLogs(
580 "%s: %s (pool) -> LFA %d"
581 % (archive.reference, path, bpf.libraryfile.id)
582 )

Subscribers

People subscribed via source and target branches

to status/vote changes: