Merge lp:~michael.nelson/launchpad/db-598464-priv-xmlrpc-access-getOrCreate into lp:launchpad/db-devel

Proposed by Michael Nelson
Status: Merged
Approved by: Michael Nelson
Approved revision: no longer in the source branch.
Merged at revision: 9526
Proposed branch: lp:~michael.nelson/launchpad/db-598464-priv-xmlrpc-access-getOrCreate
Merge into: lp:launchpad/db-devel
Prerequisite: lp:~michael.nelson/launchpad/db-598464-get-or-create-from-identity
Diff against target: 323 lines (+229/-1)
8 files modified
lib/canonical/launchpad/interfaces/launchpad.py (+3/-0)
lib/canonical/launchpad/xmlrpc/application.py (+6/-0)
lib/canonical/launchpad/xmlrpc/configure.zcml (+3/-0)
lib/canonical/launchpad/xmlrpc/faults.py (+11/-0)
lib/lp/registry/configure.zcml (+10/-0)
lib/lp/registry/interfaces/person.py (+24/-1)
lib/lp/registry/tests/test_xmlrpc.py (+125/-0)
lib/lp/registry/xmlrpc/softwarecenteragent.py (+47/-0)
To merge this branch: bzr merge lp:~michael.nelson/launchpad/db-598464-priv-xmlrpc-access-getOrCreate
Reviewer Review Type Date Requested Status
Abel Deuring (community) code Approve
Review via email: mp+29059@code.launchpad.net

Description of the change

Overview
========
This branch is related to bug 598464 and follows on from the MP at:

https://code.edge.launchpad.net/~michael.nelson/launchpad/db-598464-get-or-create-from-identity/+merge/28885

It aims to enable the software-center-agent access to the IPersonSet.getOrCreateByOpenIDIdentifier() method via private XML-RPC. It was decided against using the API to expose something similar for the agent only, due to the fact that exposing this functionality for the agent is not a long-term solution (and therefore something belonging on the API). Long term, it should be possible to create subscriptions to private PPAs without requiring ubuntu users to have Launchpad accounts.

Issue
=====
Initially I'd tried restricting the rpc call to the agent only, but then realised the reason that I couldn't find any examples in the code for other private xmlrpc services was that it's unnecessary (given that the private service is only accessible internally).

To test:
bin/test -vvm test_xmlrpc

To post a comment you must log in.
Revision history for this message
Abel Deuring (adeuring) wrote :

