Merge lp:~didrocks/launchpad/expose-sshkeys-bug-357235 into lp:launchpad/db-devel

Proposed by Didier Roche-Tolomelli
Status: Superseded
Proposed branch: lp:~didrocks/launchpad/expose-sshkeys-bug-357235
Merge into: lp:launchpad/db-devel
Diff against target: 178 lines (+96/-7)
5 files modified
lib/lp/registry/browser/configure.zcml (+5/-0)
lib/lp/registry/browser/tests/test_sshkey.py (+31/-0)
lib/lp/registry/interfaces/person.py (+6/-1)
lib/lp/registry/interfaces/ssh.py (+13/-6)
lib/lp/registry/stories/webservice/xx-person.txt (+41/-0)
To merge this branch: bzr merge lp:~didrocks/launchpad/expose-sshkeys-bug-357235
Reviewer Review Type Date Requested Status
Graham Binns (community) Needs Resubmitting
Review via email: mp+20751@code.launchpad.net

This proposal has been superseded by a proposal from 2010-03-09.

To post a comment you must log in.
Revision history for this message
Graham Binns (gmb) wrote :

Hi Didier,

I'm not comfortable reviewing this branch. There's no description of the change in the merge proposal description and I don't know whether you've had a pre-implementation discussion about the branch.

You need to include the following items when you submit a Launchpad branch for review:

 * A description of the change, including a brief list of changes by file
 * The output of `make lint`, run in the root of your branch.
 * Details of the person with whom you had a pre implementation discussion, including (if necessary) details of why you chose the solution you did.

I'm going to reject this branch; please resubmit it with the above items included. If you want to have a pre-implementation discussion this afternoon I'll be happy to make myself available.

review: Needs Resubmitting
Revision history for this message
Didier Roche-Tolomelli (didrocks) wrote :
Download full text (6.3 KiB)

The branch has been made in a pair programming session with jml, that's why I didn't added any more detail about it.
You can see inspiration and pair session programming on jml's gpg branch: http://bazaar.launchpad.net/~jml/launchpad/expose-gpgkeys-bug-389872/

The change is for exposing ssh key to the API needed for Quickly. We don't allow uploading or setting it directly yet (this will be an UDS discussion).

https://bugs.edge.launchpad.net/launchpad-registry/+bug/357235 is for the corresponding bug report. Again, we don't upload the ssh key/setting is right now.

output of make lint, I added also the testsuite call too:
$ make lint
utilities/shhh.py PYTHONPATH= python2.5 bootstrap.py\
                --ez_setup-source=ez_setup.py \
  --download-base=download-cache/dist --eggs=eggs
Enter passphrase for key '/home/ubuntu/.ssh/id_rsa':
= Launchpad lint =

Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.

Linting changed files:
  lib/lp/registry/browser/configure.zcml
  lib/lp/registry/browser/tests/test_sshkey.py
  lib/lp/registry/interfaces/person.py
  lib/lp/registry/interfaces/ssh.py
  lib/lp/registry/stories/webservice/xx-person.txt

== Pyflakes notices ==

lib/lp/registry/interfaces/ssh.py
    19: 'export_read_operation' imported but unused
    19: 'export_as_webservice_collection' imported but unused
    19: 'operation_parameters' imported but unused
    19: 'collection_default_content' imported but unused
    19: 'operation_returns_collection_of' imported but unused

== Pylint notices ==

