]> git.parisson.com Git - django-social-auth.git/commitdiff
Store extra data in JSON format, also add method to extend extra values to store...
authorMatías Aguirre <matiasaguirre@gmail.com>
Wed, 23 Feb 2011 20:24:10 +0000 (18:24 -0200)
committerMatías Aguirre <matiasaguirre@gmail.com>
Wed, 23 Feb 2011 20:24:30 +0000 (18:24 -0200)
README.rst
social_auth/backends/__init__.py
social_auth/backends/facebook.py
social_auth/backends/twitter.py
social_auth/models.py

index 32f75a47f07118dcf10c8a3ace90ee94c21b4306..b868f031863bd458096d527be4cfbed57ca5b674 100644 (file)
@@ -190,11 +190,14 @@ Configuration
 
   Final user name will have an integer suffix in case it's already taken.
 
-- OAuth_ authentication will store access_token by default, set this value
-  to False to avoid such behavior::
+- Backends will store extra values from response by default, set this to False
+  to avoid such behavior::
 
     SOCIAL_AUTH_EXTRA_DATA = False
 
+  Also more extra values will be stored if defined, details about this setting
+  are listed below on OpenId and OAuth sections.
+
 - It's possible to override the used User model if needed::
 
     SOCIAL_AUTH_USER_MODEL = 'myapp.CustomUser'
@@ -255,6 +258,24 @@ OpenId_ support is simpler to implement than OAuth_. Google and Yahoo
 providers are supported by default, others are supported by POST method
 providing endpoint URL.
 
+OpenId_ backends can store extra data in UserSocialAuth.extra_data field
+by defining a set of values names to retrieve from any of the used schemas,
+pettributeExchange and SimpleRegistration. As their keywords differ we need
+two settings.
+
+Settings is per backend, so we have two possible values for each one. Name
+is dynamically checked using uppercase backend name as prefix::
+
+    <uppercase backend name>_SREG_EXTRA_DATA
+    <uppercase backend name>_AX_EXTRA_DATA
+
+Example::
+
+    GOOGLE_SREG_EXTRA_DATA = [(..., ...)]
+    GOOGLE_AX_EXTRA_DATA = [(..., ...)]
+
+Settings must be a list of tuples mapping value name in response and value
+alias used to store.
 
 -----
 OAuth
@@ -266,6 +287,20 @@ but provides the option for unregistered applications.
 
 Check next sections for details.
 
+OAuth_ backends also can store extra data in UserSocialAuth.extra_data field
+by defining a set of values names to retrieve from service response.
+
+Settings is per backend and it's name is dynamically checked using uppercase
+backend name as prefix::
+
+    <uppercase backend name>_EXTRA_DATA
+
+Example::
+
+    FACEBOOK_EXTRA_DATA = [(..., ...)]
+
+Settings must be a list of tuples mapping value name in response and value
+alias used to store.
 
 -------
 Twitter
index 15e0b1df9e806941a8a5e48e503f4dca10a931f5..ca13e8b13eb819d63f02bbb9bd6198746f95f497 100644 (file)
@@ -50,7 +50,11 @@ AX_SCHEMA_ATTRS = [
     ('http://axschema.org/namePerson/last', 'last_name'),
     ('http://axschema.org/namePerson/friendly', 'nickname'),
 ]
-SREG_ATTR = ['email', 'fullname', 'nickname']
+SREG_ATTR = [
+    ('email', 'email'),
+    ('fullname', 'fullname'),
+    ('nickname', 'nickname')
+]
 OPENID_ID_FIELD = 'openid_identifier'
 SESSION_NAME = 'openid'
 
@@ -221,14 +225,33 @@ class SocialAuthBackend(ModelBackend):
 
 
 class OAuthBackend(SocialAuthBackend):
-    """OAuth authentication backend base class"""
+    """OAuth authentication backend base class.
+
+    EXTRA_DATA defines a set of name that will be stored in
+               extra_data field. It must be a list of tuples with
+               name and alias.
+
+    Also settings will be inspected to get more values names that should be
+    stored on extra_data field. Setting name is created from current backend
+    name (all uppercase) plus _EXTRA_DATA.
+
+    access_token is always stored.
+    """
+    EXTRA_DATA = None
+
     def get_user_id(self, details, response):
         "OAuth providers return an unique user id in response"""
         return response['id']
 
     def extra_data(self, user, uid, response, details):
-        """Return access_token to store in extra_data field"""
-        return response.get('access_token', '')
+        """Return access_token and extra defined names to store in
+        extra_data field"""
+        data = {'access_token': response.get('access_token', '')}
+        name = self.name.replace('-', '_').upper()
+        names = self.EXTRA_DATA or [] + \
+                getattr(settings, name + '_EXTRA_DATA', [])
+        data.update((alias, response.get(name)) for name, alias in names)
+        return data
 
 
 class OpenIDBackend(SocialAuthBackend):
@@ -239,21 +262,41 @@ class OpenIDBackend(SocialAuthBackend):
         """Return user unique id provided by service"""
         return response.identity_url
 
+    def values_from_response(self, response, sreg_names=None, ax_names=None):
+        """Return values from SimpleRegistration response or
+        AttributeExchange response if present.
+
+        @sreg_names and @ax_names must be a list of name and aliases
+        for such name. The alias will be used as mapping key.
+        """
+        values = {}
+
+        # Use Simple Registration attributes if provided
+        if sreg_names:
+            resp = sreg.SRegResponse.fromSuccessResponse(response)
+            if resp:
+                values.update((alias, resp.get(name) or '')
+                                    for name, alias in sreg_names)
+
+        # Use Attribute Exchange attributes if provided
+        if ax_names:
+            resp = ax.FetchResponse.fromSuccessResponse(response)
+            if resp:
+                values.update((alias.replace('old_', ''),
+                               resp.getSingle(src, ''))
+                                for src, alias in ax_names)
+        return values
+
     def get_user_details(self, response):
         """Return user details from an OpenID request"""
         values = {USERNAME: '', 'email': '', 'fullname': '',
                   'first_name': '', 'last_name': ''}
-
-        resp = sreg.SRegResponse.fromSuccessResponse(response)
-        if resp:
-            values.update((name, resp.get(name) or values.get(name) or '')
-                                for name in ('email', 'fullname', 'nickname'))
-
-        # Use Attribute Exchange attributes if provided
-        resp = ax.FetchResponse.fromSuccessResponse(response)
-        if resp:
-            values.update((alias.replace('old_', ''), resp.getSingle(src, ''))
-                            for src, alias in OLD_AX_ATTRS + AX_SCHEMA_ATTRS)
+        # update values using SimpleRegistration or AttributeExchange
+        # values
+        values.update(self.values_from_response(response,
+                                                SREG_ATTR,
+                                                OLD_AX_ATTRS + \
+                                                AX_SCHEMA_ATTRS))
 
         fullname = values.get('fullname') or ''
         first_name = values.get('first_name') or ''
@@ -273,6 +316,23 @@ class OpenIDBackend(SocialAuthBackend):
                                    (first_name.title() + last_name.title())})
         return values
 
+    def extra_data(self, user, uid, response, details):
+        """Return defined extra data names to store in extra_data field.
+        Settings will be inspected to get more values names that should be
+        stored on extra_data field. Setting name is created from current
+        backend name (all uppercase) plus _SREG_EXTRA_DATA and
+        _AX_EXTRA_DATA because values can be returned by SimpleRegistration
+        or AttributeExchange schemas.
+
+        Both list must be a value name and an alias mapping similar to
+        SREG_ATTR, OLD_AX_ATTRS or AX_SCHEMA_ATTRS
+        """
+        name = self.name.replace('-', '_').upper()
+        sreg_names = getattr(settings, name + '_SREG_EXTRA_DATA', None)
+        ax_names = getattr(settings, name + '_AX_EXTRA_DATA', None)
+        data = self.values_from_response(response, ax_names, sreg_names)
+        return data
+
 
 class BaseAuth(object):
     """Base authentication class, new authenticators should subclass
@@ -367,7 +427,7 @@ class OpenIdAuth(BaseAuth):
                 fetch_request.add(ax.AttrInfo(attr, alias=alias,
                                               required=True))
         else:
-            fetch_request = sreg.SRegRequest(optional=SREG_ATTR)
+            fetch_request = sreg.SRegRequest(optional=dict(SREG_ATTR).keys())
         openid_request.addExtension(fetch_request)
 
         return openid_request
index 6ed3752535feb314182719cc9ac7bb3d859fb4cc..a8784e912955f19c7302c1026066f6c26c96c877 100644 (file)
@@ -7,6 +7,9 @@ given by Facebook application registration process.
 
 Extended permissions are supported by defining FACEBOOK_EXTENDED_PERMISSIONS
 setting, it must be a list of values to request.
+
+By default account id and token expiration time are stored in extra_data
+field, check OAuthBackend class for details on how to extend it.
 """
 import cgi
 import urllib
@@ -28,6 +31,8 @@ FACEBOOK_CHECK_AUTH = 'https://%s/me' % FACEBOOK_SERVER
 class FacebookBackend(OAuthBackend):
     """Facebook OAuth authentication backend"""
     name = 'facebook'
+    # Default extra data to store
+    EXTRA_DATA = [('id', 'id'), ('expires', 'expires')]
 
     def get_user_details(self, response):
         """Return user details from Facebook account"""
index a4ca92d355f9dd9e7fe5e7679f70a08d4471e310..aaed2c952530bb540d961fa6bb96db51ff15ff97 100644 (file)
@@ -7,6 +7,9 @@ and TWITTER_CONSUMER_SECRET must be defined with they corresponding
 values.
 
 User screen name is used to generate username.
+
+By default account id is stored in extra_data field, check OAuthBackend
+class for details on how to extend it.
 """
 from django.utils import simplejson
 
@@ -26,6 +29,7 @@ TWITTER_CHECK_AUTH = 'https://twitter.com/account/verify_credentials.json'
 class TwitterBackend(OAuthBackend):
     """Twitter OAuth authentication backend"""
     name = 'twitter'
+    EXTRA_DATA = [('id', 'id')]
 
     def get_user_details(self, response):
         """Return user details from Twitter account"""
index 9ef0e162cfee835d799b942db5a829a6bf6da969..749f0c5f9bdc239f021db0d2367365cdccf409ac 100644 (file)
@@ -4,6 +4,8 @@ import warnings
 from django.db import models
 from django.conf import settings
 
+from social_auth.fields import JSONField
+
 # If User class is overridden, it *must* provide the following fields,
 # or it won't be playing nicely with django.contrib.auth module:
 #
@@ -36,7 +38,7 @@ class UserSocialAuth(models.Model):
     user = models.ForeignKey(User, related_name='social_auth')
     provider = models.CharField(max_length=32)
     uid = models.CharField(max_length=255)
-    extra_data = models.TextField(default='', blank=True)
+    extra_data = JSONField(blank=True)
 
     class Meta:
         """Meta data"""