Commit 086410f9 authored by root's avatar root

use pip for python-social-auth and its requirements

parent 064c5aa0
"""
python-social-auth application, allows OpenId or OAuth user
registration/authentication just adding a few configurations.
"""
version = (0, 2, 1)
extra = ''
__version__ = '.'.join(map(str, version)) + extra
from social.p3 import quote
from social.utils import sanitize_redirect, user_is_authenticated, \
user_is_active, partial_pipeline_data, setting_url
def do_auth(backend, redirect_name='next'):
# Save any defined next value into session
data = backend.strategy.request_data(merge=False)
# Save extra data into session.
for field_name in backend.setting('FIELDS_STORED_IN_SESSION', []):
if field_name in data:
backend.strategy.session_set(field_name, data[field_name])
if redirect_name in data:
# Check and sanitize a user-defined GET/POST next field value
redirect_uri = data[redirect_name]
if backend.setting('SANITIZE_REDIRECTS', True):
redirect_uri = sanitize_redirect(backend.strategy.request_host(),
redirect_uri)
backend.strategy.session_set(
redirect_name,
redirect_uri or backend.setting('LOGIN_REDIRECT_URL')
)
return backend.start()
def do_complete(backend, login, user=None, redirect_name='next',
*args, **kwargs):
# pop redirect value before the session is trashed on login()
data = backend.strategy.request_data()
redirect_value = backend.strategy.session_get(redirect_name, '') or \
data.get(redirect_name, '')
is_authenticated = user_is_authenticated(user)
user = is_authenticated and user or None
partial = partial_pipeline_data(backend, user, *args, **kwargs)
if partial:
xargs, xkwargs = partial
user = backend.continue_pipeline(*xargs, **xkwargs)
else:
user = backend.complete(user=user, *args, **kwargs)
user_model = backend.strategy.storage.user.user_model()
if user and not isinstance(user, user_model):
return user
if is_authenticated:
if not user:
url = setting_url(backend, redirect_value, 'LOGIN_REDIRECT_URL')
else:
url = setting_url(backend, redirect_value,
'NEW_ASSOCIATION_REDIRECT_URL',
'LOGIN_REDIRECT_URL')
elif user:
if user_is_active(user):
# catch is_new/social_user in case login() resets the instance
is_new = getattr(user, 'is_new', False)
social_user = user.social_user
login(backend, user, social_user)
# store last login backend name in session
backend.strategy.session_set('social_auth_last_login_backend',
social_user.provider)
if is_new:
url = setting_url(backend,
'NEW_USER_REDIRECT_URL',
redirect_value,
'LOGIN_REDIRECT_URL')
else:
url = setting_url(backend, redirect_value,
'LOGIN_REDIRECT_URL')
else:
url = setting_url(backend, 'INACTIVE_USER_URL', 'LOGIN_ERROR_URL',
'LOGIN_URL')
else:
url = setting_url(backend, 'LOGIN_ERROR_URL', 'LOGIN_URL')
if redirect_value and redirect_value != url:
redirect_value = quote(redirect_value)
url += ('?' in url and '&' or '?') + \
'{0}={1}'.format(redirect_name, redirect_value)
if backend.setting('SANITIZE_REDIRECTS', True):
url = sanitize_redirect(backend.strategy.request_host(), url) or \
backend.setting('LOGIN_REDIRECT_URL')
return backend.strategy.redirect(url)
def do_disconnect(backend, user, association_id=None, redirect_name='next',
*args, **kwargs):
partial = partial_pipeline_data(backend, user, *args, **kwargs)
if partial:
xargs, xkwargs = partial
if association_id and not xkwargs.get('association_id'):
xkwargs['association_id'] = association_id
response = backend.disconnect(*xargs, **xkwargs)
else:
response = backend.disconnect(user=user, association_id=association_id,
*args, **kwargs)
if isinstance(response, dict):
response = backend.strategy.redirect(
backend.strategy.request_data().get(redirect_name, '') or
backend.setting('DISCONNECT_REDIRECT_URL') or
backend.setting('LOGIN_REDIRECT_URL')
)
return response
"""Flask SQLAlchemy ORM models for Social Auth"""
import cherrypy
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
from social.utils import setting_name, module_member
from social.storage.sqlalchemy_orm import SQLAlchemyUserMixin, \
SQLAlchemyAssociationMixin, \
SQLAlchemyNonceMixin, \
BaseSQLAlchemyStorage
SocialBase = declarative_base()
DB_SESSION_ATTR = cherrypy.config.get(setting_name('DB_SESSION_ATTR'), 'db')
UID_LENGTH = cherrypy.config.get(setting_name('UID_LENGTH'), 255)
User = module_member(cherrypy.config[setting_name('USER_MODEL')])
class CherryPySocialBase(object):
@classmethod
def _session(cls):
return getattr(cherrypy.request, DB_SESSION_ATTR)
class UserSocialAuth(CherryPySocialBase, SQLAlchemyUserMixin, SocialBase):
"""Social Auth association model"""
uid = Column(String(UID_LENGTH))
user_id = Column(Integer, ForeignKey(User.id),
nullable=False, index=True)
user = relationship(User, backref='social_auth')
@classmethod
def username_max_length(cls):
return User.__table__.columns.get('username').type.length
@classmethod
def user_model(cls):
return User
class Nonce(CherryPySocialBase, SQLAlchemyNonceMixin, SocialBase):
"""One use numbers"""
pass
class Association(CherryPySocialBase, SQLAlchemyAssociationMixin, SocialBase):
"""OpenId account association"""
pass
class CherryPyStorage(BaseSQLAlchemyStorage):
user = UserSocialAuth
nonce = Nonce
association = Association
import warnings
from functools import wraps
import cherrypy
from social.utils import setting_name, module_member
from social.strategies.utils import get_strategy
from social.backends.utils import get_backend, user_backends_data
DEFAULTS = {
'STRATEGY': 'social.strategies.cherrypy_strategy.CherryPyStrategy',
'STORAGE': 'social.apps.cherrypy_app.models.CherryPyStorage'
}
def get_helper(name):
return cherrypy.config.get(setting_name(name), DEFAULTS.get(name, None))
def load_backend(strategy, name, redirect_uri):
backends = get_helper('AUTHENTICATION_BACKENDS')
Backend = get_backend(backends, name)
return Backend(strategy=strategy, redirect_uri=redirect_uri)
def psa(redirect_uri=None):
def decorator(func):
@wraps(func)
def wrapper(self, backend=None, *args, **kwargs):
uri = redirect_uri
if uri and backend and '%(backend)s' in uri:
uri = uri % {'backend': backend}
self.strategy = get_strategy(get_helper('STRATEGY'),
get_helper('STORAGE'))
self.backend = load_backend(self.strategy, backend, uri)
return func(self, backend, *args, **kwargs)
return wrapper
return decorator
def backends(user):
"""Load Social Auth current user data to context under the key 'backends'.
Will return the output of social.backends.utils.user_backends_data."""
return user_backends_data(user, get_helper('AUTHENTICATION_BACKENDS'),
module_member(get_helper('STORAGE')))
def strategy(*args, **kwargs):
warnings.warn('@strategy decorator is deprecated, use @psa instead')
return psa(*args, **kwargs)
import cherrypy
from social.utils import setting_name, module_member
from social.actions import do_auth, do_complete, do_disconnect
from social.apps.cherrypy_app.utils import psa
class CherryPyPSAViews(object):
@cherrypy.expose
@psa('/complete/%(backend)s')
def login(self, backend):
return do_auth(self.backend)
@cherrypy.expose
@psa('/complete/%(backend)s')
def complete(self, backend, *args, **kwargs):
login = cherrypy.config.get(setting_name('LOGIN_METHOD'))
do_login = module_member(login) if login else self.do_login
user = getattr(cherrypy.request, 'user', None)
return do_complete(self.backend, do_login, user=user, *args, **kwargs)
@cherrypy.expose
@psa()
def disconnect(self, backend, association_id=None):
user = getattr(cherrypy.request, 'user', None)
return do_disconnect(self.backend, user, association_id)
def do_login(self, backend, user, social_user):
backend.strategy.session_set('user_id', user.id)
"""
Django framework support.
To use this:
* Add 'social.apps.django_app.default' if using default ORM,
or 'social.apps.django_app.me' if using mongoengine
* Add url('', include('social.apps.django_app.urls', namespace='social')) to
urls.py
* Define SOCIAL_AUTH_STORAGE and SOCIAL_AUTH_STRATEGY, default values:
SOCIAL_AUTH_STRATEGY = 'social.strategies.django_strategy.DjangoStrategy'
SOCIAL_AUTH_STORAGE = 'social.apps.django_app.default.models.DjangoStorage'
"""
from social.strategies.utils import set_current_strategy_getter
from social.apps.django_app.utils import load_strategy
# Set strategy loader method to workaround current strategy getter needed on
# get_user() method on authentication backends when working with Django
set_current_strategy_getter(load_strategy)
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.utils.functional import SimpleLazyObject
try:
from django.utils.functional import empty as _empty
empty = _empty
except ImportError: # django < 1.4
empty = None
from social.backends.utils import user_backends_data
from social.apps.django_app.utils import Storage, BACKENDS
class LazyDict(SimpleLazyObject):
"""Lazy dict initialization."""
def __getitem__(self, name):
if self._wrapped is empty:
self._setup()
return self._wrapped[name]
def __setitem__(self, name, value):
if self._wrapped is empty:
self._setup()
self._wrapped[name] = value
def backends(request):
"""Load Social Auth current user data to context under the key 'backends'.
Will return the output of social.backends.utils.user_backends_data."""
return {'backends': LazyDict(lambda: user_backends_data(request.user,
BACKENDS,
Storage))}
def login_redirect(request):
"""Load current redirect to context."""
value = request.method == 'POST' and \
request.POST.get(REDIRECT_FIELD_NAME) or \
request.GET.get(REDIRECT_FIELD_NAME)
querystring = value and (REDIRECT_FIELD_NAME + '=' + value) or ''
return {
'REDIRECT_FIELD_NAME': REDIRECT_FIELD_NAME,
'REDIRECT_FIELD_VALUE': value,
'REDIRECT_QUERYSTRING': querystring
}
"""
Django default ORM backend support.
To enable this app:
* Add 'social.apps.django_app.default' to INSTALLED_APPS
* In urls.py include url('', include('social.apps.django_app.urls'))
"""
"""Admin settings"""
from django.conf import settings
from django.contrib import admin
from social.utils import setting_name
from social.apps.django_app.default.models import UserSocialAuth, Nonce, \
Association
class UserSocialAuthOption(admin.ModelAdmin):
"""Social Auth user options"""
list_display = ('id', 'user', 'provider', 'uid')
list_filter = ('provider',)
raw_id_fields = ('user',)
list_select_related = True
def get_search_fields(self, request=None):
search_fields = getattr(
settings, setting_name('ADMIN_USER_SEARCH_FIELDS'), None
)
if search_fields is None:
_User = UserSocialAuth.user_model()
username = getattr(_User, 'USERNAME_FIELD', None) or \
hasattr(_User, 'username') and 'username' or \
None
fieldnames = ('first_name', 'last_name', 'email', username)
all_names = _User._meta.get_all_field_names()
search_fields = [name for name in fieldnames
if name and name in all_names]
return ['user__' + name for name in search_fields]
class NonceOption(admin.ModelAdmin):
"""Nonce options"""
list_display = ('id', 'server_url', 'timestamp', 'salt')
search_fields = ('server_url',)
class AssociationOption(admin.ModelAdmin):
"""Association options"""
list_display = ('id', 'server_url', 'assoc_type')
list_filter = ('assoc_type',)
search_fields = ('server_url',)
admin.site.register(UserSocialAuth, UserSocialAuthOption)
admin.site.register(Nonce, NonceOption)
admin.site.register(Association, AssociationOption)
import json
import six
from django.core.exceptions import ValidationError
from django.db import models
try:
from django.utils.encoding import smart_unicode as smart_text
smart_text # placate pyflakes
except ImportError:
from django.utils.encoding import smart_text
class JSONField(six.with_metaclass(models.SubfieldBase, models.TextField)):
"""Simple JSON field that stores python structures as JSON strings
on database.
"""
def __init__(self, *args, **kwargs):
kwargs.setdefault('default', '{}')
super(JSONField, self).__init__(*args, **kwargs)
def to_python(self, value):
"""
Convert the input JSON value into python structures, raises
django.core.exceptions.ValidationError if the data can't be converted.
"""
if self.blank and not value:
return {}
value = value or '{}'
if isinstance(value, six.binary_type):
value = six.text_type(value, 'utf-8')
if isinstance(value, six.string_types):
try:
# with django 1.6 i have '"{}"' as default value here
if value[0] == value[-1] == '"':
value = value[1:-1]
return json.loads(value)
except Exception as err:
raise ValidationError(str(err))
else:
return value
def validate(self, value, model_instance):
"""Check value is a valid JSON string, raise ValidationError on
error."""
if isinstance(value, six.string_types):
super(JSONField, self).validate(value, model_instance)
try:
json.loads(value)
except Exception as err:
raise ValidationError(str(err))
def get_prep_value(self, value):
"""Convert value to JSON string before save"""
try:
return json.dumps(value)
except Exception as err:
raise ValidationError(str(err))
def value_to_string(self, obj):
"""Return value from object converted to string properly"""
return smart_text(self.get_prep_value(self._get_val_from_obj(obj)))
def value_from_object(self, obj):
"""Return value dumped to string."""
return self.get_prep_value(self._get_val_from_obj(obj))
try:
from south.modelsinspector import add_introspection_rules
add_introspection_rules(
[],
["^social\.apps\.django_app\.default\.fields\.JSONField"]
)
except:
pass
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
from django.conf import settings
import social.storage.django_orm
import social.apps.django_app.default.fields
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Association',
fields=[
('id', models.AutoField(serialize=False, primary_key=True,
auto_created=True, verbose_name='ID')),
('server_url', models.CharField(max_length=255)),
('handle', models.CharField(max_length=255)),
('secret', models.CharField(max_length=255)),
('issued', models.IntegerField()),
('lifetime', models.IntegerField()),
('assoc_type', models.CharField(max_length=64)),
],
options={
'db_table': 'social_auth_association',
},
bases=(
models.Model,
social.storage.django_orm.DjangoAssociationMixin
),
),
migrations.CreateModel(
name='Code',
fields=[
('id', models.AutoField(serialize=False, primary_key=True,
auto_created=True, verbose_name='ID')),
('email', models.EmailField(max_length=75)),
('code', models.CharField(db_index=True, max_length=32)),
('verified', models.BooleanField(default=False)),
],
options={
'db_table': 'social_auth_code',
},
bases=(models.Model, social.storage.django_orm.DjangoCodeMixin),
),
migrations.AlterUniqueTogether(
name='code',
unique_together=set([('email', 'code')]),
),
migrations.CreateModel(
name='Nonce',
fields=[
('id', models.AutoField(serialize=False, primary_key=True,
auto_created=True, verbose_name='ID')),
('server_url', models.CharField(max_length=255)),
('timestamp', models.IntegerField()),
('salt', models.CharField(max_length=65)),
],
options={
'db_table': 'social_auth_nonce',
},
bases=(models.Model, social.storage.django_orm.DjangoNonceMixin),
),
migrations.CreateModel(
name='UserSocialAuth',
fields=[