launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #24751
[Merge] ~cjwatson/launchpad:archive-subscriber-signing-key-fingerprint into launchpad:master
Colin Watson has proposed merging ~cjwatson/launchpad:archive-subscriber-signing-key-fingerprint into launchpad:master.
Commit message:
Allow subscribers to see Archive.signing_key_fingerprint
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #1879781 in Launchpad itself: "private ppa signing_key_fingerprint shows 'tag:launchpad.net:2008:redacted'"
https://bugs.launchpad.net/launchpad/+bug/1879781
For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/384345
They can already use Archive.getSigningKeyData, and by definition they're allowed to use the archive via apt, so it doesn't make sense to stop them seeing the fingerprint.
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:archive-subscriber-signing-key-fingerprint into launchpad:master.
diff --git a/lib/lp/soyuz/browser/tests/test_archive_webservice.py b/lib/lp/soyuz/browser/tests/test_archive_webservice.py
index 66be27d..b9ad921 100644
--- a/lib/lp/soyuz/browser/tests/test_archive_webservice.py
+++ b/lib/lp/soyuz/browser/tests/test_archive_webservice.py
@@ -1,4 +1,4 @@
-# Copyright 2010-2019 Canonical Ltd. This software is licensed under the
+# Copyright 2010-2020 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
from __future__ import absolute_import, print_function, unicode_literals
@@ -7,7 +7,9 @@ __metaclass__ = type
from datetime import timedelta
import json
+import os.path
+import responses
from testtools.matchers import (
Contains,
ContainsDict,
@@ -18,9 +20,11 @@ from testtools.matchers import (
MatchesStructure,
)
from zope.component import getUtility
+from zope.security.proxy import removeSecurityProxy
from lp.app.interfaces.launchpad import ILaunchpadCelebrities
from lp.registry.interfaces.pocket import PackagePublishingPocket
+from lp.services.gpg.interfaces import IGPGHandler
from lp.services.webapp.interfaces import OAuthPermission
from lp.soyuz.enums import (
ArchivePermissionType,
@@ -39,6 +43,7 @@ from lp.testing import (
record_two_runs,
TestCaseWithFactory,
)
+from lp.testing.gpgkeys import gpgkeysdir
from lp.testing.layers import DatabaseFunctionalLayer
from lp.testing.matchers import HasQueryCount
from lp.testing.pages import (
@@ -134,6 +139,82 @@ class TestArchiveWebservice(TestCaseWithFactory):
self.assertEqual(401, ws.delete(ppa_url).status)
+class TestSigningKey(TestCaseWithFactory):
+ """Test signing-key-related information for archives.
+
+ We just use `responses` to mock the keyserver here; the details of its
+ implementation aren't especially important, we can't use
+ `InProcessKeyServerFixture` because the keyserver operations are
+ synchronous, and `responses` is much faster than `KeyServerTac`.
+ """
+
+ layer = DatabaseFunctionalLayer
+
+ def _setUpSigningKey(self, archive):
+ key_path = os.path.join(gpgkeysdir, "ppa-sample@xxxxxxxxxxxxxxxxx")
+ gpghandler = getUtility(IGPGHandler)
+ with open(key_path, "rb") as key_file:
+ secret_key = gpghandler.importSecretKey(key_file.read())
+ public_key = gpghandler.retrieveKey(secret_key.fingerprint)
+ public_key_data = public_key.export()
+ removeSecurityProxy(archive).signing_key_fingerprint = (
+ public_key.fingerprint)
+ key_url = gpghandler.getURLForKeyInServer(
+ public_key.fingerprint, action="get")
+ responses.add("GET", key_url, body=public_key_data)
+ gpghandler.resetLocalState()
+ return public_key.fingerprint, public_key_data
+
+ @responses.activate
+ def test_signing_key_public(self):
+ # Anyone can read signing key information for public archives.
+ archive = self.factory.makeArchive()
+ fingerprint, public_key_data = self._setUpSigningKey(archive)
+ archive_url = api_url(archive)
+ ws = webservice_for_person(
+ self.factory.makePerson(), default_api_version="devel")
+ ws_archive = self.getWebserviceJSON(ws, archive_url)
+ self.assertEqual(fingerprint, ws_archive["signing_key_fingerprint"])
+ response = ws.named_get(archive_url, "getSigningKeyData")
+ self.assertEqual(200, response.status)
+ self.assertEqual(public_key_data, response.jsonBody())
+
+ @responses.activate
+ def test_signing_key_private_subscriber(self):
+ # Subscribers can read signing key information for private archives.
+ archive = self.factory.makeArchive(private=True)
+ fingerprint, public_key_data = self._setUpSigningKey(archive)
+ subscriber = self.factory.makePerson()
+ with person_logged_in(archive.owner):
+ archive.newSubscription(subscriber, archive.owner)
+ archive_url = api_url(archive)
+ ws = webservice_for_person(
+ subscriber, permission=OAuthPermission.READ_PRIVATE,
+ default_api_version="devel")
+ ws_archive = self.getWebserviceJSON(ws, archive_url)
+ self.assertEqual(fingerprint, ws_archive["signing_key_fingerprint"])
+ response = ws.named_get(archive_url, "getSigningKeyData")
+ self.assertEqual(200, response.status)
+ self.assertEqual(public_key_data, response.jsonBody())
+
+ @responses.activate
+ def test_signing_key_private_non_subscriber(self):
+ # Non-subscribers cannot read signing key information (or indeed
+ # anything else) for private archives.
+ archive = self.factory.makeArchive(private=True)
+ fingerprint, public_key_data = self._setUpSigningKey(archive)
+ archive_url = api_url(archive)
+ ws = webservice_for_person(
+ self.factory.makePerson(), permission=OAuthPermission.READ_PRIVATE,
+ default_api_version="devel")
+ ws_archive = self.getWebserviceJSON(ws, archive_url)
+ self.assertEqual(
+ "tag:launchpad.net:2008:redacted",
+ ws_archive["signing_key_fingerprint"])
+ response = ws.named_get(archive_url, "getSigningKeyData")
+ self.assertEqual(401, response.status)
+
+
class TestExternalDependencies(TestCaseWithFactory):
layer = DatabaseFunctionalLayer
diff --git a/lib/lp/soyuz/interfaces/archive.py b/lib/lp/soyuz/interfaces/archive.py
index 6347193..8d34ec4 100644
--- a/lib/lp/soyuz/interfaces/archive.py
+++ b/lib/lp/soyuz/interfaces/archive.py
@@ -461,6 +461,12 @@ class IArchiveSubscriberView(Interface):
"explicit publish flag and any other constraints."))
series_with_sources = Attribute(
"DistroSeries to which this archive has published sources")
+ signing_key_fingerprint = exported(
+ Text(
+ title=_("Archive signing key fingerprint"), required=False,
+ description=_("A OpenPGP signing key fingerprint (40 chars) "
+ "for this PPA or None if there is no signing "
+ "key available.")))
signing_key = Object(
title=_('Repository signing key.'), required=False, schema=IGPGKey)
@@ -1183,13 +1189,6 @@ class IArchiveView(IHasBuildRecords):
description=_(
"The password used by the build farm to access the archive."))
- signing_key_fingerprint = exported(
- Text(
- title=_("Archive signing key fingerprint"), required=False,
- description=_("A OpenPGP signing key fingerprint (40 chars) "
- "for this PPA or None if there is no signing "
- "key available.")))
-
@call_with(eager_load=True)
@rename_parameters_as(
name="binary_name", distroarchseries="distro_arch_series")