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
1diff --git a/lib/lp/soyuz/xmlrpc/archive.py b/lib/lp/soyuz/xmlrpc/archive.py
2index a23b7fa..c0e7b25 100644
3--- a/lib/lp/soyuz/xmlrpc/archive.py
4+++ b/lib/lp/soyuz/xmlrpc/archive.py
5@@ -144,7 +144,7 @@ class ArchiveAPI(LaunchpadXMLRPCView):
6 path.as_posix(),
7 archive_file.library_file.id,
8 )
9- return archive_file.library_file.getURL()
10+ return archive_file.library_file.getURL(include_token=True)
11
12 def _translatePathNonPool(
13 self, archive_reference: str, archive, path: PurePath
14@@ -163,7 +163,7 @@ class ArchiveAPI(LaunchpadXMLRPCView):
15 path.as_posix(),
16 archive_file.library_file.id,
17 )
18- return archive_file.library_file.getURL()
19+ return archive_file.library_file.getURL(include_token=True)
20
21 def _translatePathPool(
22 self, archive_reference: str, archive, path: PurePath
23@@ -178,7 +178,7 @@ class ArchiveAPI(LaunchpadXMLRPCView):
24 path.as_posix(),
25 lfa.id,
26 )
27- return lfa.getURL()
28+ return lfa.getURL(include_token=True)
29
30 @return_fault
31 def _translatePath(self, archive_reference: str, path: PurePath) -> str:
32diff --git a/lib/lp/soyuz/xmlrpc/tests/test_archive.py b/lib/lp/soyuz/xmlrpc/tests/test_archive.py
33index cdb7d7d..c8ba612 100644
34--- a/lib/lp/soyuz/xmlrpc/tests/test_archive.py
35+++ b/lib/lp/soyuz/xmlrpc/tests/test_archive.py
36@@ -14,7 +14,7 @@ from lp.services.macaroons.interfaces import IMacaroonIssuer
37 from lp.soyuz.enums import ArchiveRepositoryFormat, PackagePublishingStatus
38 from lp.soyuz.interfaces.archive import NAMED_AUTH_TOKEN_FEATURE_FLAG
39 from lp.soyuz.xmlrpc.archive import ArchiveAPI
40-from lp.testing import TestCaseWithFactory
41+from lp.testing import TestCaseWithFactory, person_logged_in
42 from lp.testing.layers import LaunchpadFunctionalLayer
43 from lp.xmlrpc import faults
44
45@@ -324,6 +324,31 @@ class TestArchiveAPI(TestCaseWithFactory):
46 % (archive.reference, path, archive_file.library_file.id)
47 )
48
49+ def test_translatePath_by_hash_checksum_found_private(self):
50+ archive = removeSecurityProxy(self.factory.makeArchive(private=True))
51+ self.factory.makeArchiveFile(
52+ archive=archive,
53+ container="release:jammy",
54+ path="dists/jammy/InRelease",
55+ )
56+ archive_file = self.factory.makeArchiveFile(
57+ archive=archive,
58+ container="release:jammy",
59+ path="dists/jammy/InRelease",
60+ )
61+ path = (
62+ "dists/jammy/by-hash/SHA256/%s"
63+ % archive_file.library_file.content.sha256
64+ )
65+ self.assertStartsWith(
66+ archive_file.library_file.getURL() + "?token=",
67+ self.archive_api.translatePath(archive.reference, path),
68+ )
69+ self.assertLogs(
70+ "%s: %s (by-hash) -> LFA %d"
71+ % (archive.reference, path, archive_file.library_file.id)
72+ )
73+
74 def test_translatePath_non_pool_not_found(self):
75 archive = removeSecurityProxy(self.factory.makeArchive())
76 self.factory.makeArchiveFile(archive=archive)
77@@ -354,6 +379,25 @@ class TestArchiveAPI(TestCaseWithFactory):
78 )
79 )
80
81+ def test_translatePath_non_pool_found_private(self):
82+ archive = removeSecurityProxy(self.factory.makeArchive(private=True))
83+ self.factory.makeArchiveFile(archive=archive)
84+ archive_file = self.factory.makeArchiveFile(archive=archive)
85+ self.assertStartsWith(
86+ archive_file.library_file.getURL() + "?token=",
87+ self.archive_api.translatePath(
88+ archive.reference, archive_file.path
89+ ),
90+ )
91+ self.assertLogs(
92+ "%s: %s (non-pool) -> LFA %d"
93+ % (
94+ archive.reference,
95+ archive_file.path,
96+ archive_file.library_file.id,
97+ )
98+ )
99+
100 def test_translatePath_pool_bad_file_name(self):
101 archive = removeSecurityProxy(self.factory.makeArchive())
102 path = "pool/nonexistent"
103@@ -413,6 +457,38 @@ class TestArchiveAPI(TestCaseWithFactory):
104 % (archive.reference, path, sprf.libraryfile.id)
105 )
106
107+ def test_translatePath_pool_source_found_private(self):
108+ archive = removeSecurityProxy(self.factory.makeArchive(private=True))
109+ with person_logged_in(archive.owner):
110+ spph = self.factory.makeSourcePackagePublishingHistory(
111+ archive=archive,
112+ status=PackagePublishingStatus.PUBLISHED,
113+ sourcepackagename="test-package",
114+ component="main",
115+ )
116+ sprf = self.factory.makeSourcePackageReleaseFile(
117+ sourcepackagerelease=spph.sourcepackagerelease,
118+ library_file=self.factory.makeLibraryFileAlias(
119+ filename="test-package_1.dsc", db_only=True
120+ ),
121+ )
122+ self.factory.makeSourcePackageReleaseFile(
123+ sourcepackagerelease=spph.sourcepackagerelease,
124+ library_file=self.factory.makeLibraryFileAlias(
125+ filename="test-package_1.tar.xz", db_only=True
126+ ),
127+ )
128+ IStore(sprf).flush()
129+ path = "pool/main/t/test-package/test-package_1.dsc"
130+ self.assertStartsWith(
131+ sprf.libraryfile.getURL() + "?token=",
132+ self.archive_api.translatePath(archive.reference, path),
133+ )
134+ self.assertLogs(
135+ "%s: %s (pool) -> LFA %d"
136+ % (archive.reference, path, sprf.libraryfile.id)
137+ )
138+
139 def test_translatePath_pool_binary_not_found(self):
140 archive = removeSecurityProxy(self.factory.makeArchive())
141 self.factory.makeBinaryPackagePublishingHistory(
142@@ -466,3 +542,41 @@ class TestArchiveAPI(TestCaseWithFactory):
143 "%s: %s (pool) -> LFA %d"
144 % (archive.reference, path, bpf.libraryfile.id)
145 )
146+
147+ def test_translatePath_pool_binary_found_private(self):
148+ archive = removeSecurityProxy(self.factory.makeArchive(private=True))
149+ with person_logged_in(archive.owner):
150+ bpph = self.factory.makeBinaryPackagePublishingHistory(
151+ archive=archive,
152+ status=PackagePublishingStatus.PUBLISHED,
153+ sourcepackagename="test-package",
154+ component="main",
155+ )
156+ bpf = self.factory.makeBinaryPackageFile(
157+ binarypackagerelease=bpph.binarypackagerelease,
158+ library_file=self.factory.makeLibraryFileAlias(
159+ filename="test-package_1_amd64.deb", db_only=True
160+ ),
161+ )
162+ bpph2 = self.factory.makeBinaryPackagePublishingHistory(
163+ archive=archive,
164+ status=PackagePublishingStatus.PUBLISHED,
165+ sourcepackagename="test-package",
166+ component="main",
167+ )
168+ self.factory.makeBinaryPackageFile(
169+ binarypackagerelease=bpph2.binarypackagerelease,
170+ library_file=self.factory.makeLibraryFileAlias(
171+ filename="test-package_1_i386.deb", db_only=True
172+ ),
173+ )
174+ IStore(bpf).flush()
175+ path = "pool/main/t/test-package/test-package_1_amd64.deb"
176+ self.assertStartsWith(
177+ bpf.libraryfile.getURL() + "?token=",
178+ self.archive_api.translatePath(archive.reference, path),
179+ )
180+ self.assertLogs(
181+ "%s: %s (pool) -> LFA %d"
182+ % (archive.reference, path, bpf.libraryfile.id)
183+ )

Subscribers

People subscribed via source and target branches

to status/vote changes: