Skip to content

Commit

Permalink
Initial, v0.1.3
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael Greenberg committed Jul 21, 2013
0 parents commit 247d57b
Show file tree
Hide file tree
Showing 9 changed files with 307 additions and 0 deletions.
20 changes: 20 additions & 0 deletions LICENSE
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.

2 changes: 2 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
include LICENSE
include README.rst
145 changes: 145 additions & 0 deletions README.rst
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
5 changes: 5 additions & 0 deletions django_oneall/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import auth
import views
import models

__all__ = [auth, views, models]
47 changes: 47 additions & 0 deletions django_oneall/auth.py
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
53 changes: 53 additions & 0 deletions django_oneall/models.py
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')
5 changes: 5 additions & 0 deletions django_oneall/urls.py
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'),
)
15 changes: 15 additions & 0 deletions django_oneall/views.py
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)
15 changes: 15 additions & 0 deletions setup.py
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]'
)

0 comments on commit 247d57b

Please sign in to comment.