Skip to content

Commit

Permalink
Store extra data in JSON format, also add method to extend extra valu…
Browse files Browse the repository at this point in the history
…es to store. Closes omabgh-30
  • Loading branch information
omab committed Feb 23, 2011
1 parent bf11946 commit fbc8dbb
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 19 deletions.
39 changes: 37 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
92 changes: 76 additions & 16 deletions social_auth/backends/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,11 @@
('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'

Expand Down Expand Up @@ -221,14 +225,33 @@ def get_user(self, user_id):


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):
Expand All @@ -239,21 +262,41 @@ def get_user_id(self, details, response):
"""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 ''
Expand All @@ -273,6 +316,23 @@ def get_user_details(self, response):
(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
Expand Down Expand Up @@ -367,7 +427,7 @@ def setup_request(self):
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
Expand Down
5 changes: 5 additions & 0 deletions social_auth/backends/facebook.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
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
Expand All @@ -28,6 +31,8 @@
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"""
Expand Down
4 changes: 4 additions & 0 deletions social_auth/backends/twitter.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
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

Expand All @@ -26,6 +29,7 @@
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"""
Expand Down
4 changes: 3 additions & 1 deletion social_auth/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
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:
#
Expand Down Expand Up @@ -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"""
Expand Down

0 comments on commit fbc8dbb

Please sign in to comment.