[Python-modules-commits] [python-social-auth] 12/89: Add NGP VAN ActionID support

Wolfgang Borgert debacle at moszumanska.debian.org
Sat Dec 24 15:14:52 UTC 2016


This is an automated email from the git hooks/post-receive script.

debacle pushed a commit to tag v0.2.15
in repository python-social-auth.

commit d8d711946bbf6c4eddfb8d43c1cf37f1fc03e89c
Author: Nick Catalano <nickcatal at gmail.com>
Date:   Wed Sep 30 02:26:13 2015 -0500

    Add NGP VAN ActionID support
---
 README.rst                           |   2 +
 docs/backends/index.rst              |   1 +
 docs/backends/ngpvan_actionid.rst    |  36 +++++++
 docs/intro.rst                       |   2 +
 social/backends/ngpvan.py            |  61 +++++++++++
 social/tests/backends/test_ngpvan.py | 193 +++++++++++++++++++++++++++++++++++
 6 files changed, 295 insertions(+)

diff --git a/README.rst b/README.rst
index 03df9cd..3117b7d 100644
--- a/README.rst
+++ b/README.rst
@@ -93,6 +93,7 @@ or current ones extended):
     * `Moves app`_ OAuth2 https://dev.moves-app.com/docs/authentication
     * `Mozilla Persona`_
     * NaszaKlasa_ OAuth2
+    * `NGPVAN ActionID`_ OpenId
     * Odnoklassniki_ OAuth2 and Application Auth
     * OpenId_
     * OpenStreetMap_ OAuth1 http://wiki.openstreetmap.org/wiki/OAuth
@@ -267,6 +268,7 @@ check `django-social-auth LICENSE`_ for details:
 .. _Moves app: https://dev.moves-app.com/docs/
 .. _Mozilla Persona: http://www.mozilla.org/persona/
 .. _NaszaKlasa: https://developers.nk.pl/
+.. _NGPVAN ActionID: http://developers.ngpvan.com/action-id
 .. _Odnoklassniki: http://www.odnoklassniki.ru
 .. _Pocket: http://getpocket.com
 .. _Podio: https://podio.com
diff --git a/docs/backends/index.rst b/docs/backends/index.rst
index 799ca62..c54ce04 100644
--- a/docs/backends/index.rst
+++ b/docs/backends/index.rst
@@ -98,6 +98,7 @@ Social backends
    moves
    naszaklasa
    nationbuilder
+   ngpvan_actionid
    odnoklassnikiru
    openstreetmap
    orbi
diff --git a/docs/backends/ngpvan_actionid.rst b/docs/backends/ngpvan_actionid.rst
new file mode 100644
index 0000000..cc980a7
--- /dev/null
+++ b/docs/backends/ngpvan_actionid.rst
@@ -0,0 +1,36 @@
+NGP VAN ActionID
+================
+
+`NGP VAN`_'s ActionID_ service provides an OpenID 1.1 endpoint, which provides
+first name, last name, email address, and phone number.
+
+ActionID doesn't require major settings beside being defined on
+``AUTHENTICATION_BACKENDS``
+
+.. code-block:: python
+
+    SOCIAL_AUTH_AUTHENTICATION_BACKENDS = (
+        ...
+        'social.backends.ngpvan.ActionIDOpenID',
+        ...
+    )
+
+
+If you want to be able to access the "phone" attribute offered by NGP VAN
+within ``extra_data`` you can add the following to your settings:
+
+.. code-block:: python
+
+    SOCIAL_AUTH_ACTIONID_OPENID_AX_EXTRA_DATA = [
+        ('http://openid.net/schema/contact/phone/business', 'phone')
+    ]
+
+
+NGP VAN offers the ability to have your domain whitelisted, which will disable
+the "{domain} is requesting a link to your ActionID" warning when your app
+attempts to login using an ActionID account. Contact
+`NGP VAN Developer Support`_ for more information
+
+.. _NGP VAN: http://www.ngpvan.com/
+.. _ActionID: http://developers.ngpvan.com/action-id
+.. _NGP VAN Developer Support: http://developers.ngpvan.com/support/contact
diff --git a/docs/intro.rst b/docs/intro.rst
index b138974..191d7eb 100644
--- a/docs/intro.rst
+++ b/docs/intro.rst
@@ -61,6 +61,7 @@ or extend current one):
     * Mixcloud_ OAuth2
     * `Mozilla Persona`_
     * NaszaKlasa_ OAuth2
+    * `NGPVAN ActionID`_ OpenId
     * Odnoklassniki_ OAuth2 and Application Auth
     * OpenId_
     * Podio_ OAuth2
@@ -141,6 +142,7 @@ section.
 .. _Mixcloud: https://www.mixcloud.com
 .. _Mozilla Persona: http://www.mozilla.org/persona/
 .. _NaszaKlasa: https://developers.nk.pl/
+.. _NGPVAN ActionID: http://developers.ngpvan.com/action-id
 .. _Odnoklassniki: http://www.odnoklassniki.ru
 .. _Podio: https://podio.com
 .. _Shopify: http://shopify.com
diff --git a/social/backends/ngpvan.py b/social/backends/ngpvan.py
new file mode 100644
index 0000000..42a34c8
--- /dev/null
+++ b/social/backends/ngpvan.py
@@ -0,0 +1,61 @@
+"""
+NGP VAN's `ActionID` Provider
+
+http://developers.ngpvan.com/action-id
+"""
+from social.backends.open_id import OpenIdAuth
+from openid.extensions import ax
+
+
+class ActionIDOpenID(OpenIdAuth):
+    """
+    NGP VAN's ActionID OpenID 1.1 authentication backend
+    """
+    name = 'actionid-openid'
+    URL = 'https://accounts.ngpvan.com/Home/Xrds'
+    USERNAME_KEY = 'email'
+
+    def get_ax_attributes(self):
+        """
+        Return the AX attributes that ActionID responds with, as well as the
+        user data result that it must map to.
+        """
+        return [
+            ('http://openid.net/schema/contact/internet/email', 'email'),
+            ('http://openid.net/schema/contact/phone/business', 'phone'),
+            ('http://openid.net/schema/namePerson/first', 'first_name'),
+            ('http://openid.net/schema/namePerson/last', 'last_name'),
+            ('http://openid.net/schema/namePerson', 'fullname'),
+        ]
+
+    def setup_request(self, params=None):
+        """
+        Setup the OpenID request
+
+        Because ActionID does not advertise the availiability of AX attributes
+        nor use standard attribute aliases, we need to setup the attributes
+        manually instead of rely on the parent OpenIdAuth.setup_request()
+        """
+        request = self.openid_request(params)
+
+        fetch_request = ax.FetchRequest()
+        fetch_request.add(ax.AttrInfo(
+            'http://openid.net/schema/contact/internet/email',
+            alias='ngpvanemail',
+            required=True))
+
+        fetch_request.add(ax.AttrInfo(
+            'http://openid.net/schema/contact/phone/business',
+            alias='ngpvanphone',
+            required=False))
+        fetch_request.add(ax.AttrInfo(
+            'http://openid.net/schema/namePerson/first',
+            alias='ngpvanfirstname',
+            required=False))
+        fetch_request.add(ax.AttrInfo(
+            'http://openid.net/schema/namePerson/last',
+            alias='ngpvanlastname',
+            required=False))
+        request.addExtension(fetch_request)
+
+        return request
diff --git a/social/tests/backends/test_ngpvan.py b/social/tests/backends/test_ngpvan.py
new file mode 100644
index 0000000..1189839
--- /dev/null
+++ b/social/tests/backends/test_ngpvan.py
@@ -0,0 +1,193 @@
+"""Tests for NGP VAN ActionID Backend"""
+import datetime
+
+from httpretty import HTTPretty
+
+from social.p3 import urlencode
+from social.tests.backends.open_id import OpenIdTest
+
+
+JANRAIN_NONCE = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ')
+
+
+class NGPVANActionIDOpenIDTest(OpenIdTest):
+    """Test the NGP VAN ActionID OpenID 1.1 Backend"""
+    backend_path = 'social.backends.ngpvan.ActionIDOpenID'
+    expected_username = 'testuser at user.local'
+    discovery_body = ' '.join(
+        [
+            '<?xml version="1.0" encoding="UTF-8"?>',
+            '<xrds:XRDS',
+            'xmlns:xrds="xri://$xrds"',
+            'xmlns:openid="http://openid.net/xmlns/1.0"',
+            'xmlns="xri://$xrd*($v*2.0)">',
+            '<XRD>',
+            '<Service priority="10">',
+            '<Type>http://specs.openid.net/auth/2.0/signon</Type>',
+            '<Type>http://openid.net/extensions/sreg/1.1</Type>',
+            '<Type>http://axschema.org/contact/email</Type>',
+            '<URI>https://accounts.ngpvan.com/OpenId/Provider</URI>',
+            '</Service>',
+            '<Service priority="20">',
+            '<Type>http://openid.net/signon/1.0</Type>',
+            '<Type>http://openid.net/extensions/sreg/1.1</Type>',
+            '<Type>http://axschema.org/contact/email</Type>',
+            '<URI>https://accounts.ngpvan.com/OpenId/Provider</URI>',
+            '</Service>',
+            '</XRD>',
+            '</xrds:XRDS>',
+        ])
+    server_response = urlencode({
+        'openid.claimed_id': 'https://accounts.ngpvan.com/user/abcd123',
+        'openid.identity': 'https://accounts.ngpvan.com/user/abcd123',
+        'openid.sig': 'Midw8F/rCDwW7vMz3y+vK6rjz6s=',
+        'openid.signed': 'claimed_id,identity,assoc_handle,op_endpoint,return_'
+                         'to,response_nonce,ns.alias3,alias3.mode,alias3.type.'
+                         'alias1,alias3.value.alias1,alias3.type.alias2,alias3'
+                         '.value.alias2,alias3.type.alias3,alias3.value.alias3'
+                         ',alias3.type.alias4,alias3.value.alias4,alias3.type.'
+                         'alias5,alias3.value.alias5,alias3.type.alias6,alias3'
+                         '.value.alias6,alias3.type.alias7,alias3.value.alias7'
+                         ',alias3.type.alias8,alias3.value.alias8,ns.sreg,sreg'
+                         '.fullname',
+        'openid.assoc_handle': '{635790678917902781}{GdSyFA==}{20}',
+        'openid.op_endpoint': 'https://accounts.ngpvan.com/OpenId/Provider',
+        'openid.return_to': 'http://myapp.com/complete/actionid-openid/',
+        'openid.response_nonce': JANRAIN_NONCE + 'MMgBGEre',
+        'openid.mode': 'id_res',
+        'openid.ns': 'http://specs.openid.net/auth/2.0',
+        'openid.ns.alias3': 'http://openid.net/srv/ax/1.0',
+        'openid.alias3.mode': 'fetch_response',
+        'openid.alias3.type.alias1': 'http://openid.net/schema/contact/phone/b'
+                                     'usiness',
+        'openid.alias3.value.alias1': '+12015555555',
+        'openid.alias3.type.alias2': 'http://openid.net/schema/contact/interne'
+                                     't/email',
+        'openid.alias3.value.alias2': 'testuser at user.local',
+        'openid.alias3.type.alias3': 'http://openid.net/schema/namePerson/firs'
+                                     't',
+        'openid.alias3.value.alias3': 'John',
+        'openid.alias3.type.alias4': 'http://openid.net/schema/namePerson/las'
+                                     't',
+        'openid.alias3.value.alias4': 'Smith',
+        'openid.alias3.type.alias5': 'http://axschema.org/namePerson/first',
+        'openid.alias3.value.alias5': 'John',
+        'openid.alias3.type.alias6': 'http://axschema.org/namePerson/last',
+        'openid.alias3.value.alias6': 'Smith',
+        'openid.alias3.type.alias7': 'http://axschema.org/namePerson',
+        'openid.alias3.value.alias7': 'John Smith',
+        'openid.alias3.type.alias8': 'http://openid.net/schema/namePerson',
+        'openid.alias3.value.alias8': 'John Smith',
+        'openid.ns.sreg': 'http://openid.net/extensions/sreg/1.1',
+        'openid.sreg.fullname': 'John Smith',
+    })
+
+    def setUp(self):
+        """Setup the test"""
+        super(NGPVANActionIDOpenIDTest, self).setUp()
+
+        # Mock out the NGP VAN endpoints
+        HTTPretty.register_uri(
+            HTTPretty.POST,
+            'https://accounts.ngpvan.com/Home/Xrds',
+            status=200,
+            body=self.discovery_body)
+        HTTPretty.register_uri(
+            HTTPretty.GET,
+            'https://accounts.ngpvan.com/user/abcd123',
+            status=200,
+            body=self.discovery_body)
+        HTTPretty.register_uri(
+            HTTPretty.GET,
+            'https://accounts.ngpvan.com/OpenId/Provider',
+            status=200,
+            body=self.discovery_body)
+
+    def test_login(self):
+        """Test the login flow using python-social-auth's built in test"""
+        self.do_login()
+
+    def test_partial_pipeline(self):
+        """Test the partial flow using python-social-auth's built in test"""
+        self.do_partial_pipeline()
+
+    def test_get_ax_attributes(self):
+        """Test that the AX attributes that NGP VAN responds with are present"""
+        records = self.backend.get_ax_attributes()
+
+        self.assertEqual(
+            records,
+            [
+                ('http://openid.net/schema/contact/internet/email', 'email'),
+                ('http://openid.net/schema/contact/phone/business', 'phone'),
+                ('http://openid.net/schema/namePerson/first', 'first_name'),
+                ('http://openid.net/schema/namePerson/last', 'last_name'),
+                ('http://openid.net/schema/namePerson', 'fullname'),
+            ]
+        )
+
+    def test_setup_request(self):
+        """Test the setup_request functionality in the NGP VAN backend"""
+        # We can grab the requested attributes by grabbing the HTML of the
+        # OpenID auth form and pulling out the hidden fields
+        _, inputs = self.get_form_data(self.backend.auth_html())
+
+        # Confirm that the only required attribute is email
+        self.assertEqual(inputs['openid.ax.required'], 'ngpvanemail')
+
+        # Confirm that the 3 optional attributes are requested "if available"
+        self.assertIn('ngpvanphone', inputs['openid.ax.if_available'])
+        self.assertIn('ngpvanfirstname', inputs['openid.ax.if_available'])
+        self.assertIn('ngpvanlastname', inputs['openid.ax.if_available'])
+
+        # Verify the individual attribute properties
+        self.assertEqual(
+            inputs['openid.ax.type.ngpvanemail'],
+            'http://openid.net/schema/contact/internet/email')
+        self.assertEqual(
+            inputs['openid.ax.type.ngpvanfirstname'],
+            'http://openid.net/schema/namePerson/first')
+        self.assertEqual(
+            inputs['openid.ax.type.ngpvanlastname'],
+            'http://openid.net/schema/namePerson/last')
+        self.assertEqual(
+            inputs['openid.ax.type.ngpvanphone'],
+            'http://openid.net/schema/contact/phone/business')
+
+    def test_user_data(self):
+        """Ensure that the correct user data is being passed to create_user"""
+        self.strategy.set_settings({
+            'USER_FIELDS': [
+                'email',
+                'first_name',
+                'last_name',
+                'username',
+                'phone',
+                'fullname'
+            ]
+        })
+        user = self.do_start()
+
+        self.assertEqual(user.username, u'testuser at user.local')
+        self.assertEqual(user.email, u'testuser at user.local')
+        self.assertEqual(user.extra_user_fields['phone'], u'+12015555555')
+        self.assertEqual(user.extra_user_fields['first_name'], u'John')
+        self.assertEqual(user.extra_user_fields['last_name'], u'Smith')
+        self.assertEqual(user.extra_user_fields['fullname'], u'John Smith')
+
+    def test_extra_data_phone(self):
+        """Confirm that you can get a phone number via the relevant setting"""
+        self.strategy.set_settings({
+            'SOCIAL_AUTH_ACTIONID_OPENID_AX_EXTRA_DATA': [
+                ('http://openid.net/schema/contact/phone/business', 'phone')
+            ]
+        })
+        user = self.do_start()
+        self.assertEqual(user.social_user.extra_data['phone'], u'+12015555555')
+
+    def test_association_uid(self):
+        """Test that the correct association uid is stored in the database"""
+        user = self.do_start()
+        self.assertEqual(
+            user.social_user.uid,
+            'https://accounts.ngpvan.com/user/abcd123')

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-social-auth.git



More information about the Python-modules-commits mailing list