pax_global_header00006660000000000000000000000064133663400100014506gustar00rootroot0000000000000052 comment=ce15088d2bf04a87eb60a46eb6cbb882060ab45c social-app-django-3.1.0/000077500000000000000000000000001336634001000147775ustar00rootroot00000000000000social-app-django-3.1.0/.gitignore000066400000000000000000000006551336634001000167750ustar00rootroot00000000000000*.py[cod] # C extensions *.so # Packages *.egg *.egg-info dist build eggs parts bin var sdist develop-eggs .installed.cfg lib lib64 # Installer logs pip-log.txt # Unit test / coverage reports .coverage .tox nosetests.xml # Translations *.mo # Mr Developer .mr.developer.cfg .project .pydevproject # PyCharm .idea/ test.db local_settings.py sessions/ _build/ fabfile.py changelog.sh .DS_Store .\#* \#*\# .python-version social-app-django-3.1.0/.landscape.yaml000066400000000000000000000001311336634001000176660ustar00rootroot00000000000000doc-warnings: no test-warnings: no strictness: medium max-line-length: 80 autodetect: no social-app-django-3.1.0/.travis.yml000066400000000000000000000015031336634001000171070ustar00rootroot00000000000000language: python python: - "3.5" env: - TOX_ENV=py35-django-18 - TOX_ENV=py34-django-18 - TOX_ENV=py27-django-18 - TOX_ENV=py35-django-19 - TOX_ENV=py34-django-19 - TOX_ENV=py27-django-19 - TOX_ENV=py35-django-110 - TOX_ENV=py34-django-110 - TOX_ENV=py27-django-110 - TOX_ENV=py35-django-111 - TOX_ENV=py34-django-111 - TOX_ENV=py27-django-111 - TOX_ENV=py35-django-master - TOX_ENV=py35-django-20 matrix: fast_finish: true include: - python: 3.6 env: TOX_ENV=py36-django-111 - python: 3.6 env: TOX_ENV=py36-django-master - python: 3.6 env: TOX_ENV=py36-django-20 allow_failures: - env: TOX_ENV=py35-django-master - env: TOX_ENV=py36-django-master install: pip install -r dev-requirements.txt script: tox -e $TOX_ENV after_success: - codecov -e TOX_ENV social-app-django-3.1.0/CHANGELOG.md000066400000000000000000000106461336634001000166170ustar00rootroot00000000000000# Change Log All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). ## [3.1.0](https://github.com/python-social-auth/social-app-django/releases/tag/3.1.0) - 2018-10-31 ### Changed - Updated `JSONField.from_db_value` signature to support multiple Django versions by accepting just the needed parameters. ## [3.0.0](https://github.com/python-social-auth/social-app-django/releases/tag/3.0.0) - 2018-10-28 ### Changed - Reduce log level of exceptions to `INFO` if messages app is installed - Encode association secret with `encodebytes` if available - Decode association secret for proper storage - Remove obsolete code from JSONField - Pass `user` as keyword argument to `do_complete` - Cleanup `username` when using email as username - Drop Python 3.3 support - Correct spelling errors - Correct version that renamed `field.rel` - Reduce error logs in `SocialAuthExceptionMiddleware` ## [2.1.0](https://github.com/python-social-auth/social-app-django/releases/tag/2.1.0) - 2017-12-22 ### Changed - Use Django `urlquote` since it handles unicode - Remove version check in favor of import error catch - Remove call to deprecated method `_get_val_from_obj()` - Drop Python 3.3 support ## [2.0.0](https://github.com/python-social-auth/social-app-django/releases/tag/2.0.0) - 2017-10-28 ### Changed - Better default when checking if the middleware should raise the exception - Update `JSONField` default value to `dict` callable - Updated `authenticate()` parameters cleanup to avoid double arguments errors - Fix imports to bring Django 2.0 support - Admin friendly label - Old Django versions (1.8 and below) compatibility dropped - Python 3.6 and Django 2.0 tests - Management command to clean stale data (partial sessions and codes) ### Added - Added `JSONField` support PostgreSQL builtin option if configured - Added strategy / models / views tests - Added timestamps to Partial and Code models ## [1.2.0](https://github.com/python-social-auth/social-app-django/releases/tag/1.2.0) - 2017-05-06 ### Added - Check for a `MAX_SESSION_LENGTH` setting when logging in and setting session expiry. ### Changed - Addded `on_cascade` clauses to migrations. - Restrict association URL to just integer ids ## [1.1.0](https://github.com/python-social-auth/social-app-django/releases/tag/1.1.0) - 2017-02-10 ### Added - Authenticate cleanup method override to discard request parameter getting passed starting from Django 1.11 ## [1.0.1](https://github.com/python-social-auth/social-app-django/releases/tag/1.0.1) - 2017-01-29 ### Changed - Remove migration replacement to nonexistent reference - Ensure atomic transaction if active ## [1.0.0](https://github.com/python-social-auth/social-app-django/releases/tag/1.0.0) - 2017-01-22 ### Added - Partial pipeline DB storage implementation - Explicit app_label definition in model classes ### Changed - Monkey patch BaseAuth to load the current strategy to workaround django load_backend() call - Remove usage of set/get current strategy methods - Remove usage of `social_auth` related name since it should be consider a simple helper. ## [0.1.0](https://github.com/python-social-auth/social-app-django/releases/tag/0.1.0) - 2016-12-28 ### Added - Let Django resolve URL when getting from settings (port of [#905](https://github.com/omab/python-social-auth/pull/905) by webjunkie) - Add setting to fine-tune admin search fields (port of [#1035](https://github.com/omab/python-social-auth/pull/1035) by atugushev) ### Changed - Fixed `REDIRECT_URL_VALUE` value to be quoted by default. Refs [#875](https://github.com/omab/python-social-auth/issues/875) - Django strategy should respect X-Forwarded-Port (port of [#841](https://github.com/omab/python-social-auth/pull/841) by omarkhan) - Fixed use of old private API (port of [#822](https://github.com/omab/python-social-auth/pull/822) by eranmarom) - Add ON DELETE CASCADE for user fk (port of [#1015](https://github.com/omab/python-social-auth/pull/1015) by artofhuman) - Avoid usage of SubfieldBase on 1.8 and 1.9 versions (port of [#1008](https://github.com/omab/python-social-auth/pull/1008) by tom-dalton-fanduel) ## [0.0.1](https://github.com/python-social-auth/social-app-django/releases/tag/0.0.1) - 2016-11-27 ### Changed - Split from the monolitic [python-social-auth](https://github.com/omab/python-social-auth) codebase social-app-django-3.1.0/CONTRIBUTING.md000066400000000000000000000047741336634001000172440ustar00rootroot00000000000000# How to contribute I like to encourage you to contribute to the repository. This should be as easy as possible for you but there are a few things to consider when contributing. The following guidelines for contribution should be followed if you want to submit a pull request. ## How to prepare * You need a [GitHub account](https://github.com/signup/free) * Submit an [issue ticket](https://github.com/python-social-auth/social-app-django/issues) for your issue if there is no one yet. * Describe the issue and include steps to reproduce if it's a bug. * Ensure to mention the earliest version that you know is affected. * If you are able and want to fix this, fork the repository on GitHub ## Make Changes * In your forked repository, create a topic branch for your upcoming patch. (e.g. `feature/new-backend` or `bug/auth-fails`) * Usually this is based on the `master` branch. * Create a branch based on master `git branch bug/auth-fails master` then checkout the new branch with `git checkout bug/auth-fails`. Please avoid working directly on the `master` branch. * Make commits of logical units and describe them properly. * Make sure you stick to [PEP8](https://www.python.org/dev/peps/pep-0008/) coding style that is used already. * If possible, submit tests to your patch / new feature so it can be tested easily. * Assure nothing is broken by running all the tests. * Add a meaningful entry to the `CHANGELOG.md` document. ## Submit Changes * Push your changes to a topic branch in your fork of the repository. * Open a pull request to the original repository and choose the right original branch you want to patch. * If not done in commit messages (which you really should do) please reference and update your issue with the code changes. But _please do not close the issue yourself_. * Even if you have write access to the repository, do not directly push or merge pull-requests. Let another team member review your pull request and approve. # Additional Resources * [General GitHub documentation](http://help.github.com/) * [GitHub pull request documentation](http://help.github.com/send-pull-requests/) * [Read the Issue Guidelines by @necolas](https://github.com/necolas/issue-guidelines/blob/master/CONTRIBUTING.md) for more details # Notes This documented is based in the work from [anselmh/CONTRIBUTING.md](https://github.com/anselmh/CONTRIBUTING.md), licensed as [Creative Commons Attribution 3.0 Unported License](https://github.com/anselmh/CONTRIBUTING.md/blob/master/README.md#license). social-app-django-3.1.0/Dockerfile000066400000000000000000000002361336634001000167720ustar00rootroot00000000000000FROM omab/pyenv-tox MAINTAINER Matías Aguirre RUN apt-get update RUN apt-get install -y make libxml2-dev libxmlsec1-dev pkg-config social-app-django-3.1.0/LICENSE000066400000000000000000000027711336634001000160130ustar00rootroot00000000000000Copyright (c) 2012-2016, Matías Aguirre All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of this project nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. social-app-django-3.1.0/MANIFEST.in000066400000000000000000000001471336634001000165370ustar00rootroot00000000000000global-include *.py include *.txt CHANGELOG.md LICENSE README.md recursive-exclude social_django *.pyc social-app-django-3.1.0/Makefile000066400000000000000000000013371336634001000164430ustar00rootroot00000000000000build: @ python setup.py sdist @ python setup.py bdist_wheel --python-tag py2 @ BUILD_VERSION=3 python setup.py bdist_wheel --python-tag py3 publish: @ python setup.py sdist upload @ python setup.py bdist_wheel --python-tag py2 upload @ BUILD_VERSION=3 python setup.py bdist_wheel --python-tag py3 upload clean: @ find . -name '*.py[co]' -delete @ find . -name '__pycache__' -delete @ rm -rf *.egg-info dist build docker-tox-build: @ docker inspect omab/psa-social-app-django >/dev/null 2>&1 || ( \ docker build -t omab/psa-social-app-django . \ ) docker-tox: docker-tox-build @ docker run -it --rm \ --name psa-social-app-django-test \ -v "`pwd`:/code" \ -w /code omab/psa-social-app-django tox social-app-django-3.1.0/README.md000066400000000000000000000041461336634001000162630ustar00rootroot00000000000000# Python Social Auth - Django [![Build Status](https://travis-ci.org/python-social-auth/social-app-django.svg?branch=master)](https://travis-ci.org/python-social-auth/social-app-django) [![Donate](https://img.shields.io/badge/Donate-PayPal-orange.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=matiasaguirre%40gmail%2ecom&lc=US&item_name=Python%20Social%20Auth&no_note=0¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHostedGuest) [![PyPI version](https://badge.fury.io/py/social-auth-app-django.svg)](https://badge.fury.io/py/social-auth-app-django) Python Social Auth is an easy to setup social authentication/registration mechanism with support for several frameworks and auth providers. ## Description This is the [Django](https://www.djangoproject.com/) component of the [python-social-auth ecosystem](https://github.com/python-social-auth/social-core), it implements the needed functionality to integrate [social-auth-core](https://github.com/python-social-auth/social-core) in a Django based project. ## Django version This project will focus on the currently supported Django releases as stated on the [Django Project Supported Versions table](https://www.djangoproject.com/download/#supported-versions). Backward compatibility with unsupported versions won't be enforced. ## Documentation Project documentation is available at http://python-social-auth.readthedocs.org/. ## Setup ```shell $ pip install social-auth-app-django ``` ## Contributing See the [CONTRIBUTING.md](CONTRIBUTING.md) document for details. ## Versioning This project follows [Semantic Versioning 2.0.0](http://semver.org/spec/v2.0.0.html). ## License This project follows the BSD license. See the [LICENSE](LICENSE) for details. ## Donations This project is maintained on my spare time, consider donating to keep it improving. [![Donate](https://img.shields.io/badge/Donate-PayPal-orange.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=matiasaguirre%40gmail%2ecom&lc=US&item_name=Python%20Social%20Auth&no_note=0¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHostedGuest) social-app-django-3.1.0/dev-requirements.txt000066400000000000000000000000721336634001000210360ustar00rootroot00000000000000mock==2.0.0 codecov==2.0.7 tox==2.7.0 -r requirements.txt social-app-django-3.1.0/manage.py000066400000000000000000000005111336634001000165760ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- from __future__ import unicode_literals, absolute_import import os import sys if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tests.settings") from django.core.management import execute_from_command_line execute_from_command_line(sys.argv) social-app-django-3.1.0/requirements.txt000066400000000000000000000000361336634001000202620ustar00rootroot00000000000000six social-auth-core >= 1.2.0 social-app-django-3.1.0/setup.cfg000066400000000000000000000003361336634001000166220ustar00rootroot00000000000000[flake8] max-line-length = 119 # Ignore some well known paths exclude = .venv,.tox,dist,doc,build,*.egg,db/env.py,db/versions/*.py,site [nosetests] verbosity=2 with-coverage=1 cover-erase=1 cover-package=social rednose=1 social-app-django-3.1.0/setup.py000066400000000000000000000031041336634001000165070ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Setup file for easy installation""" import re from os.path import join, dirname from setuptools import setup VERSION_RE = re.compile('__version__ = \'([\d\.]+)\'') def read_version(): with open('social_django/__init__.py') as file: version_line = [line for line in file.readlines() if line.startswith('__version__')][0] return VERSION_RE.match(version_line).groups()[0] def long_description(): return open(join(dirname(__file__), 'README.md')).read() def load_requirements(): return open(join(dirname(__file__), 'requirements.txt')).readlines() setup( name='social-auth-app-django', version=read_version(), author='Matias Aguirre', author_email='matiasaguirre@gmail.com', description='Python Social Authentication, Django integration.', license='BSD', keywords='django, social auth', url='https://github.com/python-social-auth/social-app-django', packages=[ 'social_django', 'social_django.migrations', 'social_django.management', 'social_django.management.commands', ], long_description=long_description(), install_requires=load_requirements(), classifiers=[ 'Development Status :: 4 - Beta', 'Topic :: Internet', 'License :: OSI Approved :: BSD License', 'Intended Audience :: Developers', 'Environment :: Web Environment', 'Programming Language :: Python', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3' ], zip_safe=False ) social-app-django-3.1.0/social_django/000077500000000000000000000000001336634001000175735ustar00rootroot00000000000000social-app-django-3.1.0/social_django/__init__.py000066400000000000000000000014411336634001000217040ustar00rootroot00000000000000__version__ = '3.1.0' from social_core.backends.base import BaseAuth # django.contrib.auth.load_backend() will import and instanciate the # authentication backend ignoring the possibility that it might # require more arguments. Here we set a monkey patch to # BaseAuth.__init__ to ignore the mandatory strategy argument and load # it. def baseauth_init_workaround(original_init): def fake_init(self, strategy=None, *args, **kwargs): from .utils import load_strategy original_init(self, strategy or load_strategy(), *args, **kwargs) return fake_init if not getattr(BaseAuth, '__init_patched', False): BaseAuth.__init__ = baseauth_init_workaround(BaseAuth.__init__) BaseAuth.__init_patched = True default_app_config = 'social_django.config.PythonSocialAuthConfig' social-app-django-3.1.0/social_django/admin.py000066400000000000000000000042671336634001000212460ustar00rootroot00000000000000"""Admin settings""" from itertools import chain from django.conf import settings from django.contrib import admin from social_core.utils import setting_name from .models import UserSocialAuth, Nonce, Association class UserSocialAuthOption(admin.ModelAdmin): """Social Auth user options""" list_display = ('user', 'id', '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 = self._get_all_field_names(_User._meta) search_fields = [name for name in fieldnames if name and name in all_names] return ['user__' + name for name in search_fields] + \ getattr(settings, setting_name('ADMIN_SEARCH_FIELDS'), []) @staticmethod def _get_all_field_names(model): names = chain.from_iterable( (field.name, field.attname) if hasattr(field, 'attname') else (field.name,) for field in model.get_fields() # For complete backwards compatibility, you may want to exclude # GenericForeignKey from the results. if not (field.many_to_one and field.related_model is None) ) return list(set(names)) 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) social-app-django-3.1.0/social_django/compat.py000066400000000000000000000014631336634001000214340ustar00rootroot00000000000000# coding=utf-8 import six import django from django.db import models try: from django.urls import reverse except ImportError: from django.core.urlresolvers import reverse try: from django.utils.deprecation import MiddlewareMixin except ImportError: MiddlewareMixin = object def get_rel_model(field): if django.VERSION >= (1, 9): return field.remote_field.model user_model = field.rel.to if isinstance(user_model, six.string_types): app_label, model_name = user_model.split('.') user_model = models.get_model(app_label, model_name) return user_model def get_request_port(request): if django.VERSION >= (1, 9): return request.get_port() host_parts = request.get_host().partition(':') return host_parts[2] or request.META['SERVER_PORT'] social-app-django-3.1.0/social_django/config.py000066400000000000000000000005651336634001000214200ustar00rootroot00000000000000from django.apps import AppConfig class PythonSocialAuthConfig(AppConfig): # Full Python path to the application eg. 'django.contrib.admin'. name = 'social_django' # Last component of the Python path to the application eg. 'admin'. label = 'social_django' # Human-readable name for the application eg. "Admin". verbose_name = 'Python Social Auth' social-app-django-3.1.0/social_django/context_processors.py000066400000000000000000000031431336634001000241140ustar00rootroot00000000000000from django.contrib.auth import REDIRECT_FIELD_NAME from django.utils.functional import SimpleLazyObject from django.utils.http import urlquote try: from django.utils.functional import empty as _empty empty = _empty except ImportError: # django < 1.4 empty = None from social_core.backends.utils import user_backends_data from .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_core.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) if value: value = urlquote(value) querystring = REDIRECT_FIELD_NAME + '=' + value else: querystring = '' return { 'REDIRECT_FIELD_NAME': REDIRECT_FIELD_NAME, 'REDIRECT_FIELD_VALUE': value, 'REDIRECT_QUERYSTRING': querystring } social-app-django-3.1.0/social_django/fields.py000066400000000000000000000044631336634001000214220ustar00rootroot00000000000000import json import six from django.core.exceptions import ValidationError from django.conf import settings from django.db import models from django.utils.encoding import force_text from social_core.utils import setting_name if getattr(settings, setting_name('POSTGRES_JSONFIELD'), False): from django.contrib.postgres.fields import JSONField as JSONFieldBase else: JSONFieldBase = models.TextField class JSONField(JSONFieldBase): """Simple JSON field that stores python structures as JSON strings on database. """ def __init__(self, *args, **kwargs): kwargs.setdefault('default', dict) super(JSONField, self).__init__(*args, **kwargs) def from_db_value(self, value, *args, **kwargs): return self.to_python(value) 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: 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 force_text(self.value_from_object(obj)) def value_from_object(self, obj): """Return value dumped to string.""" orig_val = super(JSONField, self).value_from_object(obj) return self.get_prep_value(orig_val) social-app-django-3.1.0/social_django/management/000077500000000000000000000000001336634001000217075ustar00rootroot00000000000000social-app-django-3.1.0/social_django/management/__init__.py000066400000000000000000000000001336634001000240060ustar00rootroot00000000000000social-app-django-3.1.0/social_django/management/commands/000077500000000000000000000000001336634001000235105ustar00rootroot00000000000000social-app-django-3.1.0/social_django/management/commands/__init__.py000066400000000000000000000000001336634001000256070ustar00rootroot00000000000000social-app-django-3.1.0/social_django/management/commands/clearsocial.py000066400000000000000000000016651336634001000263530ustar00rootroot00000000000000from datetime import timedelta from django.core.management.base import BaseCommand from django.utils import timezone from social_django.models import Code, Partial class Command(BaseCommand): help = 'removes old not used verification codes and partials' def add_arguments(self, parser): super(Command, self).add_arguments(parser) parser.add_argument( '--age', action='store', type=int, dest='age', default=14, help='how long to keep unused data (in days, defaults to 14)' ) def handle(self, *args, **options): age = timezone.now() - timedelta(days=options['age']) # Delete old not verified codes Code.objects.filter( verified=False, timestamp__lt=age ).delete() # Delete old partial data Partial.objects.filter( timestamp__lt=age ).delete() social-app-django-3.1.0/social_django/managers.py000066400000000000000000000006661336634001000217520ustar00rootroot00000000000000from django.db import models class UserSocialAuthManager(models.Manager): """Manager for the UserSocialAuth django model.""" class Meta: app_label = "social_django" def get_social_auth(self, provider, uid): try: return self.select_related('user').get(provider=provider, uid=uid) except self.model.DoesNotExist: return None social-app-django-3.1.0/social_django/middleware.py000066400000000000000000000050371336634001000222670ustar00rootroot00000000000000# -*- coding: utf-8 -*- import six from django.apps import apps from django.conf import settings from django.contrib import messages from django.contrib.messages.api import MessageFailure from django.shortcuts import redirect from django.utils.http import urlquote from social_core.exceptions import SocialAuthBaseException from social_core.utils import social_logger from .compat import MiddlewareMixin class SocialAuthExceptionMiddleware(MiddlewareMixin): """Middleware that handles Social Auth AuthExceptions by providing the user with a message, logging an error, and redirecting to some next location. By default, the exception message itself is sent to the user and they are redirected to the location specified in the SOCIAL_AUTH_LOGIN_ERROR_URL setting. This middleware can be extended by overriding the get_message or get_redirect_uri methods, which each accept request and exception. """ def process_exception(self, request, exception): strategy = getattr(request, 'social_strategy', None) if strategy is None or self.raise_exception(request, exception): return if isinstance(exception, SocialAuthBaseException): backend = getattr(request, 'backend', None) backend_name = getattr(backend, 'name', 'unknown-backend') message = self.get_message(request, exception) url = self.get_redirect_uri(request, exception) if apps.is_installed('django.contrib.messages'): social_logger.info(message) try: messages.error(request, message, extra_tags='social-auth ' + backend_name) except MessageFailure: if url: url += ('?' in url and '&' or '?') + \ 'message={0}&backend={1}'.format(urlquote(message), backend_name) else: social_logger.error(message) if url: return redirect(url) def raise_exception(self, request, exception): strategy = getattr(request, 'social_strategy', None) if strategy is not None: return strategy.setting('RAISE_EXCEPTIONS') or settings.DEBUG def get_message(self, request, exception): return six.text_type(exception) def get_redirect_uri(self, request, exception): strategy = getattr(request, 'social_strategy', None) return strategy.setting('LOGIN_ERROR_URL') social-app-django-3.1.0/social_django/migrations/000077500000000000000000000000001336634001000217475ustar00rootroot00000000000000social-app-django-3.1.0/social_django/migrations/0001_initial.py000066400000000000000000000104211336634001000244100ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models, migrations from django.conf import settings from social_core.utils import setting_name from ..fields import JSONField from ..storage import DjangoAssociationMixin, DjangoCodeMixin, \ DjangoNonceMixin, DjangoUserMixin USER_MODEL = getattr(settings, setting_name('USER_MODEL'), None) or \ getattr(settings, 'AUTH_USER_MODEL', None) or \ 'auth.User' UID_LENGTH = getattr(settings, setting_name('UID_LENGTH'), 255) NONCE_SERVER_URL_LENGTH = getattr( settings, setting_name('NONCE_SERVER_URL_LENGTH'), 255 ) ASSOCIATION_SERVER_URL_LENGTH = getattr( settings, setting_name('ASSOCIATION_SERVER_URL_LENGTH'), 255 ) ASSOCIATION_HANDLE_LENGTH = getattr( settings, setting_name('ASSOCIATION_HANDLE_LENGTH'), 255 ) class Migration(migrations.Migration): replaces = [ ('default', '0001_initial'), ('social_auth', '0001_initial') ] dependencies = [ migrations.swappable_dependency(USER_MODEL), ] operations = [ migrations.CreateModel( name='Association', fields=[ ('id', models.AutoField( verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('server_url', models.CharField(max_length=ASSOCIATION_SERVER_URL_LENGTH)), ('handle', models.CharField(max_length=ASSOCIATION_HANDLE_LENGTH)), ('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, DjangoAssociationMixin ), ), migrations.CreateModel( name='Code', fields=[ ('id', models.AutoField( verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('email', models.EmailField(max_length=75)), ('code', models.CharField(max_length=32, db_index=True)), ('verified', models.BooleanField(default=False)), ], options={ 'db_table': 'social_auth_code', }, bases=(models.Model, DjangoCodeMixin), ), migrations.CreateModel( name='Nonce', fields=[ ('id', models.AutoField( verbose_name='ID', serialize=False, auto_created=True, primary_key=True )), ('server_url', models.CharField(max_length=NONCE_SERVER_URL_LENGTH)), ('timestamp', models.IntegerField()), ('salt', models.CharField(max_length=65)), ], options={ 'db_table': 'social_auth_nonce', }, bases=(models.Model, DjangoNonceMixin), ), migrations.CreateModel( name='UserSocialAuth', fields=[ ('id', models.AutoField( verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('provider', models.CharField(max_length=32)), ('uid', models.CharField(max_length=UID_LENGTH)), ('extra_data', JSONField(default='{}')), ('user', models.ForeignKey( related_name='social_auth', to=USER_MODEL, on_delete=models.CASCADE)), ], options={ 'db_table': 'social_auth_usersocialauth', }, bases=(models.Model, DjangoUserMixin), ), migrations.AlterUniqueTogether( name='usersocialauth', unique_together={('provider', 'uid')}, ), migrations.AlterUniqueTogether( name='code', unique_together={('email', 'code')}, ), migrations.AlterUniqueTogether( name='nonce', unique_together={('server_url', 'timestamp', 'salt')}, ), ] social-app-django-3.1.0/social_django/migrations/0002_add_related_name.py000066400000000000000000000015101336634001000262070ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models, migrations from django.conf import settings from social_core.utils import setting_name USER_MODEL = getattr(settings, setting_name('USER_MODEL'), None) or \ getattr(settings, 'AUTH_USER_MODEL', None) or \ 'auth.User' class Migration(migrations.Migration): replaces = [ ('default', '0002_add_related_name'), ('social_auth', '0002_add_related_name') ] dependencies = [ ('social_django', '0001_initial'), ] operations = [ migrations.AlterField( model_name='usersocialauth', name='user', field=models.ForeignKey( related_name='social_auth', to=USER_MODEL, on_delete=models.CASCADE, ) ), ] social-app-django-3.1.0/social_django/migrations/0003_alter_email_max_length.py000066400000000000000000000012621336634001000274500ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.conf import settings from django.db import models, migrations from social_core.utils import setting_name EMAIL_LENGTH = getattr(settings, setting_name('EMAIL_LENGTH'), 254) class Migration(migrations.Migration): replaces = [ ('default', '0003_alter_email_max_length'), ('social_auth', '0003_alter_email_max_length') ] dependencies = [ ('social_django', '0002_add_related_name'), ] operations = [ migrations.AlterField( model_name='code', name='email', field=models.EmailField(max_length=EMAIL_LENGTH), ), ] social-app-django-3.1.0/social_django/migrations/0004_auto_20160423_0400.py000066400000000000000000000010711336634001000253570ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models from ..fields import JSONField class Migration(migrations.Migration): replaces = [ ('default', '0004_auto_20160423_0400'), ('social_auth', '0004_auto_20160423_0400') ] dependencies = [ ('social_django', '0003_alter_email_max_length'), ] operations = [ migrations.AlterField( model_name='usersocialauth', name='extra_data', field=JSONField(default=dict), ) ] social-app-django-3.1.0/social_django/migrations/0005_auto_20160727_2333.py000066400000000000000000000010011336634001000253670ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Generated by Django 1.9.5 on 2016-07-28 02:33 from __future__ import unicode_literals from django.db import migrations class Migration(migrations.Migration): replaces = [ ('social_auth', '0005_auto_20160727_2333') ] dependencies = [ ('social_django', '0004_auto_20160423_0400'), ] operations = [ migrations.AlterUniqueTogether( name='association', unique_together=set([('server_url', 'handle')]), ), ] social-app-django-3.1.0/social_django/migrations/0006_partial.py000066400000000000000000000020101336634001000244130ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Generated by Django 1.10.4 on 2017-01-02 11:54 from __future__ import unicode_literals from django.db import migrations, models import social_django.fields import social_django.storage class Migration(migrations.Migration): dependencies = [ ('social_django', '0005_auto_20160727_2333'), ] operations = [ migrations.CreateModel( name='Partial', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('token', models.CharField(db_index=True, max_length=32)), ('next_step', models.PositiveSmallIntegerField(default=0)), ('backend', models.CharField(max_length=32)), ('data', social_django.fields.JSONField(default=dict)), ], options={ 'db_table': 'social_auth_partial', }, bases=(models.Model, social_django.storage.DjangoPartialMixin), ), ] social-app-django-3.1.0/social_django/migrations/0007_code_timestamp.py000066400000000000000000000011751336634001000257700ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Generated by Django 1.10.7 on 2017-06-08 06:54 from __future__ import unicode_literals from django.db import migrations, models from django.utils import timezone class Migration(migrations.Migration): dependencies = [ ('social_django', '0006_partial'), ] operations = [ migrations.AddField( model_name='code', name='timestamp', field=models.DateTimeField(auto_now_add=True, db_index=True, default=timezone.now), preserve_default=False ), ] social-app-django-3.1.0/social_django/migrations/0008_partial_timestamp.py000066400000000000000000000012101336634001000265010ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Generated by Django 1.10.7 on 2017-06-08 06:57 from __future__ import unicode_literals from django.db import migrations, models from django.utils import timezone class Migration(migrations.Migration): dependencies = [ ('social_django', '0007_code_timestamp'), ] operations = [ migrations.AddField( model_name='partial', name='timestamp', field=models.DateTimeField(auto_now_add=True, db_index=True, default=timezone.now), preserve_default=False, ), ] social-app-django-3.1.0/social_django/migrations/__init__.py000066400000000000000000000000001336634001000240460ustar00rootroot00000000000000social-app-django-3.1.0/social_django/models.py000066400000000000000000000107121336634001000214310ustar00rootroot00000000000000"""Django ORM models for Social Auth""" import six from django.db import models from django.conf import settings from django.db.utils import IntegrityError from social_core.utils import setting_name from .compat import get_rel_model from .storage import DjangoUserMixin, DjangoAssociationMixin, \ DjangoNonceMixin, DjangoCodeMixin, \ DjangoPartialMixin, BaseDjangoStorage from .fields import JSONField from .managers import UserSocialAuthManager USER_MODEL = getattr(settings, setting_name('USER_MODEL'), None) or \ getattr(settings, 'AUTH_USER_MODEL', None) or \ 'auth.User' UID_LENGTH = getattr(settings, setting_name('UID_LENGTH'), 255) EMAIL_LENGTH = getattr(settings, setting_name('EMAIL_LENGTH'), 254) NONCE_SERVER_URL_LENGTH = getattr( settings, setting_name('NONCE_SERVER_URL_LENGTH'), 255) ASSOCIATION_SERVER_URL_LENGTH = getattr( settings, setting_name('ASSOCIATION_SERVER_URL_LENGTH'), 255) ASSOCIATION_HANDLE_LENGTH = getattr( settings, setting_name('ASSOCIATION_HANDLE_LENGTH'), 255) class AbstractUserSocialAuth(models.Model, DjangoUserMixin): """Abstract Social Auth association model""" user = models.ForeignKey(USER_MODEL, related_name='social_auth', on_delete=models.CASCADE) provider = models.CharField(max_length=32) uid = models.CharField(max_length=UID_LENGTH) extra_data = JSONField() objects = UserSocialAuthManager() def __str__(self): return str(self.user) class Meta: app_label = "social_django" abstract = True @classmethod def get_social_auth(cls, provider, uid): try: return cls.objects.select_related('user').get(provider=provider, uid=uid) except cls.DoesNotExist: return None @classmethod def username_max_length(cls): username_field = cls.username_field() field = cls.user_model()._meta.get_field(username_field) return field.max_length @classmethod def user_model(cls): user_model = get_rel_model(field=cls._meta.get_field('user')) return user_model class UserSocialAuth(AbstractUserSocialAuth): """Social Auth association model""" class Meta: """Meta data""" app_label = "social_django" unique_together = ('provider', 'uid') db_table = 'social_auth_usersocialauth' class Nonce(models.Model, DjangoNonceMixin): """One use numbers""" server_url = models.CharField(max_length=NONCE_SERVER_URL_LENGTH) timestamp = models.IntegerField() salt = models.CharField(max_length=65) class Meta: app_label = "social_django" unique_together = ('server_url', 'timestamp', 'salt') db_table = 'social_auth_nonce' class Association(models.Model, DjangoAssociationMixin): """OpenId account association""" server_url = models.CharField(max_length=ASSOCIATION_SERVER_URL_LENGTH) handle = models.CharField(max_length=ASSOCIATION_HANDLE_LENGTH) secret = models.CharField(max_length=255) # Stored base64 encoded issued = models.IntegerField() lifetime = models.IntegerField() assoc_type = models.CharField(max_length=64) class Meta: app_label = "social_django" db_table = 'social_auth_association' unique_together = ( ('server_url', 'handle',) ) class Code(models.Model, DjangoCodeMixin): email = models.EmailField(max_length=EMAIL_LENGTH) code = models.CharField(max_length=32, db_index=True) verified = models.BooleanField(default=False) timestamp = models.DateTimeField(auto_now_add=True, db_index=True) class Meta: app_label = "social_django" db_table = 'social_auth_code' unique_together = ('email', 'code') class Partial(models.Model, DjangoPartialMixin): token = models.CharField(max_length=32, db_index=True) next_step = models.PositiveSmallIntegerField(default=0) backend = models.CharField(max_length=32) data = JSONField() timestamp = models.DateTimeField(auto_now_add=True, db_index=True) class Meta: app_label = "social_django" db_table = 'social_auth_partial' class DjangoStorage(BaseDjangoStorage): user = UserSocialAuth nonce = Nonce association = Association code = Code partial = Partial @classmethod def is_integrity_error(cls, exception): return exception.__class__ is IntegrityError social-app-django-3.1.0/social_django/storage.py000066400000000000000000000163211336634001000216140ustar00rootroot00000000000000"""Django ORM models for Social Auth""" import base64 import six import sys from django.core.exceptions import FieldDoesNotExist from django.db import transaction from django.db.utils import IntegrityError from social_core.storage import UserMixin, AssociationMixin, NonceMixin, \ CodeMixin, PartialMixin, BaseStorage class DjangoUserMixin(UserMixin): """Social Auth association model""" @classmethod def changed(cls, user): user.save() def set_extra_data(self, extra_data=None): if super(DjangoUserMixin, self).set_extra_data(extra_data): self.save() @classmethod def allowed_to_disconnect(cls, user, backend_name, association_id=None): if association_id is not None: qs = cls.objects.exclude(id=association_id) else: qs = cls.objects.exclude(provider=backend_name) qs = qs.filter(user=user) if hasattr(user, 'has_usable_password'): valid_password = user.has_usable_password() else: valid_password = True return valid_password or qs.count() > 0 @classmethod def disconnect(cls, entry): entry.delete() @classmethod def username_field(cls): return getattr(cls.user_model(), 'USERNAME_FIELD', 'username') @classmethod def user_exists(cls, *args, **kwargs): """ Return True/False if a User instance exists with the given arguments. Arguments are directly passed to filter() manager method. """ if 'username' in kwargs: kwargs[cls.username_field()] = kwargs.pop('username') return cls.user_model().objects.filter(*args, **kwargs).count() > 0 @classmethod def get_username(cls, user): return getattr(user, cls.username_field(), None) @classmethod def create_user(cls, *args, **kwargs): username_field = cls.username_field() if 'username' in kwargs: if username_field not in kwargs: kwargs[username_field] = kwargs.pop('username') else: # If username_field is 'email' and there is no field named "username" # then latest should be removed from kwargs. try: cls.user_model()._meta.get_field('username') except FieldDoesNotExist: kwargs.pop('username') try: if hasattr(transaction, 'atomic'): # In Django versions that have an "atomic" transaction decorator / context # manager, there's a transaction wrapped around this call. # If the create fails below due to an IntegrityError, ensure that the transaction # stays undamaged by wrapping the create in an atomic. with transaction.atomic(): user = cls.user_model().objects.create_user(*args, **kwargs) else: user = cls.user_model().objects.create_user(*args, **kwargs) except IntegrityError: # User might have been created on a different thread, try and find them. # If we don't, re-raise the IntegrityError. exc_info = sys.exc_info() # If email comes in as None it won't get found in the get if kwargs.get('email', True) is None: kwargs['email'] = '' try: user = cls.user_model().objects.get(*args, **kwargs) except cls.user_model().DoesNotExist: six.reraise(*exc_info) return user @classmethod def get_user(cls, pk=None, **kwargs): if pk: kwargs = {'pk': pk} try: return cls.user_model().objects.get(**kwargs) except cls.user_model().DoesNotExist: return None @classmethod def get_users_by_email(cls, email): user_model = cls.user_model() email_field = getattr(user_model, 'EMAIL_FIELD', 'email') return user_model.objects.filter(**{email_field + '__iexact': email}) @classmethod def get_social_auth(cls, provider, uid): if not isinstance(uid, six.string_types): uid = str(uid) try: return cls.objects.get(provider=provider, uid=uid) except cls.DoesNotExist: return None @classmethod def get_social_auth_for_user(cls, user, provider=None, id=None): qs = cls.objects.filter(user=user) if provider: qs = qs.filter(provider=provider) if id: qs = qs.filter(id=id) return qs @classmethod def create_social_auth(cls, user, uid, provider): if not isinstance(uid, six.string_types): uid = str(uid) if hasattr(transaction, 'atomic'): # In Django versions that have an "atomic" transaction decorator / context # manager, there's a transaction wrapped around this call. # If the create fails below due to an IntegrityError, ensure that the transaction # stays undamaged by wrapping the create in an atomic. with transaction.atomic(): social_auth = cls.objects.create(user=user, uid=uid, provider=provider) else: social_auth = cls.objects.create(user=user, uid=uid, provider=provider) return social_auth class DjangoNonceMixin(NonceMixin): @classmethod def use(cls, server_url, timestamp, salt): return cls.objects.get_or_create(server_url=server_url, timestamp=timestamp, salt=salt)[1] class DjangoAssociationMixin(AssociationMixin): @classmethod def store(cls, server_url, association): # Don't use get_or_create because issued cannot be null try: assoc = cls.objects.get(server_url=server_url, handle=association.handle) except cls.DoesNotExist: assoc = cls(server_url=server_url, handle=association.handle) try: assoc.secret = base64.encodebytes(association.secret).decode() except AttributeError: assoc.secret = base64.encodestring(association.secret).decode() assoc.issued = association.issued assoc.lifetime = association.lifetime assoc.assoc_type = association.assoc_type assoc.save() @classmethod def get(cls, *args, **kwargs): return cls.objects.filter(*args, **kwargs) @classmethod def remove(cls, ids_to_delete): cls.objects.filter(pk__in=ids_to_delete).delete() class DjangoCodeMixin(CodeMixin): @classmethod def get_code(cls, code): try: return cls.objects.get(code=code) except cls.DoesNotExist: return None class DjangoPartialMixin(PartialMixin): @classmethod def load(cls, token): try: return cls.objects.get(token=token) except cls.DoesNotExist: return None @classmethod def destroy(cls, token): partial = cls.load(token) if partial: partial.delete() class BaseDjangoStorage(BaseStorage): user = DjangoUserMixin nonce = DjangoNonceMixin association = DjangoAssociationMixin code = DjangoCodeMixin social-app-django-3.1.0/social_django/strategy.py000066400000000000000000000126541336634001000220170ustar00rootroot00000000000000# coding=utf-8 from django.conf import settings from django.http import HttpResponse, HttpRequest from django.db.models import Model from django.contrib.contenttypes.models import ContentType from django.contrib.auth import authenticate from django.shortcuts import redirect, resolve_url from django.template import TemplateDoesNotExist, loader, engines from django.utils.crypto import get_random_string from django.utils.encoding import force_text from django.utils.functional import Promise from django.utils.translation import get_language from social_core.strategy import BaseStrategy, BaseTemplateStrategy from .compat import get_request_port def render_template_string(request, html, context=None): """Take a template in the form of a string and render it for the given context""" template = engines['django'].from_string(html) return template.render(context=context, request=request) class DjangoTemplateStrategy(BaseTemplateStrategy): def render_template(self, tpl, context): template = loader.get_template(tpl) return template.render(context=context, request=self.strategy.request) def render_string(self, html, context): return render_template_string(self.strategy.request, html, context) class DjangoStrategy(BaseStrategy): DEFAULT_TEMPLATE_STRATEGY = DjangoTemplateStrategy def __init__(self, storage, request=None, tpl=None): self.request = request self.session = request.session if request else {} super(DjangoStrategy, self).__init__(storage, tpl) def get_setting(self, name): value = getattr(settings, name) # Force text on URL named settings that are instance of Promise if name.endswith('_URL'): if isinstance(value, Promise): value = force_text(value) value = resolve_url(value) return value def request_data(self, merge=True): if not self.request: return {} if merge: data = self.request.GET.copy() data.update(self.request.POST) elif self.request.method == 'POST': data = self.request.POST else: data = self.request.GET return data def request_host(self): if self.request: return self.request.get_host() def request_is_secure(self): """Is the request using HTTPS?""" return self.request.is_secure() def request_path(self): """path of the current request""" return self.request.path def request_port(self): """Port in use for this request""" return get_request_port(request=self.request) def request_get(self): """Request GET data""" return self.request.GET.copy() def request_post(self): """Request POST data""" return self.request.POST.copy() def redirect(self, url): return redirect(url) def html(self, content): return HttpResponse(content, content_type='text/html;charset=UTF-8') def render_html(self, tpl=None, html=None, context=None): if not tpl and not html: raise ValueError('Missing template or html parameters') context = context or {} try: template = loader.get_template(tpl) return template.render(context=context, request=self.request) except TemplateDoesNotExist: return render_template_string(self.request, html, context) def authenticate(self, backend, *args, **kwargs): kwargs['strategy'] = self kwargs['storage'] = self.storage kwargs['backend'] = backend return authenticate(*args, **kwargs) def clean_authenticate_args(self, *args, **kwargs): """Cleanup request argument if present, which is passed to authenticate as for Django 1.11""" if len(args) > 0 and isinstance(args[0], HttpRequest): kwargs['request'], args = args[0], args[1:] return args, kwargs def session_get(self, name, default=None): return self.session.get(name, default) def session_set(self, name, value): self.session[name] = value if hasattr(self.session, 'modified'): self.session.modified = True def session_pop(self, name): return self.session.pop(name, None) def session_setdefault(self, name, value): return self.session.setdefault(name, value) def build_absolute_uri(self, path=None): if self.request: return self.request.build_absolute_uri(path) else: return path def random_string(self, length=12, chars=BaseStrategy.ALLOWED_CHARS): return get_random_string(length, chars) def to_session_value(self, val): """Converts values that are instance of Model to a dictionary with enough information to retrieve the instance back later.""" if isinstance(val, Model): val = { 'pk': val.pk, 'ctype': ContentType.objects.get_for_model(val).pk } return val def from_session_value(self, val): """Converts back the instance saved by self._ctype function.""" if isinstance(val, dict) and 'pk' in val and 'ctype' in val: ctype = ContentType.objects.get_for_id(val['ctype']) ModelClass = ctype.model_class() val = ModelClass.objects.get(pk=val['pk']) return val def get_language(self): """Return current language""" return get_language() social-app-django-3.1.0/social_django/urls.py000066400000000000000000000013751336634001000211400ustar00rootroot00000000000000"""URLs module""" from django.conf import settings from django.conf.urls import url from social_core.utils import setting_name from . import views extra = getattr(settings, setting_name('TRAILING_SLASH'), True) and '/' or '' app_name = 'social' urlpatterns = [ # authentication / association url(r'^login/(?P[^/]+){0}$'.format(extra), views.auth, name='begin'), url(r'^complete/(?P[^/]+){0}$'.format(extra), views.complete, name='complete'), # disconnection url(r'^disconnect/(?P[^/]+){0}$'.format(extra), views.disconnect, name='disconnect'), url(r'^disconnect/(?P[^/]+)/(?P\d+){0}$' .format(extra), views.disconnect, name='disconnect_individual'), ] social-app-django-3.1.0/social_django/utils.py000066400000000000000000000033631336634001000213120ustar00rootroot00000000000000# coding=utf-8 from functools import wraps from django.conf import settings from django.http import Http404 from social_core.utils import setting_name, module_member, get_strategy from social_core.exceptions import MissingBackend from social_core.backends.utils import get_backend from .compat import reverse BACKENDS = settings.AUTHENTICATION_BACKENDS STRATEGY = getattr(settings, setting_name('STRATEGY'), 'social_django.strategy.DjangoStrategy') STORAGE = getattr(settings, setting_name('STORAGE'), 'social_django.models.DjangoStorage') Strategy = module_member(STRATEGY) Storage = module_member(STORAGE) def load_strategy(request=None): return get_strategy(STRATEGY, STORAGE, request) def load_backend(strategy, name, redirect_uri): Backend = get_backend(BACKENDS, name) return Backend(strategy, redirect_uri) def psa(redirect_uri=None, load_strategy=load_strategy): def decorator(func): @wraps(func) def wrapper(request, backend, *args, **kwargs): uri = redirect_uri if uri and not uri.startswith('/'): uri = reverse(redirect_uri, args=(backend,)) request.social_strategy = load_strategy(request) # backward compatibility in attribute name, only if not already # defined if not hasattr(request, 'strategy'): request.strategy = request.social_strategy try: request.backend = load_backend(request.social_strategy, backend, uri) except MissingBackend: raise Http404('Backend not found') return func(request, backend, *args, **kwargs) return wrapper return decorator social-app-django-3.1.0/social_django/views.py000066400000000000000000000130221336634001000213000ustar00rootroot00000000000000from django.conf import settings from django.contrib.auth import login, REDIRECT_FIELD_NAME from django.contrib.auth.decorators import login_required from django.views.decorators.csrf import csrf_exempt, csrf_protect from django.views.decorators.http import require_POST from django.views.decorators.cache import never_cache from social_core.utils import setting_name from social_core.actions import do_auth, do_complete, do_disconnect from .utils import psa NAMESPACE = getattr(settings, setting_name('URL_NAMESPACE'), None) or 'social' # Calling `session.set_expiry(None)` results in a session lifetime equal to # platform default session lifetime. DEFAULT_SESSION_TIMEOUT = None @never_cache @psa('{0}:complete'.format(NAMESPACE)) def auth(request, backend): return do_auth(request.backend, redirect_name=REDIRECT_FIELD_NAME) @never_cache @csrf_exempt @psa('{0}:complete'.format(NAMESPACE)) def complete(request, backend, *args, **kwargs): """Authentication complete view""" return do_complete(request.backend, _do_login, user=request.user, redirect_name=REDIRECT_FIELD_NAME, request=request, *args, **kwargs) @never_cache @login_required @psa() @require_POST @csrf_protect def disconnect(request, backend, association_id=None): """Disconnects given backend from current logged in user.""" return do_disconnect(request.backend, request.user, association_id, redirect_name=REDIRECT_FIELD_NAME) def get_session_timeout(social_user, enable_session_expiration=False, max_session_length=None): if enable_session_expiration: # Retrieve an expiration date from the social user who just finished # logging in; this value was set by the social auth backend, and was # typically received from the server. expiration = social_user.expiration_datetime() # We've enabled session expiration. Check to see if we got # a specific expiration time from the provider for this user; # if not, use the platform default expiration. if expiration: received_expiration_time = expiration.total_seconds() else: received_expiration_time = DEFAULT_SESSION_TIMEOUT # Check to see if the backend set a value as a maximum length # that a session may be; if they did, then we should use the minimum # of that and the received session expiration time, if any, to # set the session length. if received_expiration_time is None and max_session_length is None: # We neither received an expiration length, nor have a maximum # session length. Use the platform default. session_expiry = DEFAULT_SESSION_TIMEOUT elif received_expiration_time is None and max_session_length is not None: # We only have a maximum session length; use that. session_expiry = max_session_length elif received_expiration_time is not None and max_session_length is None: # We only have an expiration time received by the backend # from the provider, with no set maximum. Use that. session_expiry = received_expiration_time else: # We received an expiration time from the backend, and we also # have a set maximum session length. Use the smaller of the two. session_expiry = min(received_expiration_time, max_session_length) else: # If there's an explicitly-set maximum session length, use that # even if we don't want to retrieve session expiry times from # the backend. If there isn't, then use the platform default. if max_session_length is None: session_expiry = DEFAULT_SESSION_TIMEOUT else: session_expiry = max_session_length return session_expiry def _do_login(backend, user, social_user): user.backend = '{0}.{1}'.format(backend.__module__, backend.__class__.__name__) # Get these details early to avoid any issues involved in the # session switch that happens when we call login(). enable_session_expiration = backend.setting('SESSION_EXPIRATION', False) max_session_length_setting = backend.setting('MAX_SESSION_LENGTH', None) # Log the user in, creating a new session. login(backend.strategy.request, user) # Make sure that the max_session_length value is either an integer or # None. Because we get this as a setting from the backend, it can be set # to whatever the backend creator wants; we want to be resilient against # unexpected types being presented to us. try: max_session_length = int(max_session_length_setting) except (TypeError, ValueError): # We got a response that doesn't look like a number; use the default. max_session_length = None # Get the session expiration length based on the maximum session length # setting, combined with any session length received from the backend. session_expiry = get_session_timeout( social_user, enable_session_expiration=enable_session_expiration, max_session_length=max_session_length, ) try: # Set the session length to our previously determined expiry length. backend.strategy.request.session.set_expiry(session_expiry) except OverflowError: # The timestamp we used wasn't in the range of values supported by # Django for session length; use the platform default. We tried. backend.strategy.request.session.set_expiry(DEFAULT_SESSION_TIMEOUT) social-app-django-3.1.0/tests/000077500000000000000000000000001336634001000161415ustar00rootroot00000000000000social-app-django-3.1.0/tests/__init__.py000066400000000000000000000000001336634001000202400ustar00rootroot00000000000000social-app-django-3.1.0/tests/compat.py000066400000000000000000000005451336634001000200020ustar00rootroot00000000000000# coding=utf-8 import django from django.conf.urls import include from django.contrib import admin if django.VERSION < (1, 9): admin_urls = include(admin.site.urls) base_url = 'http://testserver' else: admin_urls = admin.site.urls base_url = '' if django.VERSION < (1, 10): username_max_length = 30 else: username_max_length = 150 social-app-django-3.1.0/tests/settings.py000066400000000000000000000030651336634001000203570ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals, absolute_import import os BASE_DIR = os.path.dirname(os.path.abspath(__file__)) DEBUG = True USE_TZ = True DATABASES = { "default": { "ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:", } } ROOT_URLCONF = "tests.urls" INSTALLED_APPS = [ "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.sites", "django.contrib.messages", "django.contrib.admin", "social_django", ] SITE_ID = 1 MIDDLEWARE = MIDDLEWARE_CLASSES = ( 'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'social_django.middleware.SocialAuthExceptionMiddleware', ) AUTHENTICATION_BACKENDS = ( 'social_core.backends.facebook.FacebookOAuth2', 'django.contrib.auth.backends.ModelBackend', ) TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'APP_DIRS': True, 'DIRS': [os.path.join(BASE_DIR, 'templates')], 'OPTIONS': { 'context_processors': [ 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', 'social_django.context_processors.backends', 'social_django.context_processors.login_redirect', ], } }, ] SECRET_KEY = '6p%gef2(6kvjsgl*7!51a7z8c3=u4uc&6ulpua0g1^&sthiifp' STATIC_URL = '/static/' social-app-django-3.1.0/tests/templates/000077500000000000000000000000001336634001000201375ustar00rootroot00000000000000social-app-django-3.1.0/tests/templates/test.html000066400000000000000000000000041336634001000217760ustar00rootroot00000000000000testsocial-app-django-3.1.0/tests/test_admin.py000066400000000000000000000021501336634001000206400ustar00rootroot00000000000000# -*- coding: utf-8 from __future__ import unicode_literals, absolute_import from django.contrib.auth import get_user_model from django.test import TestCase from social_django.compat import reverse from social_django.models import UserSocialAuth class SocialAdminTest(TestCase): @classmethod def setUpTestData(cls): User = get_user_model() User.objects.create_superuser( username='admin', email='admin@test.com', first_name='Admin', password='super-duper-test' ) def test_admin_app_name(self): """The App name in the admin index page""" self.client.login(username='admin', password='super-duper-test') response = self.client.get(reverse('admin:index')) self.assertContains(response, "Python Social Auth") def test_social_auth_changelist(self): """The App name in the admin index page""" self.client.login(username='admin', password='super-duper-test') meta = UserSocialAuth._meta url_name = 'admin:%s_%s_changelist' % (meta.app_label, meta.model_name) self.client.get(reverse(url_name)) social-app-django-3.1.0/tests/test_context_processors.py000066400000000000000000000013271336634001000235230ustar00rootroot00000000000000# -*- coding: utf-8 -*- from django.test import TestCase, RequestFactory, override_settings from social_django.context_processors import login_redirect @override_settings(REDIRECT_FIELD_NAME='next') class TestContextProcessors(TestCase): def setUp(self): self.request_factory = RequestFactory() def test_login_redirect_unicode_quote(self): request = self.request_factory.get('/', data={'next': 'profile/sjó'}) result = login_redirect(request) self.assertEqual( result, { 'REDIRECT_FIELD_NAME': 'next', 'REDIRECT_FIELD_VALUE': 'profile/sj%C3%B3', 'REDIRECT_QUERYSTRING': 'next=profile/sj%C3%B3' } ) social-app-django-3.1.0/tests/test_middleware.py000066400000000000000000000041041336634001000216660ustar00rootroot00000000000000# coding=utf-8 import logging import mock from django.contrib.messages import MessageFailure from django.http import HttpResponseRedirect from django.test import TestCase, override_settings from social_core.exceptions import AuthCanceled from social_django.compat import reverse from .compat import base_url class MockAuthCanceled(AuthCanceled): def __init__(self, *args, **kwargs): if not args: kwargs.setdefault('backend', None) super(MockAuthCanceled, self).__init__(*args, **kwargs) @mock.patch('social_core.backends.base.BaseAuth.request', side_effect=MockAuthCanceled) class TestMiddleware(TestCase): def setUp(self): session = self.client.session session['facebook_state'] = '1' session.save() self.complete_url = reverse('social:complete', kwargs={'backend': 'facebook'}) self.complete_url += '?code=2&state=1' def test_exception(self, mocked): with self.assertRaises(MockAuthCanceled): self.client.get(self.complete_url) @override_settings(DEBUG=True) def test_exception_debug(self, mocked): logging.disable(logging.CRITICAL) with self.assertRaises(MockAuthCanceled): self.client.get(self.complete_url) logging.disable(logging.NOTSET) @override_settings(SOCIAL_AUTH_LOGIN_ERROR_URL='/') def test_login_error_url(self, mocked): response = self.client.get(self.complete_url) self.assertTrue(isinstance(response, HttpResponseRedirect)) self.assertEqual(response.url, base_url + '/') @override_settings(SOCIAL_AUTH_LOGIN_ERROR_URL='/') @mock.patch('django.contrib.messages.error', side_effect=MessageFailure) def test_message_failure(self, mocked_request, mocked_error): response = self.client.get(self.complete_url) self.assertTrue(isinstance(response, HttpResponseRedirect)) self.assertEqual(response.url, base_url + '/?message=Authentication%20process%20canceled' '&backend=facebook') social-app-django-3.1.0/tests/test_models.py000066400000000000000000000204541336634001000210420ustar00rootroot00000000000000# -*- coding: utf-8 from __future__ import unicode_literals, absolute_import from datetime import timedelta import mock from django.contrib.auth import get_user_model from django.core.management import call_command from django.db import IntegrityError from django.test import TestCase from social_django.models import AbstractUserSocialAuth, Association, Code, \ DjangoStorage, Nonce, Partial, UserSocialAuth from .compat import username_max_length class TestSocialAuthUser(TestCase): def test_user_relationship_none(self): """Accessing User.social_user outside of the pipeline doesn't work""" User = get_user_model() user = User.objects.create_user(username="randomtester") with self.assertRaises(AttributeError): user.social_user def test_user_existing_relationship(self): """Accessing User.social_user outside of the pipeline doesn't work""" User = get_user_model() user = User.objects.create_user(username="randomtester") UserSocialAuth.objects.create(user=user, provider='my-provider', uid='1234') with self.assertRaises(AttributeError): user.social_user def test_get_social_auth(self): User = get_user_model() user = User.objects.create_user(username="randomtester") user_social = UserSocialAuth.objects.create(user=user, provider='my-provider', uid='1234') other = UserSocialAuth.get_social_auth('my-provider', '1234') self.assertEqual(other, user_social) def test_get_social_auth_none(self): other = UserSocialAuth.get_social_auth('my-provider', '1234') self.assertIsNone(other) def test_cleanup(self): Code.objects.create(email='first@example.com') Code.objects.create(email='second@example.com') code = Code.objects.create(email='expire@example.com') code.timestamp -= timedelta(days=30) code.save() Partial.objects.create() partial = Partial.objects.create() partial.timestamp -= timedelta(days=30) partial.save() call_command('clearsocial') self.assertEqual(2, Code.objects.count()) self.assertEqual(1, Partial.objects.count()) class TestUserSocialAuth(TestCase): def setUp(self): self.user_model = get_user_model() self.user = self.user_model.objects.create_user( username='randomtester', email='user@example.com') self.usa = UserSocialAuth.objects.create( user=self.user, provider='my-provider', uid='1234') def test_changed(self): self.user.email = eml = 'test@example.com' UserSocialAuth.changed(user=self.user) db_eml = self.user_model.objects.get(username=self.user.username).email self.assertEqual(db_eml, eml) def test_set_extra_data(self): self.usa.set_extra_data({'a': 'b'}) self.usa.refresh_from_db() db_data = UserSocialAuth.objects.get(id=self.usa.id).extra_data self.assertEqual(db_data, {'a': 'b'}) def test_disconnect(self): m = mock.Mock() UserSocialAuth.disconnect(m) self.assertListEqual(m.method_calls, [mock.call.delete()]) def test_username_field(self): self.assertEqual(UserSocialAuth.username_field(), 'username') with mock.patch('social_django.models.UserSocialAuth.user_model', return_value=mock.Mock(USERNAME_FIELD='test')): self.assertEqual(UserSocialAuth.username_field(), 'test') def test_user_exists(self): self.assertTrue(UserSocialAuth.user_exists(username=self.user.username)) self.assertFalse(UserSocialAuth.user_exists(username='test')) def test_get_username(self): self.assertEqual(UserSocialAuth.get_username(self.user), self.user.username) def test_create_user(self): # Catch integrity error and find existing user UserSocialAuth.create_user(username=self.user.username) def test_create_user_reraise(self): with self.assertRaises(IntegrityError): UserSocialAuth.create_user(username=self.user.username, email=None) @mock.patch('social_django.models.UserSocialAuth.username_field', return_value='email') @mock.patch('django.contrib.auth.models.UserManager.create_user', side_effect=IntegrityError) def test_create_user_custom_username(self, *args): UserSocialAuth.create_user(username=self.user.email) @mock.patch('social_django.storage.transaction', spec=[]) def test_create_user_without_transaction_atomic(self, *args): UserSocialAuth.create_user(username='test') self.assertTrue(self.user_model.objects.filter( username='test').exists()) def test_get_user(self): self.assertEqual(UserSocialAuth.get_user(pk=self.user.pk), self.user) self.assertIsNone(UserSocialAuth.get_user(pk=123)) def test_get_users_by_email(self): qs = UserSocialAuth.get_users_by_email(email=self.user.email) self.assertEqual(qs.count(), 1) def test_get_social_auth(self): usa = self.usa # Model self.assertEqual(UserSocialAuth.get_social_auth( provider=usa.provider, uid=usa.uid), usa) self.assertIsNone(UserSocialAuth.get_social_auth(provider='a', uid=1)) # Mixin self.assertEqual(super(AbstractUserSocialAuth, usa).get_social_auth( provider=usa.provider, uid=usa.uid), usa) self.assertIsNone(super(AbstractUserSocialAuth, usa).get_social_auth( provider='a', uid=1)) # Manager self.assertEqual(UserSocialAuth.objects.get_social_auth( provider=usa.provider, uid=usa.uid), usa) self.assertIsNone(UserSocialAuth.objects.get_social_auth( provider='a', uid=1)) def test_get_social_auth_for_user(self): qs = UserSocialAuth.get_social_auth_for_user( user=self.user, provider=self.usa.provider, id=self.usa.id) self.assertEqual(qs.count(), 1) def test_create_social_auth(self): usa = UserSocialAuth.create_social_auth( user=self.user, provider='test', uid=1) self.assertEqual(usa.uid, '1') self.assertEqual(str(usa), str(self.user)) @mock.patch('social_django.storage.transaction', spec=[]) def test_create_social_auth_without_transaction_atomic(self, *args): with self.assertRaises(IntegrityError): UserSocialAuth.create_social_auth( user=self.user, provider=self.usa.provider, uid=self.usa.uid) def test_username_max_length(self): self.assertEqual(UserSocialAuth.username_max_length(), username_max_length) class TestNonce(TestCase): def test_use(self): self.assertEqual(Nonce.objects.count(), 0) self.assertTrue(Nonce.use(server_url='/', timestamp=1, salt='1')) self.assertFalse(Nonce.use(server_url='/', timestamp=1, salt='1')) self.assertEqual(Nonce.objects.count(), 1) class TestAssociation(TestCase): def test_store_get_remove(self): Association.store(server_url='/', association=mock.Mock( handle='a', secret=b'b', issued=1, lifetime=2, assoc_type='c')) qs = Association.get(handle='a') self.assertEqual(qs.count(), 1) self.assertEqual(qs[0].secret, 'Yg==\n') Association.remove(ids_to_delete=[qs.first().id]) self.assertEqual(Association.objects.count(), 0) class TestCode(TestCase): def test_get_code(self): code1 = Code.objects.create(email='test@example.com', code='abc') code2 = Code.get_code(code='abc') self.assertEqual(code1, code2) self.assertIsNone(Code.get_code(code='xyz')) class TestPartial(TestCase): def test_load_destroy(self): p = Partial.objects.create(token='x', backend='y', data={}) self.assertEqual(Partial.load(token='x'), p) self.assertIsNone(Partial.load(token='y')) Partial.destroy(token='x') self.assertEqual(Partial.objects.count(), 0) class TestDjangoStorage(TestCase): def test_is_integrity_error(self): self.assertTrue(DjangoStorage.is_integrity_error(IntegrityError())) social-app-django-3.1.0/tests/test_strategy.py000066400000000000000000000104601336634001000214150ustar00rootroot00000000000000# coding=utf-8 from __future__ import unicode_literals, absolute_import import mock from django.contrib.auth import get_user_model from django.contrib.contenttypes.models import ContentType from django.contrib.sessions.middleware import SessionMiddleware from django.http import QueryDict, HttpResponse from django.test import TestCase, RequestFactory from django.utils.translation import ugettext_lazy from social_django.utils import load_strategy, load_backend class TestStrategy(TestCase): def setUp(self): self.request_factory = RequestFactory() self.request = self.request_factory.get('/', data={'x': '1'}) SessionMiddleware().process_request(self.request) self.strategy = load_strategy(request=self.request) def test_request_methods(self): self.assertEqual(self.strategy.request_port(), '80') self.assertEqual(self.strategy.request_path(), '/') self.assertEqual(self.strategy.request_host(), 'testserver') self.assertEqual(self.strategy.request_is_secure(), False) self.assertEqual(self.strategy.request_data(), QueryDict('x=1')) self.assertEqual(self.strategy.request_get(), QueryDict('x=1')) self.assertEqual(self.strategy.request_post(), {}) self.request.method = 'POST' self.assertEqual(self.strategy.request_data(merge=False), {}) def test_build_absolute_uri(self): self.assertEqual(self.strategy.build_absolute_uri('/'), 'http://testserver/') def test_settings(self): with self.settings(LOGIN_ERROR_URL='/'): self.assertEqual(self.strategy.get_setting('LOGIN_ERROR_URL'), '/') with self.settings(LOGIN_ERROR_URL=ugettext_lazy('/')): self.assertEqual(self.strategy.get_setting('LOGIN_ERROR_URL'), '/') def test_session_methods(self): self.strategy.session_set('k', 'v') self.assertEqual(self.strategy.session_get('k'), 'v') self.assertEqual(self.strategy.session_setdefault('k', 'x'), 'v') self.assertEqual(self.strategy.session_pop('k'), 'v') def test_random_string(self): rs1 = self.strategy.random_string() self.assertEqual(len(rs1), 12) self.assertNotEqual(rs1, self.strategy.random_string()) def test_session_value(self): user_model = get_user_model() user = user_model.objects.create_user(username="test") ctype = ContentType.objects.get_for_model(user_model) val = self.strategy.to_session_value(val=user) self.assertEqual(val, {'pk': user.pk, 'ctype': ctype.pk}) instance = self.strategy.from_session_value(val=val) self.assertEqual(instance, user) def test_get_language(self): self.assertEqual(self.strategy.get_language(), 'en-us') def test_html(self): result = self.strategy.render_html(tpl='test.html') self.assertEqual(result, 'test') result = self.strategy.render_html(html='xoxo') self.assertEqual(result, 'xoxo') with self.assertRaisesMessage( ValueError, 'Missing template or html parameters'): self.strategy.render_html() result = self.strategy.html(content='xoxo') self.assertIsInstance(result, HttpResponse) self.assertEqual(result.content, b'xoxo') ctx = {'x': 1} result = self.strategy.tpl.render_template(tpl='test.html', context=ctx) self.assertEqual(result, 'test') result = self.strategy.tpl.render_string(html='xoxo', context=ctx) self.assertEqual(result, 'xoxo') def test_authenticate(self): backend = load_backend(strategy=self.strategy, name='facebook', redirect_uri='/') user = mock.Mock() with mock.patch('social_core.backends.base.BaseAuth.pipeline', return_value=user): result = self.strategy.authenticate(backend=backend, response=mock.Mock()) self.assertEqual(result, user) self.assertEqual(result.backend, 'social_core.backends.facebook.FacebookOAuth2') def test_clean_authenticate_args(self): args, kwargs = self.strategy.clean_authenticate_args(self.request) self.assertEqual(args, ()) self.assertEqual(kwargs, {'request': self.request}) social-app-django-3.1.0/tests/test_views.py000066400000000000000000000123211336634001000207060ustar00rootroot00000000000000# -*- coding: utf-8 -*- import mock from django.contrib.auth import get_user_model from django.contrib.auth.models import AbstractBaseUser from django.test import TestCase, override_settings from social_django.compat import reverse from social_django.models import UserSocialAuth from social_django.views import get_session_timeout from .compat import base_url @override_settings(SOCIAL_AUTH_FACEBOOK_KEY='1', SOCIAL_AUTH_FACEBOOK_SECRET='2') class TestViews(TestCase): def setUp(self): session = self.client.session session['facebook_state'] = '1' session.save() def test_begin_view(self): response = self.client.get(reverse('social:begin', kwargs={'backend': 'facebook'})) self.assertEqual(response.status_code, 302) url = reverse('social:begin', kwargs={'backend': 'blabla'}) response = self.client.get(url) self.assertEqual(response.status_code, 404) @mock.patch('social_core.backends.base.BaseAuth.request') def test_complete(self, mock_request): url = reverse('social:complete', kwargs={'backend': 'facebook'}) url += '?code=2&state=1' mock_request.return_value.json.return_value = {'access_token': '123'} with mock.patch('django.contrib.sessions.backends.base.SessionBase' '.set_expiry', side_effect=[OverflowError, None]): response = self.client.get(url) self.assertEqual(response.status_code, 302) self.assertEqual(response.url, base_url + '/accounts/profile/') @mock.patch('social_core.backends.base.BaseAuth.request') def test_disconnect(self, mock_request): user_model = get_user_model() user = user_model.objects.create_user(username='test', password='pwd') UserSocialAuth.objects.create(user=user, provider='facebook') self.client.login(username='test', password='pwd') url = reverse('social:disconnect', kwargs={'backend': 'facebook'}) response = self.client.post(url) self.assertEqual(response.status_code, 302) self.assertEqual(response.url, 'http://testserver/accounts/profile/') url = reverse('social:disconnect_individual', kwargs={'backend': 'facebook', 'association_id': '123'}) hup = AbstractBaseUser.has_usable_password del AbstractBaseUser.has_usable_password response = self.client.post(url) self.assertEqual(response.status_code, 302) self.assertEqual(response.url, 'http://testserver/accounts/profile/') AbstractBaseUser.has_usable_password = hup class TestGetSessionTimeout(TestCase): """ Ensure that the branching logic of get_session_timeout behaves as expected. """ def setUp(self): self.social_user = mock.MagicMock() self.social_user.expiration_datetime.return_value = None super(TestGetSessionTimeout, self).setUp() def set_user_expiration(self, seconds): self.social_user.expiration_datetime.return_value = mock.MagicMock( total_seconds = mock.MagicMock(return_value=seconds) ) def test_expiration_disabled_no_max(self): self.set_user_expiration(60) expiration_length = get_session_timeout( self.social_user, enable_session_expiration=False ) self.assertIsNone(expiration_length) def test_expiration_disabled_with_max(self): expiration_length = get_session_timeout( self.social_user, enable_session_expiration=False, max_session_length=60 ) self.assertEqual(expiration_length, 60) def test_expiration_disabled_with_zero_max(self): expiration_length = get_session_timeout( self.social_user, enable_session_expiration=False, max_session_length=0 ) self.assertEqual(expiration_length, 0) def test_user_has_session_length_no_max(self): self.set_user_expiration(60) expiration_length = get_session_timeout( self.social_user, enable_session_expiration=True ) self.assertEqual(expiration_length, 60) def test_user_has_session_length_larger_max(self): self.set_user_expiration(60) expiration_length = get_session_timeout( self.social_user, enable_session_expiration=True, max_session_length=90 ) self.assertEqual(expiration_length, 60) def test_user_has_session_length_smaller_max(self): self.set_user_expiration(60) expiration_length = get_session_timeout( self.social_user, enable_session_expiration=True, max_session_length=30 ) self.assertEqual(expiration_length, 30) def test_user_has_no_session_length_with_max(self): expiration_length = get_session_timeout( self.social_user, enable_session_expiration=True, max_session_length=60 ) self.assertEqual(expiration_length, 60) def test_user_has_no_session_length_no_max(self): expiration_length = get_session_timeout( self.social_user, enable_session_expiration=True ) self.assertIsNone(expiration_length) social-app-django-3.1.0/tests/urls.py000066400000000000000000000003241336634001000174770ustar00rootroot00000000000000# -*- coding: utf-8 from django.conf.urls import url, include from .compat import admin_urls urlpatterns = [ url(r'^admin/', admin_urls), url(r'^', include('social_django.urls', namespace='social')), ] social-app-django-3.1.0/tox.ini000066400000000000000000000014031336634001000163100ustar00rootroot00000000000000[tox] envlist = {py27,py34,py35}-django-18 {py27,py34,py35}-django-19 {py27,py34,py35}-django-110 {py27,py34,py35,py36}-django-111 {py35,py36}-django-master {py35,py36}-django-20 [testenv] install_command = pip install --pre {opts} {packages} setenv = PYTHONPATH = {toxinidir}:{toxinidir}/social_django commands = coverage run manage.py test deps = django-18: Django>=1.8,<1.9 django-19: Django>=1.9,<1.10 django-110: Django>=1.10,<1.11 django-111: Django>=1.11,<2 django-master: https://github.com/django/django/archive/master.tar.gz django-20: Django>=2.0b1,<2.1 -r{toxinidir}/dev-requirements.txt basepython = py36: python3.6 py35: python3.5 py34: python3.4 py32: python3.2 py27: python2.7