← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~wgrant/launchpad/sso-xmlrpc into lp:launchpad

 

William Grant has proposed merging lp:~wgrant/launchpad/sso-xmlrpc into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~wgrant/launchpad/sso-xmlrpc/+merge/92937

SSO currently grabs a person's name, timezone and team memberships from a few mirrored LP DB tables. This means the two databases must be part of a single Slony cluster, causing awkward deployment constraints.

After discussions with ISD, we're switching SSO to a private XML-RPC API provided by LP. This is that API. SSO will make a request with the OpenID identifier fragment, and get back the name, timezone and public and private team memberships.
-- 
https://code.launchpad.net/~wgrant/launchpad/sso-xmlrpc/+merge/92937
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~wgrant/launchpad/sso-xmlrpc into lp:launchpad.
=== modified file 'lib/lp/registry/configure.zcml'
--- lib/lp/registry/configure.zcml	2012-02-14 00:20:34 +0000
+++ lib/lp/registry/configure.zcml	2012-02-14 10:09:49 +0000
@@ -1108,6 +1108,16 @@
         interface="lp.registry.interfaces.person.ISoftwareCenterAgentAPI"
         class="lp.registry.xmlrpc.softwarecenteragent.SoftwareCenterAgentAPI"
         permission="zope.Public"/>
+    <securedutility
+        class="lp.registry.xmlrpc.canonicalsso.CanonicalSSOApplication"
+        provides="lp.registry.interfaces.person.ICanonicalSSOApplication">
+      <allow interface="lp.registry.interfaces.person.ICanonicalSSOApplication" />
+    </securedutility>
+    <xmlrpc:view
+        for="lp.registry.interfaces.person.ICanonicalSSOApplication"
+        interface="lp.registry.interfaces.person.ICanonicalSSOAPI"
+        class="lp.registry.xmlrpc.canonicalsso.CanonicalSSOAPI"
+        permission="zope.Public"/>
 
     <!-- Helper page for held message approval -->
 

=== modified file 'lib/lp/registry/interfaces/person.py'
--- lib/lp/registry/interfaces/person.py	2012-02-14 00:20:34 +0000
+++ lib/lp/registry/interfaces/person.py	2012-02-14 10:09:49 +0000
@@ -11,6 +11,8 @@
     'CLOSED_TEAM_POLICY',
     'IAdminPeopleMergeSchema',
     'IAdminTeamMergeSchema',
+    'ICanonicalSSOAPI',
+    'ICanonicalSSOApplication',
     'IHasStanding',
     'IObjectReassignment',
     'IPerson',
@@ -18,11 +20,11 @@
     'IPersonPublic',
     'IPersonSet',
     'IPersonSettings',
-    'ISoftwareCenterAgentAPI',
-    'ISoftwareCenterAgentApplication',
     'IPersonLimitedView',
     'IPersonViewRestricted',
     'IRequestPeopleMerge',
+    'ISoftwareCenterAgentAPI',
+    'ISoftwareCenterAgentApplication',
     'ITeam',
     'ITeamContactAddressForm',
     'ITeamCreation',