(13:09:31) adeuring: noodles775: again, a nice branch. But I think it is a bit odd to have a class PersonSetAPIView (ie.e.,a quite generic name) where the method getorCreateByOpenIDIdentifier() unconditionally providesthe rationale "software centerpurchase" as a account creation rationale. Shouldn't we either make the class name more specific or provide the rationale as a parameter of the method?
(13:10:47) adeuring: (or use a method name like getOrCreateForSoftwarePurchase())
(13:45:12) noodles775: adeuring: yes - I thought the same when I pushed the last rev. So I'd be keen to change the application name... most of the other private xmlrpc apps have names like 'ICodehostingApplication' identifying who uses them.
(13:46:42) noodles775: So perhaps, ISoftwareCenterAgentAPI, http://..../softwarecenteragent etc.?
(13:47:07) adeuring: noodles775: ok, so something like s/Person/Shopper/ ?
(13:48:29) noodles775: The app name usually matches the API, so perhaps ISoftwareCenterAgentApplication...
(13:50:06) adeuring: noodles775: ah, right. so, that, combined with a method name like getOrCreateShopCustomer()?
(13:50:20) noodles775: Sounds good.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/canonical/launchpad/interfaces/launchpad.py'
--- lib/canonical/launchpad/interfaces/launchpad.py 2010-06-22 17:08:23 +0000
+++ lib/canonical/launchpad/interfaces/launchpad.py 2010-07-02 12:20:58 +0000
@@ -296,6 +296,9 @@
296296
297 bugs = Attribute("""Launchpad Bugs XML-RPC end point.""")297 bugs = Attribute("""Launchpad Bugs XML-RPC end point.""")
298298
299 softwarecenteragent = Attribute(
300 """Software center agent XML-RPC end point.""")
301
299302
300class IAuthServerApplication(ILaunchpadApplication):303class IAuthServerApplication(ILaunchpadApplication):
301 """Launchpad legacy AuthServer application root."""304 """Launchpad legacy AuthServer application root."""
302305
=== modified file 'lib/canonical/launchpad/xmlrpc/application.py'
--- lib/canonical/launchpad/xmlrpc/application.py 2010-04-19 06:35:23 +0000
+++ lib/canonical/launchpad/xmlrpc/application.py 2010-07-02 12:20:58 +0000
@@ -27,6 +27,7 @@
27from lp.code.interfaces.codehosting import ICodehostingApplication27from lp.code.interfaces.codehosting import ICodehostingApplication
28from lp.code.interfaces.codeimportscheduler import (28from lp.code.interfaces.codeimportscheduler import (
29 ICodeImportSchedulerApplication)29 ICodeImportSchedulerApplication)
30from lp.registry.interfaces.person import ISoftwareCenterAgentApplication
30from canonical.launchpad.webapp import LaunchpadXMLRPCView31from canonical.launchpad.webapp import LaunchpadXMLRPCView
3132
3233
@@ -58,6 +59,11 @@
58 """See `IPrivateApplication`."""59 """See `IPrivateApplication`."""
59 return getUtility(IPrivateMaloneApplication)60 return getUtility(IPrivateMaloneApplication)
6061
62 @property
63 def softwarecenteragent(self):
64 """See `IPrivateApplication`."""
65 return getUtility(ISoftwareCenterAgentApplication)
66
6167
62class ISelfTest(Interface):68class ISelfTest(Interface):
63 """XMLRPC external interface for testing the XMLRPC external interface."""69 """XMLRPC external interface for testing the XMLRPC external interface."""
6470
=== modified file 'lib/canonical/launchpad/xmlrpc/configure.zcml'
--- lib/canonical/launchpad/xmlrpc/configure.zcml 2010-04-19 23:35:41 +0000
+++ lib/canonical/launchpad/xmlrpc/configure.zcml 2010-07-02 12:20:58 +0000
@@ -204,4 +204,7 @@
204 <require like_class="xmlrpclib.Fault" />204 <require like_class="xmlrpclib.Fault" />
205 </class>205 </class>
206206
207 <class class="canonical.launchpad.xmlrpc.faults.AccountSuspended">
208 <require like_class="xmlrpclib.Fault" />
209 </class>
207</configure>210</configure>
208211
=== modified file 'lib/canonical/launchpad/xmlrpc/faults.py'
--- lib/canonical/launchpad/xmlrpc/faults.py 2010-04-09 12:58:01 +0000
+++ lib/canonical/launchpad/xmlrpc/faults.py 2010-07-02 12:20:58 +0000
@@ -450,3 +450,14 @@
450450
451 def __init__(self, job_id):451 def __init__(self, job_id):
452 LaunchpadFault.__init__(self, job_id=job_id)452 LaunchpadFault.__init__(self, job_id=job_id)
453
454
455class AccountSuspended(LaunchpadFault):
456 """Raised by `ISoftwareCenterAgentAPI` when an account is suspended."""
457
458 error_code = 370
459 msg_template = ('The openid_identifier \'%(openid_identifier)s\''
460 ' is linked to a suspended account.')
461
462 def __init__(self, openid_identifier):
463 LaunchpadFault.__init__(self, openid_identifier=openid_identifier)
453464
=== modified file 'lib/lp/registry/configure.zcml'
--- lib/lp/registry/configure.zcml 2010-06-09 08:26:26 +0000
+++ lib/lp/registry/configure.zcml 2010-07-02 12:20:58 +0000
@@ -1006,6 +1006,16 @@
1006 interface="lp.registry.interfaces.mailinglist.IMailingListAPIView"1006 interface="lp.registry.interfaces.mailinglist.IMailingListAPIView"
1007 class="canonical.launchpad.xmlrpc.MailingListAPIView"1007 class="canonical.launchpad.xmlrpc.MailingListAPIView"
1008 permission="zope.Public"/>1008 permission="zope.Public"/>
1009 <securedutility
1010 class="lp.registry.xmlrpc.softwarecenteragent.SoftwareCenterAgentApplication"
1011 provides="lp.registry.interfaces.person.ISoftwareCenterAgentApplication">
1012 <allow interface="lp.registry.interfaces.person.ISoftwareCenterAgentApplication" />
1013 </securedutility>
1014 <xmlrpc:view
1015 for="lp.registry.interfaces.person.ISoftwareCenterAgentApplication"
1016 interface="lp.registry.interfaces.person.ISoftwareCenterAgentAPI"
1017 class="lp.registry.xmlrpc.softwarecenteragent.SoftwareCenterAgentAPI"
1018 permission="zope.Public"/>
10091019
1010 <!-- Helper page for held message approval -->1020 <!-- Helper page for held message approval -->
10111021
10121022
=== modified file 'lib/lp/registry/interfaces/person.py'
--- lib/lp/registry/interfaces/person.py 2010-07-02 12:20:53 +0000
+++ lib/lp/registry/interfaces/person.py 2010-07-02 12:20:58 +0000
@@ -16,6 +16,8 @@
16 'IPersonClaim',16 'IPersonClaim',
17 'IPersonPublic', # Required for a monkey patch in interfaces/archive.py17 'IPersonPublic', # Required for a monkey patch in interfaces/archive.py
18 'IPersonSet',18 'IPersonSet',
19 'ISoftwareCenterAgentAPI',
20 'ISoftwareCenterAgentApplication',
19 'IPersonViewRestricted',21 'IPersonViewRestricted',
20 'IRequestPeopleMerge',22 'IRequestPeopleMerge',
21 'ITeam',23 'ITeam',
@@ -75,7 +77,8 @@
75from canonical.launchpad.validators.email import email_validator77from canonical.launchpad.validators.email import email_validator
76from canonical.launchpad.validators.name import name_validator78from canonical.launchpad.validators.name import name_validator
77from canonical.launchpad.webapp.authorization import check_permission79from canonical.launchpad.webapp.authorization import check_permission
78from canonical.launchpad.webapp.interfaces import NameLookupFailed80from canonical.launchpad.webapp.interfaces import (
81 ILaunchpadApplication, NameLookupFailed)
7982
80from lp.app.interfaces.headings import IRootContext83from lp.app.interfaces.headings import IRootContext
81from lp.blueprints.interfaces.specificationtarget import (84from lp.blueprints.interfaces.specificationtarget import (
@@ -2086,6 +2089,26 @@
2086 required=True, vocabulary=TeamContactMethod)2089 required=True, vocabulary=TeamContactMethod)
20872090
20882091
2092class ISoftwareCenterAgentAPI(Interface):
2093 """XMLRPC API used by the software center agent."""
2094
2095 def getOrCreateSoftwareCenterCustomer(openid_identifier, email,
2096 full_name):
2097 """Get or create an LP person based on a given identifier.
2098
2099 See the method of the same name on `IPersonSet`. This XMLRPC version
2100 doesn't require the creation rationale and comment.
2101
2102 This is added as a private XMLRPC method instead of exposing via the
2103 API as it should not be needed long-term. Long term we should allow
2104 the software center to create subscriptions to private PPAs without
2105 requiring a Launchpad account.
2106 """
2107
2108class ISoftwareCenterAgentApplication(ILaunchpadApplication):
2109 """XMLRPC application root for ISoftwareCenterAgentAPI."""
2110
2111
2089class JoinNotAllowed(Exception):2112class JoinNotAllowed(Exception):
2090 """User is not allowed to join a given team."""2113 """User is not allowed to join a given team."""
20912114
20922115
=== added file 'lib/lp/registry/tests/test_xmlrpc.py'
--- lib/lp/registry/tests/test_xmlrpc.py 1970-01-01 00:00:00 +0000
+++ lib/lp/registry/tests/test_xmlrpc.py 2010-07-02 12:20:58 +0000
@@ -0,0 +1,125 @@
1# Copyright 2010 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Testing registry-related xmlrpc calls."""
5
6__metaclass__ = type
7
8import unittest
9import xmlrpclib
10from zope.component import getUtility
11from zope.security.proxy import removeSecurityProxy
12
13from canonical.functional import XMLRPCTestTransport
14from canonical.launchpad.interfaces import IPrivateApplication
15from canonical.launchpad.interfaces.account import AccountStatus
16from canonical.launchpad.webapp.servers import LaunchpadTestRequest
17from canonical.testing.layers import LaunchpadFunctionalLayer
18from lp.registry.interfaces.person import (
19 IPersonSet, ISoftwareCenterAgentAPI, ISoftwareCenterAgentApplication,
20 PersonCreationRationale)
21from lp.registry.xmlrpc.softwarecenteragent import SoftwareCenterAgentAPI
22from lp.testing import TestCaseWithFactory
23
24
25class TestSoftwareCenterAgentAPI(TestCaseWithFactory):
26
27 layer = LaunchpadFunctionalLayer
28
29 def setUp(self):
30 super(TestSoftwareCenterAgentAPI, self).setUp()
31 self.private_root = getUtility(IPrivateApplication)
32 self.sca_api = SoftwareCenterAgentAPI(
33 context=self.private_root.softwarecenteragent,
34 request=LaunchpadTestRequest())
35
36 def test_provides_interface(self):
37 # The view interface is provided.
38 self.assertProvides(self.sca_api, ISoftwareCenterAgentAPI)
39
40 def test_getOrCreateSoftwareCenterCustomer(self):
41 # The method returns the username of the person, and sets the
42 # correct creation rational/comment.
43 user_name = self.sca_api.getOrCreateSoftwareCenterCustomer(
44 'openid-ident', 'alice@b.com', 'Joe Blogs')
45
46 self.assertEqual('alice', user_name)
47 person = getUtility(IPersonSet).getByName(user_name)
48 self.assertEqual(
49 'openid-ident',
50 removeSecurityProxy(person.account).openid_identifier)
51 self.assertEqual(
52 PersonCreationRationale.SOFTWARE_CENTER_PURCHASE,
53 person.creation_rationale)
54 self.assertEqual(
55 "when purchasing an application via Software Center.",
56 person.creation_comment)
57
58
59class TestSoftwareCenterAgentApplication(TestCaseWithFactory):
60
61 layer = LaunchpadFunctionalLayer
62
63 def setUp(self):
64 super(TestSoftwareCenterAgentApplication, self).setUp()
65 self.private_root = getUtility(IPrivateApplication)
66 self.rpc_proxy = xmlrpclib.ServerProxy(
67 'http://xmlrpc-private.launchpad.dev:8087/softwarecenteragent',
68 transport=XMLRPCTestTransport())
69
70 def test_provides_interface(self):
71 # The application is provided.
72 self.assertProvides(
73 self.private_root.softwarecenteragent,
74 ISoftwareCenterAgentApplication)
75
76 def test_getOrCreateSoftwareCenterCustomer_xmlrpc(self):
77 # The method can be called via xmlrpc
78 user_name = self.rpc_proxy.getOrCreateSoftwareCenterCustomer(
79 'openid-ident', 'a@b.com', 'Joe Blogs')
80 person = getUtility(IPersonSet).getByName(user_name)
81 self.assertEqual(
82 'openid-ident',
83 removeSecurityProxy(person.account).openid_identifier)
84
85 def test_getOrCreateSoftwareCenterCustomer_xmlrpc_error(self):
86 # A suspended account results in an appropriate xmlrpc fault.
87 suspended_account = self.factory.makeAccount(
88 'Joe Blogs', email='a@b.com', status=AccountStatus.SUSPENDED)
89 openid_identifier = removeSecurityProxy(
90 suspended_account).openid_identifier
91
92 # assertRaises doesn't let us check the type of Fault.
93 fault_raised = False
94 try:
95 self.rpc_proxy.getOrCreateSoftwareCenterCustomer(
96 openid_identifier, 'a@b.com', 'Joe Blogs')
97 except xmlrpclib.Fault, e:
98 fault_raised = True
99 self.assertEqual(370, e.faultCode)
100 self.assertIn(openid_identifier, e.faultString)
101
102 self.assertTrue(fault_raised)
103
104 def test_not_available_on_public_api(self):
105 # The person set api is not available on the public xmlrpc
106 # service.
107 public_rpc_proxy = xmlrpclib.ServerProxy(
108 'http://test@canonical.com:test@'
109 'xmlrpc.launchpad.dev/softwarecenteragent',
110 transport=XMLRPCTestTransport())
111
112 # assertRaises doesn't let us check the type of Fault.
113 protocol_error_raised = False
114 try:
115 public_rpc_proxy.getOrCreateSoftwareCenterCustomer(
116 'openid-ident', 'a@b.com', 'Joe Blogs')
117 except xmlrpclib.ProtocolError, e:
118 protocol_error_raised = True
119 self.assertEqual(404, e.errcode)
120
121 self.assertTrue(protocol_error_raised)
122
123
124def test_suite():
125 return unittest.TestLoader().loadTestsFromName(__name__)
0126
=== added directory 'lib/lp/registry/xmlrpc'
=== added file 'lib/lp/registry/xmlrpc/__init__.py'
=== added file 'lib/lp/registry/xmlrpc/softwarecenteragent.py'
--- lib/lp/registry/xmlrpc/softwarecenteragent.py 1970-01-01 00:00:00 +0000
+++ lib/lp/registry/xmlrpc/softwarecenteragent.py 2010-07-02 12:20:58 +0000
@@ -0,0 +1,47 @@
1# Copyright 2010 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""XMLRPC APIs for person set."""
5
6__metaclass__ = type
7__all__ = [
8 'SoftwareCenterAgentAPI',
9 ]
10
11
12from zope.component import getUtility
13from zope.interface import implements
14
15from canonical.launchpad.interfaces.account import AccountSuspendedError
16from canonical.launchpad.webapp import LaunchpadXMLRPCView
17from canonical.launchpad.xmlrpc import faults
18from lp.registry.interfaces.person import (
19 IPersonSet, ISoftwareCenterAgentAPI, ISoftwareCenterAgentApplication,
20 PersonCreationRationale)
21
22
23class SoftwareCenterAgentAPI(LaunchpadXMLRPCView):
24 """See `ISoftwareCenterAgentAPI`."""
25
26 implements(ISoftwareCenterAgentAPI)
27
28 def getOrCreateSoftwareCenterCustomer(self, openid_identifier, email,
29 full_name):
30 try:
31 person, db_updated = getUtility(
32 IPersonSet).getOrCreateByOpenIDIdentifier(
33 openid_identifier, email, full_name,
34 PersonCreationRationale.SOFTWARE_CENTER_PURCHASE,
35 "when purchasing an application via Software Center.")
36 except AccountSuspendedError:
37 return faults.AccountSuspended(openid_identifier)
38
39 return person.name
40
41
42class SoftwareCenterAgentApplication:
43 """Software center agent end-point."""
44 implements(ISoftwareCenterAgentApplication)
45
46 title = "Software Center Agent API"
47

Subscribers

People subscribed via source and target branches

to status/vote changes: