-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Michael Greenberg
committed
Jul 21, 2013
0 parents
commit 247d57b
Showing
9 changed files
with
307 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
Copyright (c) 2013 Leandigo (www.leandigo.com) | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in | ||
all copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
THE SOFTWARE. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
include LICENSE | ||
include README.rst |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
django-oneall - Django Authentication support for OneAll | ||
======================================================== | ||
|
||
**OneAll** (|oneall|_) provides web-applications with a unified API for **20+ social networks**. | ||
|
||
**django-oneall** is a Django app providing allowing user authentication and management through OneAll | ||
|
||
Disclaimer | ||
---------- | ||
*This package is new, and so far has been tested in a development of a small number of projects.* | ||
*Please be sure to test all edge-cases where this package is used with your application!* | ||
|
||
Requirements | ||
------------ | ||
|
||
#. ``django>=1.4`` (Wasn't tested on earlier versions, but might work) | ||
#. ``pyoneall==0.1`` | ||
|
||
Implementation Overview | ||
----------------------- | ||
OneAll API documentation is available at |onealldoc|_. However, in order to use pyoneall with your application, it's | ||
enough to read the docs for the Connection API: `Connection API Documentation`_. | ||
|
||
For more information on ``pyoneall`` the OneAll API wrapper that this package relies on, check out | ||
`<https://github.com/leandigo/pyoneall>`_. It has more goodies you could use. | ||
|
||
This package provides user authentication capabilities based on ``django.contrib.auth``. It provides the following | ||
components: | ||
|
||
#. Authentication Backend | ||
#. An identity model which stores a cached copy of the user's identity | ||
#. A callback view for the authentication process | ||
|
||
Setup | ||
----- | ||
|
||
Installation | ||
```````````` | ||
Add this app to your project directory, or install via:: | ||
|
||
pip install django_oneall | ||
|
||
|
||
Configuration | ||
````````````` | ||
``settings.py`` | ||
^^^^^^^^^^^^^^^ | ||
|
||
Add ``django_oneall`` to ``INSTALLED_APPS``, make sure you have ``django.contrib.auth`` enabled:: | ||
|
||
INSTALLED_APPS = ( | ||
'django.contrib.auth', | ||
# . . . | ||
'django_oneall', | ||
) | ||
|
||
Add the authentication backend:: | ||
|
||
AUTHENTICATION_BACKENDS = ('django_oneall.auth.OneAllAuthBackend',) | ||
|
||
Configure OneAll connection:: | ||
|
||
ONEALL_SITE_NAME = 'NAME OF YOUR ONEALL SITE' | ||
ONEALL_PRIVATE_KEY = 'PRIVATE KEY OF YOUR SITE' | ||
ONEALL_PUBLIC_KEY = 'PUBLIC KEY OF YOUR SITE' | ||
|
||
Configure behavior (these are good to go as they are here, so you can just copy-paste):: | ||
|
||
# This setting lets you decide which identity data you want stored in the User model. | ||
# The keys stand for the fields in the User model, while the values are the expressions that will be evaluated | ||
# as attributes of the identity object as received from OneAll. There can be more than one identity expression, | ||
# in case different authentication providers have different data structures. | ||
# Note that the usernames will default to the user_token, which is a UUID. You can override it with any other | ||
# unique identity information | ||
ONEALL_CACHE_FIELDS = { | ||
'username': ('user_token',), | ||
'email': ('emails[0].value',), | ||
'first_name': ('name.givenName',), | ||
'last_name': ('name.familyName',), | ||
} | ||
|
||
# User identity is always cached on first authentication. However, if you want to spare an API call for users | ||
# who are already known to your Django app, you can disable the refresh of cache for the second time they | ||
# connect and onward. | ||
ONEALL_REFRESH_CACHE_ON_AUTH = True | ||
|
||
# The OneAll cache table in the DB, where cached identities are stored | ||
ONEALL_CACHE_TABLE = 'oneall_cache' | ||
|
||
``urls.py`` | ||
^^^^^^^^^^^ | ||
Add the following URL pattern to your ``urlpatterns`` tuple:: | ||
|
||
url(r'^oneall/', include('django_oneall.urls')) | ||
|
||
This should enable you to use the callback view at ``/oneall/auth/`` | ||
|
||
Template | ||
^^^^^^^^ | ||
When embedding the body of the OneAll plugin inside your login template, set the ``callback_uri`` attribute to the URL | ||
of the OneAll callback view like so: ``http://example.com{% url oneall_auth %}``. The template might look like:: | ||
|
||
<script type="text/javascript"> | ||
oneall.api.plugins.social_login.build("social_login_container", { | ||
'providers' : ['facebook', 'google', 'linkedin', 'twitter', 'yahoo'], | ||
'css_theme_uri': 'https://oneallcdn.com/css/api/socialize/themes/buildin/connect/large-v1.css', | ||
'grid_size_x': '1', | ||
'callback_uri': 'http://example.com{% url oneall_auth %}' | ||
}); | ||
</script> | ||
|
||
Notes and Stuff | ||
--------------- | ||
After configuring, ``python manage.py syncdb`` is **required**. | ||
|
||
Now users can authenticate and attain user privileges using their social accounts, without the need for you app to | ||
handle the registration. | ||
|
||
Inside your views or any other Python code, you can access the user's identity information like this:: | ||
|
||
user = User.objects.get(username='<user_token>') | ||
identity = user.identity | ||
|
||
``identity`` is an instance of the ``OneAllUserIdentity`` model, which allows you access to cached user identity | ||
information, provided by the social network the user used to authenticate. The data provided varies between different | ||
social networks. ``vars(identity)`` will show you the user's information. | ||
|
||
You can create your own authentication views. ``django.contrib.auth.authenticate`` and ``django.contrib.auth.login`` | ||
will work seamlessly with OneAll if you've added ``django_oneall.auth.OneAllAuthBackend`` to your | ||
``AUTHENTICATION_BACKENDS``. You can find docs on that at `Connection API Documentation`_, or take a look at the very | ||
simple code in ``views.py`` provided in this package. | ||
|
||
License | ||
------- | ||
Copyright (c) 2013, Leandigo (|leandigo|_) | ||
|
||
Released under the MIT License. See the LICENSE file for details. | ||
|
||
.. |oneall| replace:: http://www.oneall.com | ||
.. _oneall: http://www.oneall.com | ||
.. |onealldoc| replace:: http://docs.oneall.com | ||
.. _onealldoc: http://docs.oneall.com | ||
.. _Connection API Documentation: http://docs.oneall.com/api/resources/connections/ | ||
.. |leandigo| replace:: www.leandigo.com | ||
.. _leandigo: http://www.leandigo.com |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import auth | ||
import views | ||
import models | ||
|
||
__all__ = [auth, views, models] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
from pyoneall import OneAll | ||
from django.contrib.auth.models import User | ||
from django.conf import settings | ||
from django.db.models import get_model | ||
|
||
OneAllUserIdentity = get_model('django_oneall', 'OneAllUserIdentity') | ||
|
||
# The worker to be used for authentication | ||
oneall = OneAll(settings.ONEALL_SITE_NAME, settings.ONEALL_PUBLIC_KEY, settings.ONEALL_PRIVATE_KEY) | ||
|
||
class OneAllAuthBackend(object): | ||
""" | ||
OneAll Authentication Backend. | ||
""" | ||
def authenticate(self, token): | ||
""" | ||
Performs authentication using a connection token. Creates and updates User and OneAllUserIdentity | ||
if necessary. | ||
:param str token: OneAll connection token | ||
""" | ||
oa_user = oneall.connection(token).user | ||
|
||
# Check if user exists and create one if not | ||
identity, created = OneAllUserIdentity.objects.get_or_create( | ||
user_token=oa_user.user_token, | ||
raw=unicode(oa_user.identity) | ||
) | ||
|
||
# Update cache for new users and for existing users if setting is provided | ||
if created: | ||
identity.update_user_cache() | ||
elif getattr(settings, 'ONEALL_REFRESH_CACHE_ON_AUTH', True): | ||
identity.refresh(raw=oa_user.identity) | ||
identity.update_user_cache() | ||
|
||
# Return authenticated user | ||
return identity.user | ||
|
||
def get_user(self, user_id): | ||
""" | ||
Retrieve user by user ID | ||
:param user_id: User ID | ||
""" | ||
try: | ||
return User.objects.get(pk=user_id) | ||
except User.DoesNotExist: | ||
return None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
from django.contrib.auth.models import User | ||
from django.db import models | ||
from django.conf import settings | ||
from pyoneall.base import OADict | ||
|
||
class OneAllUserIdentity(models.Model): | ||
""" | ||
OneAll User Identity Model | ||
Caches raw JSON corresponding with user's social identity allow instant retrieval of user details. | ||
""" | ||
user_token = models.CharField(max_length=36, primary_key=True) | ||
raw = models.CharField(max_length=8192, default='{}') | ||
user = models.OneToOneField(User, related_name="identity", null=True) | ||
|
||
def __init__(self, *args, **kwargs): | ||
""" | ||
Upon creation, creates attributes to correspond with the cached data in `raw` | ||
""" | ||
super(self.__class__, self).__init__(*args, **kwargs) | ||
self.__dict__.update(OADict(**eval(self.raw))) | ||
|
||
def refresh(self, raw=None): | ||
""" | ||
Refresh identity cache from OneAll | ||
""" | ||
if not raw: | ||
from auth import oneall | ||
raw = oneall.user(self.user.username).identities.identity[0] | ||
raw.pop('id', None) | ||
raw.pop('user', None) | ||
self.raw = unicode(raw) | ||
self.__dict__.update(OADict(**eval(self.raw))) | ||
self.save() | ||
|
||
def update_user_cache(self): | ||
""" | ||
Update selected fields in the User model from social identity | ||
""" | ||
user = self.user if self.user else User() | ||
for field, values in getattr(settings, 'ONEALL_CACHE_FIELDS', {}).iteritems(): | ||
for value in values: | ||
try: | ||
setattr(user, field, eval('self.%s' % value)) | ||
print eval('self.%s' % value) | ||
except Exception as e: | ||
print e | ||
user.save() | ||
if not self.user: | ||
self.user = user | ||
self.save() | ||
|
||
class Meta: | ||
db_table = getattr(settings, 'ONEALL_CACHE_TABLE', 'oneall_cache') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from django.conf.urls import patterns, url | ||
|
||
urlpatterns = patterns('', | ||
url(r'^auth/$', 'django_oneall.views.oneall_auth', name='oneall_auth'), | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
from django.views.decorators.csrf import csrf_exempt | ||
from django.contrib.auth import authenticate, login | ||
from django.conf import settings | ||
from django.http import HttpResponseRedirect | ||
|
||
@csrf_exempt | ||
def oneall_auth(request): | ||
""" | ||
Callback view for OneAll Authentication | ||
:returns HttpResponseRedirect: A redirection to the LOGIN_REDIRECT_URL | ||
""" | ||
connection_token=request.POST['connection_token'] | ||
user = authenticate(token=connection_token) | ||
login(request, user) | ||
return HttpResponseRedirect(settings.LOGIN_REDIRECT_URL) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import os | ||
from setuptools import setup | ||
README = open(os.path.join(os.path.dirname(__file__), 'README.rst')).read() | ||
setup(name='django-oneall', | ||
version='0.1.3', | ||
packages=['django_oneall'], | ||
requires=['Django (>=1.4)',], | ||
install_requires=['pyoneall == 0.1'], | ||
license='MIT License, see LICENSE file', | ||
description='Django Authentication support for OneAll. Provides unified authentication for 20+ social networks', | ||
long_description=README, | ||
url='http://www.leandigo.com/', | ||
author='Michael Greenberg / Leandigo', | ||
author_email='[email protected]' | ||
) |