@@ -2550,6 +2552,17 @@
         """
 
 
+class ICanonicalSSOApplication(ILaunchpadApplication):
+    """XMLRPC application root for ICanonicalSSOAPI."""
+
+
+class ICanonicalSSOAPI(Interface):
+    """XMLRPC API used by the software center agent."""
+
+    def getPersonDetailsByOpenIDIdentifier(openid_identifier):
+        """Get the details of an LP person based on an OpenID identifier."""
+
+
 class ISoftwareCenterAgentApplication(ILaunchpadApplication):
     """XMLRPC application root for ISoftwareCenterAgentAPI."""
 

=== modified file 'lib/lp/registry/tests/test_xmlrpc.py'
--- lib/lp/registry/tests/test_xmlrpc.py	2012-01-20 06:58:13 +0000
+++ lib/lp/registry/tests/test_xmlrpc.py	2012-02-14 10:09:49 +0000
@@ -15,19 +15,20 @@
     ISoftwareCenterAgentAPI,
     ISoftwareCenterAgentApplication,
     PersonCreationRationale,
+    PersonVisibility,
     )
 from lp.registry.xmlrpc.softwarecenteragent import SoftwareCenterAgentAPI
 from lp.services.identity.interfaces.account import AccountStatus
 from lp.services.webapp.servers import LaunchpadTestRequest
 from lp.testing import TestCaseWithFactory
-from lp.testing.layers import LaunchpadFunctionalLayer
+from lp.testing.layers import DatabaseFunctionalLayer
 from lp.testing.xmlrpc import XMLRPCTestTransport
 from lp.xmlrpc.interfaces import IPrivateApplication
 
 
 class TestSoftwareCenterAgentAPI(TestCaseWithFactory):
 
-    layer = LaunchpadFunctionalLayer
+    layer = DatabaseFunctionalLayer
 
     def setUp(self):
         super(TestSoftwareCenterAgentAPI, self).setUp()
@@ -61,7 +62,7 @@
 
 class TestSoftwareCenterAgentApplication(TestCaseWithFactory):
 
-    layer = LaunchpadFunctionalLayer
+    layer = DatabaseFunctionalLayer
 
     def setUp(self):
         super(TestSoftwareCenterAgentApplication, self).setUp()
@@ -129,14 +130,54 @@
             'http://test@xxxxxxxxxxxxx:test@'
             'xmlrpc.launchpad.dev/softwarecenteragent',
             transport=XMLRPCTestTransport())
-
-        # assertRaises doesn't let us check the type of Fault.
-        protocol_error_raised = False
-        try:
-            public_rpc_proxy.getOrCreateSoftwareCenterCustomer(
-                'openid-ident', 'a@xxxxx', 'Joe Blogs')
-        except xmlrpclib.ProtocolError, e:
-            protocol_error_raised = True
-            self.assertEqual(404, e.errcode)
-
-        self.assertTrue(protocol_error_raised)
+        e = self.assertRaises(
+            xmlrpclib.ProtocolError,
+            public_rpc_proxy.getOrCreateSoftwareCenterCustomer,
+            'openid-ident', 'a@xxxxx', 'Joe Blogs')
+        self.assertEqual(404, e.errcode)
+
+
+class TestCanonicalSSOApplication(TestCaseWithFactory):
+
+    layer = DatabaseFunctionalLayer
+
+    def setUp(self):
+        super(TestCanonicalSSOApplication, self).setUp()
+        self.rpc_proxy = xmlrpclib.ServerProxy(
+            'http://xmlrpc-private.launchpad.dev:8087/canonicalsso',
+            transport=XMLRPCTestTransport())
+
+    def test_getPersonDetailsByOpenIDIdentifier(self):
+        person = self.factory.makePerson(time_zone='Australia/Melbourne')
+        self.factory.makeTeam(
+            name='pubteam', members=[person],
+            visibility=PersonVisibility.PUBLIC)
+        self.factory.makeTeam(
+            name='privteam', members=[person],
+            visibility=PersonVisibility.PRIVATE)
+        openid_identifier = removeSecurityProxy(
+            person.account).openid_identifiers.any().identifier
+        result = self.rpc_proxy.getPersonDetailsByOpenIDIdentifier(
+            openid_identifier)
+        self.assertEqual(
+            dict(
+                name=person.name,
+                time_zone=person.location.time_zone,
+                teams={'pubteam': False, 'privteam': True}),
+            result)
+
+    def test_not_available_on_public_api(self):
+        # The person set api is not available on the public xmlrpc
+        # service.
+        person = self.factory.makePerson()
+        openid_identifier = removeSecurityProxy(
+            person.account).openid_identifiers.any().identifier
+        public_rpc_proxy = xmlrpclib.ServerProxy(
+            'http://test@xxxxxxxxxxxxx:test@'
+            'xmlrpc.launchpad.dev/canonicalsso',
+            transport=XMLRPCTestTransport())
+        e = self.assertRaises(
+            xmlrpclib.ProtocolError,
+            public_rpc_proxy.getPersonDetailsByOpenIDIdentifier,
+            openid_identifier)
+        self.assertEqual(404, e.errcode)

=== added file 'lib/lp/registry/xmlrpc/canonicalsso.py'
--- lib/lp/registry/xmlrpc/canonicalsso.py	1970-01-01 00:00:00 +0000
+++ lib/lp/registry/xmlrpc/canonicalsso.py	2012-02-14 10:09:49 +0000
@@ -0,0 +1,56 @@
+# Copyright 2012 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""XMLRPC APIs for Canonical SSO to retrieve person details."""
+
+__metaclass__ = type
+__all__ = [
+    'CanonicalSSOAPI',
+    'CanonicalSSOApplication',
+    ]
+
+
+from zope.component import getUtility
+from zope.interface import implements
+from zope.security.proxy import removeSecurityProxy
+
+from lp.registry.interfaces.person import (
+    ICanonicalSSOAPI,
+    ICanonicalSSOApplication,
+    IPerson,
+    )
+from lp.services.identity.interfaces.account import IAccountSet
+from lp.services.webapp import LaunchpadXMLRPCView
+
+
+class CanonicalSSOAPI(LaunchpadXMLRPCView):
+    """See `ICanonicalSSOAPI`."""
+
+    implements(ICanonicalSSOAPI)
+
+    def getPersonDetailsByOpenIDIdentifier(self, openid_identifier):
+        try:
+            account = getUtility(IAccountSet).getByOpenIDIdentifier(
+                openid_identifier.decode('ascii'))
+        except LookupError:
+            return None
+        person = IPerson(account, None)
+        if person is None:
+            return
+
+        time_zone = person.location and person.location.time_zone
+        team_names = dict(
+            (removeSecurityProxy(t).name, t.private)
+            for t in person.teams_participated_in)
+        return {
+            'name': person.name,
+            'time_zone': time_zone,
+            'teams': team_names,
+            }
+
+
+class CanonicalSSOApplication:
+    """Canonical SSO end-point."""
+    implements(ICanonicalSSOApplication)
+
+    title = "Canonical SSO API"