lib/lp/registry/interfaces/person.py
    520: [C0301] Line too long (80/78)
    53: [F0401] Unable to import 'lazr.enum' (No module named enum)
    54: [F0401] Unable to import 'lazr.lifecycle.snapshot' (No module named lifecycle)
    55: [F0401] Unable to import 'lazr.restful.interface' (No module named restful)
    56: [F0401] Unable to import 'lazr.restful.declarations' (No module named restful)
    63: [F0401] Unable to import 'lazr.restful.fields' (No module named restful)
    410: [E1002, PersonNameField._validate] Use super on an old style class
    1404: [C0322, IPersonEditRestricted.addMember] Operator not preceded by a space
    status=copy_field(ITeamMembership['status']),
    ^
    comment=Text(required=False))
    @export_write_operation()
    def addMember(person, reviewer, status=TeamMembershipStatus.APPROVED,
    comment=None, force_team_add=False,
    may_subscribe_to_list=True):
    1445: [C0322, IPersonEditRestricted.acceptInvitationToBeMemberOf] Operator not preceded by a space
    comment=Text())
    ^
    @export_write_operation()
    def acceptInvitationToBeMemberOf(team, comment):
    1457: [C0322, IPersonEditRestricted.declineInvitationToBeMemberOf] Operator not preceded by a space
    comment=Text())
    ^
    @export_write_operation()
    def declineInvitationToBeMemberOf(team, comment):
    1755: [C0322, IPersonSet.newTeam] Operator not preceded by a space
    defaultmembershipperiod='default_membership_period',
    ^
    defaultrenewalperiod='default_renewal_period')
    @operation_parameters(
    subscriptionpolicy=Choice(
    title=_('Subs...

Read more...

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/registry/browser/configure.zcml'
2--- lib/lp/registry/browser/configure.zcml 2010-02-19 11:55:02 +0000
3+++ lib/lp/registry/browser/configure.zcml 2010-03-05 14:29:32 +0000
4@@ -2159,4 +2159,9 @@
5 classes="
6 PersonProductFacets"
7 module="lp.registry.browser.personproduct"/>
8+ <browser:url
9+ for="lp.registry.interfaces.ssh.ISSHKey"
10+ path_expression="string:+ssh-keys/${id}"
11+ rootsite="api"
12+ attribute_to_parent="person" />
13 </configure>
14
15=== added file 'lib/lp/registry/browser/tests/test_sshkey.py'
16--- lib/lp/registry/browser/tests/test_sshkey.py 1970-01-01 00:00:00 +0000
17+++ lib/lp/registry/browser/tests/test_sshkey.py 2010-03-05 14:29:32 +0000
18@@ -0,0 +1,31 @@
19+# Copyright 2010 Canonical Ltd. This software is licensed under the
20+# GNU Affero General Public License version 3 (see the file LICENSE).
21+
22+"""Tests for GPG key on the web."""
23+
24+__metaclass__ = type
25+
26+import unittest
27+
28+from canonical.launchpad.webapp import canonical_url
29+from canonical.testing.layers import DatabaseFunctionalLayer
30+from lp.testing import TestCaseWithFactory
31+
32+
33+class TestCanonicalUrl(TestCaseWithFactory):
34+
35+ layer = DatabaseFunctionalLayer
36+
37+ def test_canonical_url(self):
38+ # The canonical URL of a GPG key is ssh-keys
39+ person = self.factory.makePerson()
40+ sshkey = self.factory.makeSSHKey(person)
41+ self.assertEqual(
42+ '%s/+ssh-keys/%s' % (
43+ canonical_url(person, rootsite='api'), sshkey.id),
44+ canonical_url(sshkey))
45+
46+
47+def test_suite():
48+ return unittest.TestLoader().loadTestsFromName(__name__)
49+
50
51=== modified file 'lib/lp/registry/interfaces/person.py'
52--- lib/lp/registry/interfaces/person.py 2010-02-27 10:19:18 +0000
53+++ lib/lp/registry/interfaces/person.py 2010-03-05 14:29:32 +0000
54@@ -93,6 +93,7 @@
55 from lp.registry.interfaces.mailinglistsubscription import (
56 MailingListAutoSubscribePolicy)
57 from lp.registry.interfaces.mentoringoffer import IHasMentoringOffers
58+from lp.registry.interfaces.ssh import ISSHKey
59 from lp.registry.interfaces.teammembership import (
60 ITeamMembership, ITeamParticipation, TeamMembershipStatus)
61 from lp.registry.interfaces.wikiname import IWikiName
62@@ -602,7 +603,11 @@
63
64 oauth_request_tokens = Attribute(_("Non-expired request tokens"))
65
66- sshkeys = Attribute(_('List of SSH keys'))
67+ sshkeys = exported(
68+ CollectionField(
69+ title= _('List of SSH keys'),
70+ readonly=False, required=False,
71+ value_type=Reference(schema=ISSHKey)))
72
73 account_status = Choice(
74 title=_("The status of this person's account"), required=False,
75
76=== modified file 'lib/lp/registry/interfaces/ssh.py'
77--- lib/lp/registry/interfaces/ssh.py 2009-06-25 04:06:00 +0000
78+++ lib/lp/registry/interfaces/ssh.py 2010-03-05 14:29:32 +0000
79@@ -16,6 +16,10 @@
80 from zope.schema import Choice, Int, TextLine
81 from zope.interface import Interface
82 from lazr.enum import DBEnumeratedType, DBItem
83+from lazr.restful.declarations import (
84+ collection_default_content, export_as_webservice_collection,
85+ export_as_webservice_entry, export_read_operation, exported,
86+ operation_parameters, operation_returns_collection_of)
87
88 from canonical.launchpad import _
89
90@@ -42,14 +46,17 @@
91
92 class ISSHKey(Interface):
93 """SSH public key"""
94- id = Int(title=_("Database ID"), required=True, readonly=True)
95+
96+ export_as_webservice_entry('ssh_key')
97+
98+ id = exported(Int(title=_("Database ID"), required=True, readonly=True))
99 person = Int(title=_("Owner"), required=True, readonly=True)
100 personID = Int(title=_('Owner ID'), required=True, readonly=True)
101- keytype = Choice(title=_("Key type"), required=True,
102- vocabulary=SSHKeyType)
103- keytext = TextLine(title=_("Key text"), required=True)
104- comment = TextLine(title=_("Comment describing this key"),
105- required=True)
106+ keytype = exported(Choice(title=_("Key type"), required=True,
107+ vocabulary=SSHKeyType))
108+ keytext = exported(TextLine(title=_("Key text"), required=True))
109+ comment = exported(TextLine(title=_("Comment describing this key"),
110+ required=True))
111
112 def destroySelf():
113 """Remove this SSHKey from the database."""
114
115=== modified file 'lib/lp/registry/stories/webservice/xx-person.txt'
116--- lib/lp/registry/stories/webservice/xx-person.txt 2010-02-24 16:53:30 +0000
117+++ lib/lp/registry/stories/webservice/xx-person.txt 2010-03-05 14:29:32 +0000
118@@ -44,6 +44,7 @@
119 proposed_members_collection_link: u'http://.../~salgado/proposed_members'
120 resource_type_link: u'http://.../#person'
121 self_link: u'http://.../~salgado'
122+ sshkeys_collection_link: u'http://.../~salgado/sshkeys'
123 sub_teams_collection_link: u'http://.../~salgado/sub_teams'
124 super_teams_collection_link: u'http://.../~salgado/super_teams'
125 team_owner_link: None
126@@ -92,6 +93,7 @@
127 renewal_policy: u'invite them to apply for renewal'
128 resource_type_link: u'http://.../#team'
129 self_link: u'http://.../~ubuntu-team'
130+ sshkeys_collection_link: u'http://.../~ubuntu-team/sshkeys'
131 sub_teams_collection_link: u'http://.../~ubuntu-team/sub_teams'
132 subscription_policy: u'Moderated Team'
133 super_teams_collection_link: u'http://.../~ubuntu-team/super_teams'
134@@ -147,6 +149,45 @@
135 HTTP/1.1 404 Not Found
136 ...
137
138+== SSH keys ===
139+
140+People have SSH keys which we can manipulate over the API.
141+
142+The sample person "name12" doesn't have any keys to begin with:
143+
144+ >>> sample_person = webservice.get("/~name12").jsonBody()
145+ >>> sshkeys = sample_person['sshkeys_collection_link']
146+ >>> print sshkeys
147+ http://.../~name12/sshkeys
148+ >>> print_self_link_of_entries(webservice.get(sshkeys).jsonBody())
149+ http://.../~name12/+ssh-keys/1
150+
151+Let's give "name12" a key via the back door of our internal Python APIs:
152+
153+ >>> from zope.component import getUtility
154+ >>> from lp.registry.interfaces.person import IPersonSet
155+ >>> login(ANONYMOUS)
156+ >>> ssh_user = getUtility(IPersonSet).getByName('name12')
157+ >>> ssh_key = factory.makeGPGKey(ssh_user)
158+ >>> logout()
159+
160+Now when we get the sshkey collection for 'name12' again, the key should show
161+up:
162+
163+ >>> keys = webservice.get(sshkeys).jsonBody()
164+ >>> print_self_link_of_entries(keys)
165+ http://.../~name12/+ssh-keys/1
166+
167+
168+And then we can actually retrieve the key:
169+
170+ >>> pprint_entry(keys['entries'][0])
171+ comment: u'andrew@trogdor'
172+ id: 1
173+ keytext: u'AAAAB3NzaC1kc3MAAAEBAPfhCA15ZaT08brwVXwpJjcZT6QFIipzF1sGy57HY7QPi/W+uljr1VcCHzWdlSmda7YpTCTx0NFYYQIccQRGX6zYL8v1w9FSRCAnxxUJmqEhsUDFYFdVTa9uLCrs3MSbmh7wwFPdRrGrO6X5x7T4dMZQwykSZrOVdpLcCHRgrMZslLomIAjERn6OAQNiGFz7B2tEi/3Soqd52bGJwOtGymRiAXkPSLbH7KfzSCe34ytdh6BD+4SrgSoa+TL3VDV70QAdlOFXD42ZHl3Sc0Tde4LbZeYq2Uf84DOATLZBbOYpRSqTLkM9XngpnvCRVb6dxEQfgODDw783tEuPpySLj2EAAAAVANpUVgivDjt9gFibN/AXfYy1meeBAAABAB6FtnMywmWZg2lr2I3nDfE5U5QbGUQB/ZEP98ZkSkhOcF29VlnGOxyb2/VZbVTLa/btlPF82L4An/c8VKtKZnel7LnAlMoArdgzQNXGVQQVtnaWwM26ydgDzkSSIes3elNZgsfnPRBvaF0ol9Tqju0rNGKjnr3ZOX/NX+42bxpjRnxYj1h56yP2jKKeGfjorI6JK1YfqBAiTxzaDMzSpknnrbztaKJoh7IFqMMOp9ANSFh7H106pEaCv3ebCTJZprtWqNKjb2zum7OQPRz3upA0qx22ocTokjv4itXJ6yj/BvGu9qdOIQFXuB2rsFtLZtS8ATueOly0GzyeiZBx/AEAAAEBAO8jRYjL7tAYnVlO1p6UzPOicAuGCFWfNbBEDRAXoSgLNdj451jStw+eUc9ZVz7tG/XRVZsiavtFHb2cbrcfX1YOd69xi0m+IY6mo3yKt3irQRokDtt376sHoUdHgj2ozySZJgG8IJndtoS+VQQy6NdClA3fNFb96bF865eNaRYoHJO9ZI84lkWQL++MLzIuyFfCs1hSlapyyuHC8kFmF7AQdrVZvbohSbnWs+w53nIW8nAA7z21wAukvE1Pl6AQyG0e7U1sYS8Pc8dtmzJvdtVZWBl02/gqQJ7f06mFvnsN45rR1Uyxnrwl6rbFwqabZDlyD5Ac6Icbvz9SG1gBOiI='
174+ keytype: u'DSA'
175+ resource_type_link: u'http://.../#ssh_key'
176+ self_link: u'http://.../~name12/+ssh-keys/1'
177
178 === Team memberships ===
179

Subscribers

People subscribed via source and target branches

to status/vote changes: