[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