=== modified file 'lib/lp/xmlrpc/application.py'
--- lib/lp/xmlrpc/application.py	2012-01-01 02:58:52 +0000
+++ lib/lp/xmlrpc/application.py	2012-02-14 10:09:49 +0000
@@ -27,7 +27,10 @@
     ICodeImportSchedulerApplication,
     )
 from lp.registry.interfaces.mailinglist import IMailingListApplication
-from lp.registry.interfaces.person import ISoftwareCenterAgentApplication
+from lp.registry.interfaces.person import (
+    ICanonicalSSOApplication,
+    ISoftwareCenterAgentApplication,
+    )
 from lp.services.authserver.interfaces import IAuthServerApplication
 from lp.services.features.xmlrpc import IFeatureFlagApplication
 from lp.services.webapp import LaunchpadXMLRPCView
@@ -70,6 +73,11 @@
         return getUtility(ISoftwareCenterAgentApplication)
 
     @property
+    def canonicalsso(self):
+        """See `IPrivateApplication`."""
+        return getUtility(ICanonicalSSOApplication)
+
+    @property
     def featureflags(self):
         """See `IPrivateApplication`."""
         return getUtility(IFeatureFlagApplication)

=== modified file 'lib/lp/xmlrpc/interfaces.py'
--- lib/lp/xmlrpc/interfaces.py	2011-12-24 17:49:30 +0000
+++ lib/lp/xmlrpc/interfaces.py	2012-02-14 10:09:49 +0000
@@ -30,4 +30,7 @@
     softwarecenteragent = Attribute(
         """Software center agent XML-RPC end point.""")
 
+    canonicalsso = Attribute(
+        """Canonical SSO XML-RPC end point.""")
+
     featureflags = Attribute("""Feature flag information endpoint""")