Merge ~cjwatson/launchpad:export-sshkey-getFullKeyText into launchpad:master

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: 1a520e5a6e8c9add2bbcb8bb534e61e6f1913cb8
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~cjwatson/launchpad:export-sshkey-getFullKeyText
Merge into: launchpad:master
Diff against target: 88 lines (+39/-1)
3 files modified
lib/lp/registry/browser/person.py (+8/-0)
lib/lp/registry/interfaces/ssh.py (+4/-0)
lib/lp/registry/tests/test_ssh.py (+27/-1)
Reviewer Review Type Date Requested Status
Guruprasad Approve
Review via email: mp+446623@code.launchpad.net

Commit message

Export ISSHKey.getFullKeyText

Description of the change

There are currently several cron jobs on loganberry that export email and SSH key data for the members of several teams, using `scripts/list-team-members`; this information is used as part of various integrations such as lists.ubuntu.com posting privileges, the ability for Ubuntu members to upload files to people.ubuntu.com using SFTP, and so on. However, doing this with ad-hoc cron jobs on a Launchpad machine isn't ideal; it would be better if it were possible to get this information over the API, and then IS would only need a suitably-privileged bot account.

As far as I can see, the only API gap here is that we can only extract the raw key text for an SSH key, and not the "full" key text that's decorated with the key type and comment in a format suitable for adding to an OpenSSH `authorized_keys` file. That transformation isn't quite trivial, so export a method that does it on the webservice.

To post a comment you must log in.
Revision history for this message
Guruprasad (lgp171188) wrote :

LGTM 👍

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/lib/lp/registry/browser/person.py b/lib/lp/registry/browser/person.py
2index 3c67245..3a61179 100644
3--- a/lib/lp/registry/browser/person.py
4+++ b/lib/lp/registry/browser/person.py
5@@ -627,6 +627,14 @@ class PersonNavigation(BranchTraversalMixin, Navigation):
6 raise NotFoundError(name)
7 return snap
8
9+ @stepthrough("+ssh-keys")
10+ def traverse_ssh_keys(self, id):
11+ """Traverse to this person's SSH keys."""
12+ ssh_key = getUtility(ISSHKeySet).getByID(id)
13+ if ssh_key is None or ssh_key.person != self.context:
14+ return None
15+ return ssh_key
16+
17
18 class PersonSetNavigation(Navigation):
19 usedfor = IPersonSet
20diff --git a/lib/lp/registry/interfaces/ssh.py b/lib/lp/registry/interfaces/ssh.py
21index 5590a99..5559526 100644
22--- a/lib/lp/registry/interfaces/ssh.py
23+++ b/lib/lp/registry/interfaces/ssh.py
24@@ -16,8 +16,10 @@ import http.client
25 from lazr.enum import DBEnumeratedType, DBItem
26 from lazr.restful.declarations import (
27 error_status,
28+ export_read_operation,
29 exported,
30 exported_as_webservice_entry,
31+ operation_for_version,
32 )
33 from zope.interface import Interface
34 from zope.schema import Choice, Int, TextLine
35@@ -107,6 +109,8 @@ class ISSHKey(Interface):
36 def destroySelf():
37 """Remove this SSHKey from the database."""
38
39+ @export_read_operation()
40+ @operation_for_version("devel")
41 def getFullKeyText():
42 """Get the full text of the SSH key."""
43
44diff --git a/lib/lp/registry/tests/test_ssh.py b/lib/lp/registry/tests/test_ssh.py
45index 3353e66..a3448bf 100644
46--- a/lib/lp/registry/tests/test_ssh.py
47+++ b/lib/lp/registry/tests/test_ssh.py
48@@ -12,9 +12,16 @@ from lp.registry.interfaces.ssh import (
49 ISSHKeySet,
50 SSHKeyAdditionError,
51 )
52-from lp.testing import TestCaseWithFactory, admin_logged_in, person_logged_in
53+from lp.services.webapp.interfaces import OAuthPermission
54+from lp.testing import (
55+ TestCaseWithFactory,
56+ admin_logged_in,
57+ api_url,
58+ person_logged_in,
59+)
60 from lp.testing.layers import DatabaseFunctionalLayer
61 from lp.testing.mail_helpers import pop_notifications
62+from lp.testing.pages import webservice_for_person
63
64
65 class TestSSHKey(TestCaseWithFactory):
66@@ -298,3 +305,22 @@ class TestSSHKeySet(TestCaseWithFactory):
67 self.assertContentEqual(
68 [person1_key1, person1_key2, person2_key1], keys
69 )
70+
71+
72+class TestSSHKeyWebservice(TestCaseWithFactory):
73+ layer = DatabaseFunctionalLayer
74+
75+ def test_getFullKeyText(self):
76+ person = self.factory.makePerson()
77+ with person_logged_in(person):
78+ key = self.factory.makeSSHKey(person, "ssh-rsa")
79+ key_url = api_url(key)
80+ expected = "ssh-rsa %s %s" % (key.keytext, key.comment)
81+ webservice = webservice_for_person(
82+ person,
83+ permission=OAuthPermission.READ_PUBLIC,
84+ default_api_version="devel",
85+ )
86+ response = webservice.named_get(key_url, "getFullKeyText")
87+ self.assertEqual(200, response.status)
88+ self.assertEqual(expected, response.jsonBody())

Subscribers

People subscribed via source and target branches

to status/vote changes: