social-auth-core-3.1.0/0000755000175000017500000000000013433256445014541 5ustar omabomab00000000000000social-auth-core-3.1.0/requirements-saml-python2.txt0000644000175000017500000000002313336542652022353 0ustar omabomab00000000000000python-saml>=2.2.0 social-auth-core-3.1.0/requirements-base.txt0000644000175000017500000000012213336542652020730 0ustar omabomab00000000000000requests>=2.9.1 oauthlib>=1.0.3 requests-oauthlib>=0.6.1 six>=1.10.0 PyJWT>=1.4.0 social-auth-core-3.1.0/LICENSE0000644000175000017500000000277113336542652015555 0ustar omabomab00000000000000Copyright (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-auth-core-3.1.0/CHANGELOG.md0000644000175000017500000002772713433256401016361 0ustar omabomab00000000000000# 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/). ## [Unreleased](https://github.com/python-social-auth/social-core/commits/master) ## [3.1.0](https://github.com/python-social-auth/social-core/releases/tag/3.1.0) - 2019-02-20 ### Added - Universe Ticketing backend - Auth0.com authentication backend ### Changed - Update Bungie backend dropping any Django reference - Enable and fix JWT related tests - Remove PyPy support from Tox - Drop support for Python 3.4 in Tox - Allow to override JWT decode options in Open ID Connect base backend - Pass access token via Authorization header to Google user data url - Updated `user_data` method in `AzureADOAuth2` to return `access_token` if `id_token` is not present in response ## [3.0.0](https://github.com/python-social-auth/social-core/releases/tag/3.0.0) - 2019-01-14 ### Changed - Updated Azure B2C to extract first email from list if it's a list - Replace deprecated Google+ API usage with https://www.googleapis.com/oauth2/v3/userinfo - Updated Azure Tenant to fix Nonetype error - Updated comment denoting incorrect setting name - Yandex: do not fail when no email is present - Mediawiki: do not fail when no email is present - Mediawiki: enhance `get_user_details` to return more details ## [2.0.0](https://github.com/python-social-auth/social-core/releases/tag/2.0.0) - 2018-10-28 ### Added - Telegram authentication backend - Keycloak backend is added with preliminary OAuth2 support - Globus OpenId Connect backend - Discord OAuth2 backend - SciStarter OAuth2 backend - Flat OAuth2 backend - ELIXIR OpenId Connect backend - Atlassian OAuth2 backend ### Changed - GitHub backend now uses `state` parameter instead of `redirect_state` - Correct setting name on AzureAD Tenant backend - Introduce access token expired threshold of 5 seconds by default - Delete partial token from session if still present - Use `userPrincipalName` to set `username` and `email` accordingly - Send authorization headers to Kakao OAuth2, properly fill user details - LINE API update to v2.1 - Use `unitest2` with Python 3 - Update Slack backend to use computed usename on teams setups - Enforce `unicode_literals` on Slack backend - Update ORCID backend to support Member API - Updated Pixelpin backend to use the new OpenId Connect service - Update `sanitize_redirect` to invalidate redirects like `///evil.com` - Update Coinbase API endpoint - Dropped Python 3.3 support - Updated Weixin backend to use `urlencode` from `six` - Updated Google+ backend to properly process requests with `id_token` - Updated OpenId connect dependencies ## [1.7.0](https://github.com/python-social-auth/social-core/releases/tag/1.7.0) - 2018-02-20 ### Changed - Update EvenOnline token expiration key - Update OpenStreetMap URL to `https` - Fix LinkedIn backend to send the oauth_token as `Authorization` header - Fixed `extra_data` update to use the `alias` as key too - Make `signed_request` optional in Facebook App OAuth2 backend - Support string and lists on SAML permanent id value - Correct sending `params` sending on `GET` access-token retrieval case - Ensure b2c policy name check - Use `extras_requrie` to specify python specific version dependencies ### Added - Added support for AzureAD B2C OAuth2 - Added LinkedIn Mobile OAuth2 backend ## [1.6.0](https://github.com/python-social-auth/social-core/releases/tag/1.6.0) - 2017-12-22 ### Changed - Fix coinbase backend to use api v2 - Default `REDIRECT_STATE` to `False` in `FacebookOAuth2` backend. - Add revoke token url for Coinbase OAuth2 backend - Fix LinkedIn backend to send `oauth_token` as request header - Make partial step decorator handle arguments ### Added - Added support for ChatWork OAuth2 backend ## [1.5.0](https://github.com/python-social-auth/social-core/releases/tag/1.5.0) - 2017-10-28 ### Changed - Fix using the entire SAML2 nameid string - Prevent timing attacks against state token - Updated GitLab API version to v4 - Enforce UTC when calculating access token expiration time - Cleanup user attributes update from social details - Send authorization header on Reddit auth ### Added - Added support for tenant for Azure AD backend - Added JWT validation for Azure AD backend - Added support for Bungie.net OAuth2 backend - Added support for Eventbrite OAuth2 backend - Added support for OpenShift OAuth2 backend - Added support for Microsoft Graph OAuth2 backend ## [1.4.0](https://github.com/python-social-auth/social-core/releases/tag/1.4.0) - 2017-06-09 ### Changed - Fix path in import BaseOAuth2 for Monzo - Fix auth header formatting problem for Fitbit OAuth2 - Raise AuthForbidden when provider returns 401. - Update Facebook API to version 2.9 - Speed up authorization process for VKAppOAuth2 - Apply same sanitization as on connect to disconnect. - Disable `redirect_state` usage on Disqus backend ### Added - Added Udata OAuth2 backend - Added ORCID backend - Added feature to get all extra data from backend through `GET_ALL_EXTRA_DATA` boolean flag. - Added Patreon provider ## [1.3.0](https://github.com/python-social-auth/social-core/releases/tag/1.3.0) - 2017-05-06 ### Added - Use extra_data method when refreshing an `access_token`, ensure that auth-time is updated then - Added 500px OAuth1 backend - Added Monzo OAuth2 backend - Added `get_access_token` method that will refresh if expired ### Changed - Updated email validation to pass the partial pipeline token if given. - Prefer passed parameters in `authenticate` method - Properly discard already used verification codes - Save SAML attributes in `extra_data` - Note `id_token` in GooglePlusAuth's AuthMissingParameter ## [1.2.0](https://github.com/python-social-auth/social-core/releases/tag/1.2.0) - 2017-02-10 ### Added - Limit Slack by team through `SOCIAL_AUTH_SLACK_TEAM` setting ### Changed - Enable defining extra arguments for AzureAD backend. - Updated key `expires` to `expires_in` for Facebook OAuth2 backend - Updated Slack `id` fetch to default to user `id` if not present in response ## [1.1.0](https://github.com/python-social-auth/social-core/releases/tag/1.1.0) - 2017-01-31 ### Added - Mediawiki backend - Strategy method to let implementation cleanup arguments passed to the authenticate method ### Changed - Removed OneLogin SAML IDP dummy settings while generating metadata xml - Fixed Asana user details response handling - Enforce defusedxml version with support for Python 3.6 - Updated documentation URL in backends ## [1.0.1](https://github.com/python-social-auth/social-core/releases/tag/1.0.1) - 2017-01-23 ### Changed - Fixed broken dependencies while building the package ## [1.0.0](https://github.com/python-social-auth/social-core/releases/tag/1.0.0) - 2017-01-22 ### Added - Store partial pipeline data in an storage class - Store `auth_time` with the last time authentication toke place, use `auth_time` to determine if access token expired - Ensure that `testkey.pem` is distributed - Added Asana OAuth2 backend ### Changed - Removed the old `save_status_to_session` to partialize a pipeline run ## [0.2.1](https://github.com/python-social-auth/social-core/releases/tag/0.2.1) - 2016-12-31 ### Added - Defined `extras` for SAML, and "all" that will install SAML and OpenIdConnect - Added `auth_time` in extra data by default to store the time that the authentication toke place ### Changed - Remove set/get current strategy methods - Fixed the `extras` requirements defined in the setup.py script ## [0.2.0](https://github.com/python-social-auth/social-core/releases/tag/0.2.0) - 2016-12-31 ### Changed - Reorganize requirements, make OpenIdConnect optional - Split OpenIdConnect from OpenId module, install with `social-core[openidconnect]` ## [0.1.0](https://github.com/python-social-auth/social-core/releases/tag/0.1.0) - 2016-12-28 ### Added - Added support for GitLab OAuth2 backend. Refs [#2](https://github.com/python-social-auth/social-core/issues/2) - Added support for Facebook OAuth2 return_scopes parameter. Refs [#818](https://github.com/omab/python-social-auth/issues/818) - Added support for per-backend USER_FIELDS setting. Refs [#661](https://github.com/omab/python-social-auth/issues/661) - Added `expires_in` as `expires` for LinkedIn OAuth2. Refs [#666](https://github.com/omab/python-social-auth/issues/666) - Added `SOCIAL_AUTH_USER_AGENT` setting to override the default User-Agent header. Refs [#752](https://github.com/omab/python-social-auth/issues/752) - Enabled Python 3 SAML support through python3-saml package. Refs [#846](https://github.com/omab/python-social-auth/issues/846) - Added better username characters clenup rules, support for a configurable cleanup function through SOCIAL_AUTH_CLEAN_USERNAME_FUNCTION (import path) setting. - Added configurable option SOCIAL_AUTH_FACEBOOK_*_API_VERSION to override the default Facebook API version used. - Add Lyft OAuth2 implementation to Python Social Auth (port from [#1036](https://github.com/omab/python-social-auth/pull/1036/files) by iampark) - Added the ability to specify a pipeline on a per backend basis (port from [#1019](https://github.com/omab/python-social-auth/pull/1019) by keattang) - Add support for MailChimp as an OAuth v2 backend (port from [#1037](https://github.com/omab/python-social-auth/pull/1037) by svvitale) - Added Shimmering backend (port from [#1054](https://github.com/omab/python-social-auth/pull/1054) by iamkhush) - Added Quizlet backend (port from [#1012](https://github.com/omab/python-social-auth/pull/1012) by s-alexey) - Added Dockerfile to simplify the running of tests (`make docker-tox`) ### Changed - Changed Facebook refresh token processing. Refs [#866](https://github.com/omab/python-social-auth/issues/866) - Update Google+ Auth tokeninfo API version, drop support for deprecated API scopes. Refs [#791](https://github.com/omab/python-social-auth/issues/791). - Fixed OAuth1/2 early state validation on error responses. - Disabled SAML test when running on Travis-ci on Python 3.5 since it [segfaults](https://travis-ci.org/python-social-auth/social-core/jobs/186790227) probably by a bad build in one of the dependencies - Fixed Xing backend testing broken by previous change - Fixed Xing backend dropping `callback_uri` and `oauth_verifier` parameters on authenticated API calls. Refs [#871](https://github.com/omab/python-social-auth/issues/871) - Updated slack backend implementation, update API endpoints used, add test case. - Changed Dailymotion user data API endpoint - Changed how "false" values are treated in the user attributes update pipeline - Fix google OpenID Connect (port from [#747](https://github.com/omab/python-social-auth/pull/747) by mvschaik) - Update Facebook api version to v2.8 (port from [#1047](https://github.com/omab/python-social-auth/pull/1047) by browniebroke) - Remove Facebook2OAuth2 and Facebook2AppOAuth2 backends (port from [#1046](https://github.com/omab/python-social-auth/pull/1046) by browniebroke) - change username, email and fullname keys (port from [#1028](https://github.com/omab/python-social-auth/pull/1028) by inlanger) - Moves fix convert username to string (port from [#1021](https://github.com/omab/python-social-auth/pull/1021) by WarmongeR1) - Fix auth_params for Stripe backend (port from [#1034](https://github.com/omab/python-social-auth/pull/1034) by dchanm) - Preserve order of backends in BACKENDSCACHE (port from [#1004](https://github.com/omab/python-social-auth/pull/1004) by tsouvarev) - Don't lose custom exception message on raising AuthCanceled (port from [#1062](https://github.com/omab/python-social-auth/pull/1062) by dotsbb) - Fixed VK backend (port from [#1007](https://github.com/omab/python-social-auth/pull/1007) by DeKaN) - Updated Dropbox backend (port from [#1018](https://github.com/omab/python-social-auth/pull/1018) by illing2005) ## [0.0.1](https://github.com/python-social-auth/social-core/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-auth-core-3.1.0/requirements-python3.txt0000644000175000017500000000010513336542652021423 0ustar omabomab00000000000000defusedxml>=0.5.0rc1 python3-openid>=3.0.10 -r requirements-base.txt social-auth-core-3.1.0/requirements-saml-python3.txt0000644000175000017500000000002413336542652022355 0ustar omabomab00000000000000python3-saml>=1.2.1 social-auth-core-3.1.0/requirements-python2.txt0000644000175000017500000000005613336542652021427 0ustar omabomab00000000000000python-openid>=2.2.5 -r requirements-base.txt social-auth-core-3.1.0/PKG-INFO0000644000175000017500000000540013433256445015635 0ustar omabomab00000000000000Metadata-Version: 2.1 Name: social-auth-core Version: 3.1.0 Summary: Python social authentication made simple. Home-page: https://github.com/python-social-auth/social-core Author: Matias Aguirre Author-email: matiasaguirre@gmail.com License: BSD Description: # Python Social Auth - Core [![Build Status](https://travis-ci.org/python-social-auth/social-core.svg?branch=master)](https://travis-ci.org/python-social-auth/social-core) [![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) Python Social Auth is an easy to setup social authentication/registration mechanism with support for several frameworks and auth providers. ## Description This is the core component of the python-social-auth ecosystem, it implements the common interface to define new authentication backends to third parties services, implement integrations with web frameworks and storage solutions. ## Documentation Project documentation is available at http://python-social-auth.readthedocs.org/. ## Setup ```shell $ pip install social-auth-core ``` ## 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 maintened 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) Keywords: openid,oauth,saml,social auth Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Topic :: Internet Classifier: License :: OSI Approved :: BSD License Classifier: Intended Audience :: Developers Classifier: Environment :: Web Environment Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Provides-Extra: openidconnect Provides-Extra: saml Provides-Extra: allpy2 Provides-Extra: azuread Provides-Extra: allpy3 Provides-Extra: all social-auth-core-3.1.0/requirements-azuread.txt0000644000175000017500000000002413432613341021441 0ustar omabomab00000000000000cryptography>=2.1.1 social-auth-core-3.1.0/MANIFEST.in0000644000175000017500000000033013336542652016273 0ustar omabomab00000000000000global-include *.py include *.txt CHANGELOG.md LICENSE README.md social_core/tests/testkey.pem recursive-include social_core/tests *.txt graft examples recursive-exclude .tox * recursive-exclude social_core *.pyc social-auth-core-3.1.0/social_core/0000755000175000017500000000000013433256445017023 5ustar omabomab00000000000000social-auth-core-3.1.0/social_core/utils.py0000644000175000017500000002301613336542652020537 0ustar omabomab00000000000000import re import sys import time import unicodedata import collections import functools import logging import six import requests import social_core from six.moves.urllib_parse import urlparse, urlunparse, urlencode, \ parse_qs as battery_parse_qs from requests.adapters import HTTPAdapter from requests.packages.urllib3.poolmanager import PoolManager from .exceptions import AuthCanceled, AuthForbidden, AuthUnreachableProvider SETTING_PREFIX = 'SOCIAL_AUTH' PARTIAL_TOKEN_SESSION_NAME = 'partial_pipeline_token' social_logger = logging.getLogger('social') class SSLHttpAdapter(HTTPAdapter): """" Transport adapter that allows to use any SSL protocol. Based on: http://requests.rtfd.org/latest/user/advanced/#example-specific-ssl-version """ def __init__(self, ssl_protocol): self.ssl_protocol = ssl_protocol super(SSLHttpAdapter, self).__init__() def init_poolmanager(self, connections, maxsize, block=False): self.poolmanager = PoolManager( num_pools=connections, maxsize=maxsize, block=block, ssl_version=self.ssl_protocol ) @classmethod def ssl_adapter_session(cls, ssl_protocol): session = requests.Session() session.mount('https://', SSLHttpAdapter(ssl_protocol)) return session def import_module(name): __import__(name) return sys.modules[name] def module_member(name): mod, member = name.rsplit('.', 1) module = import_module(mod) return getattr(module, member) def user_agent(): """Builds a simple User-Agent string to send in requests""" return 'social-auth-' + social_core.__version__ def url_add_parameters(url, params): """Adds parameters to URL, parameter will be repeated if already present""" if params: fragments = list(urlparse(url)) value = parse_qs(fragments[4]) value.update(params) fragments[4] = urlencode(value) url = urlunparse(fragments) return url def to_setting_name(*names): return '_'.join([name.upper().replace('-', '_') for name in names if name]) def setting_name(*names): return to_setting_name(*((SETTING_PREFIX,) + names)) def sanitize_redirect(hosts, redirect_to): """ Given a list of hostnames and an untrusted URL to redirect to, this method tests it to make sure it isn't garbage/harmful and returns it, else returns None, similar as how's it done on django.contrib.auth.views. """ # Avoid redirect on evil URLs like ///evil.com if not redirect_to or not hasattr(redirect_to, 'startswith') or \ redirect_to.startswith('///'): return None try: # Don't redirect to a host that's not in the list netloc = urlparse(redirect_to)[1] or hosts[0] except (TypeError, AttributeError): pass else: if netloc in hosts: return redirect_to def user_is_authenticated(user): if user and hasattr(user, 'is_authenticated'): if isinstance(user.is_authenticated, collections.Callable): authenticated = user.is_authenticated() else: authenticated = user.is_authenticated elif user: authenticated = True else: authenticated = False return authenticated def user_is_active(user): if user and hasattr(user, 'is_active'): if isinstance(user.is_active, collections.Callable): is_active = user.is_active() else: is_active = user.is_active elif user: is_active = True else: is_active = False return is_active # This slugify version was borrowed from django revision a61dbd6 def slugify(value): """Converts to lowercase, removes non-word characters (alphanumerics and underscores) and converts spaces to hyphens. Also strips leading and trailing whitespace.""" value = unicodedata.normalize('NFKD', six.text_type(value)) \ .encode('ascii', 'ignore') \ .decode('ascii') value = re.sub(r'[^\w\s-]', '', value).strip().lower() return re.sub(r'[-\s]+', '-', value) def first(func, items): """Return the first item in the list for what func returns True""" for item in items: if func(item): return item def parse_qs(value): """Like urlparse.parse_qs but transform list values to single items""" return drop_lists(battery_parse_qs(value)) def drop_lists(value): out = {} for key, val in value.items(): val = val[0] if isinstance(key, six.binary_type): key = six.text_type(key, 'utf-8') if isinstance(val, six.binary_type): val = six.text_type(val, 'utf-8') out[key] = val return out def partial_pipeline_data(backend, user=None, partial_token=None, *args, **kwargs): request_data = backend.strategy.request_data() partial_argument_name = backend.setting('PARTIAL_PIPELINE_TOKEN_NAME', 'partial_token') partial_token = partial_token or \ request_data.get(partial_argument_name) or \ backend.strategy.session_get(PARTIAL_TOKEN_SESSION_NAME, None) if partial_token: partial = backend.strategy.partial_load(partial_token) partial_matches_request = False if partial and partial.backend == backend.name: partial_matches_request = True # Normally when resuming a pipeline, request_data will be empty. We # only need to check for a uid match if new data was provided (i.e. # if current request specifies the ID_KEY). if backend.ID_KEY in request_data: id_from_partial = partial.kwargs.get('uid') id_from_request = request_data.get(backend.ID_KEY) if id_from_partial != id_from_request: partial_matches_request = False if partial_matches_request: if user: # don't update user if it's None kwargs.setdefault('user', user) kwargs.setdefault('request', request_data) partial.extend_kwargs(kwargs) return partial else: backend.strategy.clean_partial_pipeline(partial_token) def build_absolute_uri(host_url, path=None): """Build absolute URI with given (optional) path""" path = path or '' if path.startswith('http://') or path.startswith('https://'): return path if host_url.endswith('/') and path.startswith('/'): path = path[1:] return host_url + path def constant_time_compare(val1, val2): """ Returns True if the two strings are equal, False otherwise. The time taken is independent of the number of characters that match. This code was borrowed from Django 1.5.4-final """ if len(val1) != len(val2): return False result = 0 if six.PY3 and isinstance(val1, bytes) and isinstance(val2, bytes): for x, y in zip(val1, val2): result |= x ^ y else: for x, y in zip(val1, val2): result |= ord(x) ^ ord(y) return result == 0 def is_url(value): return value and \ (value.startswith('http://') or value.startswith('https://') or value.startswith('/')) def setting_url(backend, *names): for name in names: if is_url(name): return name else: value = backend.setting(name) if is_url(value): return value def handle_http_errors(func): @functools.wraps(func) def wrapper(*args, **kwargs): try: return func(*args, **kwargs) except requests.HTTPError as err: if err.response.status_code == 400: raise AuthCanceled(args[0], response=err.response) elif err.response.status_code == 401: raise AuthForbidden(args[0]) elif err.response.status_code == 503: raise AuthUnreachableProvider(args[0]) else: raise return wrapper def append_slash(url): """Make sure we append a slash at the end of the URL otherwise we have issues with urljoin Example: >>> urlparse.urljoin('http://www.example.com/api/v3', 'user/1/') 'http://www.example.com/api/user/1/' """ if url and not url.endswith('/'): url = '{0}/'.format(url) return url def get_strategy(strategy, storage, *args, **kwargs): Strategy = module_member(strategy) Storage = module_member(storage) return Strategy(Storage, *args, **kwargs) class cache(object): """ Cache decorator that caches the return value of a method for a specified time. It maintains a cache per class, so subclasses have a different cache entry for the same cached method. Does not work for methods with arguments. """ def __init__(self, ttl): self.ttl = ttl self.cache = {} def __call__(self, fn): def wrapped(this): now = time.time() last_updated = None cached_value = None if this.__class__ in self.cache: last_updated, cached_value = self.cache[this.__class__] if not cached_value or now - last_updated > self.ttl: try: cached_value = fn(this) self.cache[this.__class__] = (now, cached_value) except: # Use previously cached value when call fails, if available if not cached_value: raise return cached_value return wrapped social-auth-core-3.1.0/social_core/store.py0000644000175000017500000000527013336542652020535 0ustar omabomab00000000000000import time try: import cPickle as pickle except ImportError: import pickle from openid.store.interface import OpenIDStore as BaseOpenIDStore from openid.store.nonce import SKEW class OpenIdStore(BaseOpenIDStore): """Storage class""" def __init__(self, strategy): """Init method""" super(OpenIdStore, self).__init__() self.strategy = strategy self.storage = strategy.storage self.assoc = self.storage.association self.nonce = self.storage.nonce self.max_nonce_age = 6 * 60 * 60 # Six hours def storeAssociation(self, server_url, association): """Store new assocition if doesn't exist""" self.assoc.store(server_url, association) def removeAssociation(self, server_url, handle): """Remove association""" associations_ids = list(dict(self.assoc.oids(server_url, handle)).keys()) if associations_ids: self.assoc.remove(associations_ids) def expiresIn(self, assoc): if hasattr(assoc, 'getExpiresIn'): return assoc.getExpiresIn() else: # python3-openid 3.0.2 return assoc.expiresIn def getAssociation(self, server_url, handle=None): """Return stored assocition""" associations, expired = [], [] for assoc_id, association in self.assoc.oids(server_url, handle): expires = self.expiresIn(association) if expires > 0: associations.append(association) elif expires == 0: expired.append(assoc_id) if expired: # clear expired associations self.assoc.remove(expired) if associations: # return most recet association return associations[0] def useNonce(self, server_url, timestamp, salt): """Generate one use number and return *if* it was created""" if abs(timestamp - time.time()) > SKEW: return False return self.nonce.use(server_url, timestamp, salt) class OpenIdSessionWrapper(dict): pickle_instances = ( '_yadis_services__openid_consumer_', '_openid_consumer_last_token' ) def __getitem__(self, name): value = super(OpenIdSessionWrapper, self).__getitem__(name) if name in self.pickle_instances: value = pickle.loads(value) return value def __setitem__(self, name, value): if name in self.pickle_instances: value = pickle.dumps(value, 0) super(OpenIdSessionWrapper, self).__setitem__(name, value) def get(self, name, default=None): try: return self[name] except KeyError: return default social-auth-core-3.1.0/social_core/strategy.py0000644000175000017500000001774513336542652021255 0ustar omabomab00000000000000import time import random import hashlib from .utils import setting_name, module_member, PARTIAL_TOKEN_SESSION_NAME from .store import OpenIdStore, OpenIdSessionWrapper from .pipeline import DEFAULT_AUTH_PIPELINE, DEFAULT_DISCONNECT_PIPELINE from .pipeline.utils import partial_load, partial_store, partial_prepare class BaseTemplateStrategy(object): def __init__(self, strategy): self.strategy = strategy def render(self, tpl=None, html=None, context=None): if not tpl and not html: raise ValueError('Missing template or html parameters') context = context or {} if tpl: return self.render_template(tpl, context) else: return self.render_string(html, context) def render_template(self, tpl, context): raise NotImplementedError('Implement in subclass') def render_string(self, html, context): raise NotImplementedError('Implement in subclass') class BaseStrategy(object): ALLOWED_CHARS = 'abcdefghijklmnopqrstuvwxyz' \ 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' \ '0123456789' DEFAULT_TEMPLATE_STRATEGY = BaseTemplateStrategy def __init__(self, storage=None, tpl=None): self.storage = storage self.tpl = (tpl or self.DEFAULT_TEMPLATE_STRATEGY)(self) def setting(self, name, default=None, backend=None): names = [setting_name(name), name] if backend: names.insert(0, setting_name(backend.name, name)) for name in names: try: return self.get_setting(name) except (AttributeError, KeyError): pass return default def create_user(self, *args, **kwargs): return self.storage.user.create_user(*args, **kwargs) def get_user(self, *args, **kwargs): return self.storage.user.get_user(*args, **kwargs) def session_setdefault(self, name, value): self.session_set(name, value) return self.session_get(name) def openid_session_dict(self, name): # Many frameworks are switching the session serialization from Pickle # to JSON to avoid code execution risks. Flask did this from Flask # 0.10, Django is switching to JSON by default from version 1.6. # # Sadly python-openid stores classes instances in the session which # fails the JSON serialization, the classes are: # # openid.yadis.manager.YadisServiceManager # openid.consumer.discover.OpenIDServiceEndpoint # # This method will return a wrapper over the session value used with # openid (a dict) which will automatically keep a pickled value for the # mentioned classes. return OpenIdSessionWrapper(self.session_setdefault(name, {})) def to_session_value(self, val): return val def from_session_value(self, val): return val def partial_save(self, next_step, backend, *args, **kwargs): return partial_store(self, backend, next_step, *args, **kwargs) def partial_prepare(self, next_step, backend, *args, **kwargs): return partial_prepare(self, backend, next_step, *args, **kwargs) def partial_load(self, token): return partial_load(self, token) def clean_partial_pipeline(self, token): self.storage.partial.destroy(token) self.session_pop(PARTIAL_TOKEN_SESSION_NAME) def openid_store(self): return OpenIdStore(self) def get_pipeline(self, backend=None): return self.setting('PIPELINE', DEFAULT_AUTH_PIPELINE, backend) def get_disconnect_pipeline(self, backend=None): return self.setting( 'DISCONNECT_PIPELINE', DEFAULT_DISCONNECT_PIPELINE, backend ) def random_string(self, length=12, chars=ALLOWED_CHARS): # Implementation borrowed from django 1.4 try: random.SystemRandom() except NotImplementedError: key = self.setting('SECRET_KEY', '') seed = '{0}{1}{2}'.format(random.getstate(), time.time(), key) random.seed(hashlib.sha256(seed.encode()).digest()) return ''.join([random.choice(chars) for i in range(length)]) def absolute_uri(self, path=None): uri = self.build_absolute_uri(path) if uri and self.setting('REDIRECT_IS_HTTPS'): uri = uri.replace('http://', 'https://') return uri def get_language(self): """Return current language""" return '' def send_email_validation(self, backend, email, partial_token=None): email_validation = self.setting('EMAIL_VALIDATION_FUNCTION') send_email = module_member(email_validation) code = self.storage.code.make_code(email) send_email(self, backend, code, partial_token) return code def validate_email(self, email, code): verification_code = self.storage.code.get_code(code) if not verification_code or verification_code.code != code: return False elif verification_code.email != email: return False elif verification_code.verified: return False else: verification_code.verify() return True def render_html(self, tpl=None, html=None, context=None): """Render given template or raw html with given context""" return self.tpl.render(tpl, html, context) def authenticate(self, backend, *args, **kwargs): """Trigger the authentication mechanism tied to the current framework""" kwargs['strategy'] = self kwargs['storage'] = self.storage kwargs['backend'] = backend args, kwargs = self.clean_authenticate_args(*args, **kwargs) return backend.authenticate(*args, **kwargs) def clean_authenticate_args(self, *args, **kwargs): """Take authenticate arguments and return a "cleaned" version of them""" return args, kwargs def get_backends(self): """Return configured backends""" return self.setting('AUTHENTICATION_BACKENDS', []) # Implement the following methods on strategies sub-classes def redirect(self, url): """Return a response redirect to the given URL""" raise NotImplementedError('Implement in subclass') def get_setting(self, name): """Return value for given setting name""" raise NotImplementedError('Implement in subclass') def html(self, content): """Return HTTP response with given content""" raise NotImplementedError('Implement in subclass') def request_data(self, merge=True): """Return current request data (POST or GET)""" raise NotImplementedError('Implement in subclass') def request_host(self): """Return current host value""" raise NotImplementedError('Implement in subclass') def session_get(self, name, default=None): """Return session value for given key""" raise NotImplementedError('Implement in subclass') def session_set(self, name, value): """Set session value for given key""" raise NotImplementedError('Implement in subclass') def session_pop(self, name): """Pop session value for given key""" raise NotImplementedError('Implement in subclass') def build_absolute_uri(self, path=None): """Build absolute URI with given (optional) path""" raise NotImplementedError('Implement in subclass') def request_is_secure(self): """Is the request using HTTPS?""" raise NotImplementedError('Implement in subclass') def request_path(self): """path of the current request""" raise NotImplementedError('Implement in subclass') def request_port(self): """Port in use for this request""" raise NotImplementedError('Implement in subclass') def request_get(self): """Request GET data""" raise NotImplementedError('Implement in subclass') def request_post(self): """Request POST data""" raise NotImplementedError('Implement in subclass') social-auth-core-3.1.0/social_core/backends/0000755000175000017500000000000013433256445020575 5ustar omabomab00000000000000social-auth-core-3.1.0/social_core/backends/mailru.py0000644000175000017500000000325613336542652022446 0ustar omabomab00000000000000""" Mail.ru OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/mailru.html """ from hashlib import md5 from six.moves.urllib_parse import unquote from .oauth import BaseOAuth2 class MailruOAuth2(BaseOAuth2): """Mail.ru authentication backend""" name = 'mailru-oauth2' ID_KEY = 'uid' AUTHORIZATION_URL = 'https://connect.mail.ru/oauth/authorize' ACCESS_TOKEN_URL = 'https://connect.mail.ru/oauth/token' ACCESS_TOKEN_METHOD = 'POST' EXTRA_DATA = [('refresh_token', 'refresh_token'), ('expires_in', 'expires')] def get_user_details(self, response): """Return user details from Mail.ru request""" fullname, first_name, last_name = self.get_user_names( first_name=unquote(response['first_name']), last_name=unquote(response['last_name']) ) return {'username': unquote(response['nick']), 'email': unquote(response['email']), 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token, *args, **kwargs): """Return user data from Mail.ru REST API""" key, secret = self.get_key_and_secret() data = {'method': 'users.getInfo', 'session_key': access_token, 'app_id': key, 'secure': '1'} param_list = sorted(list(item + '=' + data[item] for item in data)) data['sig'] = md5( (''.join(param_list) + secret).encode('utf-8') ).hexdigest() return self.get_json('http://www.appsmail.ru/platform/api', params=data)[0] social-auth-core-3.1.0/social_core/backends/linkedin.py0000644000175000017500000001044213336542652022745 0ustar omabomab00000000000000""" LinkedIn OAuth1 and OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/linkedin.html """ from .oauth import BaseOAuth1, BaseOAuth2 class BaseLinkedinAuth(object): EXTRA_DATA = [('id', 'id'), ('expires_in', 'expires'), ('first-name', 'first_name', True), ('last-name', 'last_name', True), ('firstName', 'first_name', True), ('lastName', 'last_name', True)] USER_DETAILS = 'https://api.linkedin.com/v1/people/~:({0})' def get_user_details(self, response): """Return user details from Linkedin account""" fullname, first_name, last_name = self.get_user_names( first_name=response['firstName'], last_name=response['lastName'] ) email = response.get('emailAddress', '') return {'username': first_name + last_name, 'fullname': fullname, 'first_name': first_name, 'last_name': last_name, 'email': email} def user_details_url(self): # use set() since LinkedIn fails when values are duplicated fields_selectors = list(set(['first-name', 'id', 'last-name'] + self.setting('FIELD_SELECTORS', []))) # user sort to ease the tests URL mocking fields_selectors.sort() fields_selectors = ','.join(fields_selectors) return self.USER_DETAILS.format(fields_selectors) def user_data_headers(self): lang = self.setting('FORCE_PROFILE_LANGUAGE') if lang: return { 'Accept-Language': lang if lang is not True else self.strategy.get_language() } class LinkedinOAuth(BaseLinkedinAuth, BaseOAuth1): """Linkedin OAuth authentication backend""" name = 'linkedin' SCOPE_SEPARATOR = '+' AUTHORIZATION_URL = 'https://www.linkedin.com/uas/oauth/authenticate' REQUEST_TOKEN_URL = 'https://api.linkedin.com/uas/oauth/requestToken' ACCESS_TOKEN_URL = 'https://api.linkedin.com/uas/oauth/accessToken' def user_data(self, access_token, *args, **kwargs): """Return user data provided""" return self.get_json( self.user_details_url(), params={'format': 'json'}, auth=self.oauth_auth(access_token), headers=self.user_data_headers() ) def unauthorized_token(self): """Makes first request to oauth. Returns an unauthorized Token.""" scope = self.get_scope() or '' if scope: scope = '?scope=' + self.SCOPE_SEPARATOR.join(scope) return self.request(self.REQUEST_TOKEN_URL + scope, params=self.request_token_extra_arguments(), auth=self.oauth_auth()).text class LinkedinOAuth2(BaseLinkedinAuth, BaseOAuth2): name = 'linkedin-oauth2' SCOPE_SEPARATOR = ' ' AUTHORIZATION_URL = 'https://www.linkedin.com/uas/oauth2/authorization' ACCESS_TOKEN_URL = 'https://www.linkedin.com/uas/oauth2/accessToken' ACCESS_TOKEN_METHOD = 'POST' REDIRECT_STATE = False def user_data(self, access_token, *args, **kwargs): headers = self.user_data_headers() or {} headers['Authorization'] = 'Bearer {access_token}'.format( access_token=access_token ) return self.get_json( self.user_details_url(), params={'format': 'json'}, headers=headers ) def request_access_token(self, *args, **kwargs): # LinkedIn expects a POST request with querystring parameters, despite # the spec http://tools.ietf.org/html/rfc6749#section-4.1.3 kwargs['params'] = kwargs.pop('data') return super(LinkedinOAuth2, self).request_access_token( *args, **kwargs ) class LinkedinMobileOAuth2(LinkedinOAuth2): name = 'linkedin-mobile-oauth2' def user_data(self, access_token, *args, **kwargs): headers = self.user_data_headers() if not headers: headers = {} headers['Authorization'] = 'Bearer ' + access_token headers['x-li-src'] = 'msdk' return self.get_json( self.user_details_url(), params={'format': 'json'}, headers=headers ) social-auth-core-3.1.0/social_core/backends/suse.py0000644000175000017500000000071613336542652022132 0ustar omabomab00000000000000""" Open Suse OpenId backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/suse.html """ from .open_id import OpenIdAuth class OpenSUSEOpenId(OpenIdAuth): name = 'opensuse' URL = 'https://www.opensuse.org/openid/user/' def get_user_id(self, details, response): """ Return user unique id provided by service. For openSUSE the nickname is original. """ return details['nickname'] social-auth-core-3.1.0/social_core/backends/pixelpin.py0000644000175000017500000000242213336542652022777 0ustar omabomab00000000000000import json from .open_id_connect import OpenIdConnectAuth class PixelPinOpenIDConnect(OpenIdConnectAuth): """PixelPin OpenID Connect authentication backend""" name = 'pixelpin-openidconnect' ID_KEY = 'sub' AUTHORIZATION_URL = 'https://login.pixelpin.io/connect/authorize' ACCESS_TOKEN_URL = 'https://login.pixelpin.io/connect/token' OIDC_ENDPOINT = 'https://login.pixelpin.io' JWKS_URI = 'https://login.pixelpin.io/.well-known/jwks' ACCESS_TOKEN_METHOD = 'POST' REQUIRES_EMAIL_VALIDATION = False def get_user_details(self, response): """Return user details from PixelPin account""" first_name = response.get('given_name') last_name = response.get('family_name') sub = response.get('sub') username = first_name + last_name + sub return {'username': username, 'email': response.get('email'), 'fullname': first_name + ' ' + last_name, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token, *args, **kwargs): return self.get_json( 'https://login.pixelpin.io/connect/userinfo', headers={ 'Authorization': 'Bearer {0}'.format(access_token) } ) social-auth-core-3.1.0/social_core/backends/ubuntu.py0000644000175000017500000000056213336542652022474 0ustar omabomab00000000000000""" Ubuntu One OpenId backend """ from .open_id import OpenIdAuth class UbuntuOpenId(OpenIdAuth): name = 'ubuntu' URL = 'https://login.ubuntu.com' def get_user_id(self, details, response): """ Return user unique id provided by service. For Ubuntu One the nickname should be original. """ return details['nickname'] social-auth-core-3.1.0/social_core/backends/loginradius.py0000644000175000017500000000507413336542652023475 0ustar omabomab00000000000000""" LoginRadius BaseOAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/loginradius.html """ from .oauth import BaseOAuth2 class LoginRadiusAuth(BaseOAuth2): """LoginRadius BaseOAuth2 authentication backend.""" name = 'loginradius' ID_KEY = 'ID' ACCESS_TOKEN_URL = 'https://api.loginradius.com/api/v2/access_token' PROFILE_URL = 'https://api.loginradius.com/api/v2/userprofile' ACCESS_TOKEN_METHOD = 'GET' REDIRECT_STATE = False STATE_PARAMETER = False def uses_redirect(self): """Return False because we return HTML instead.""" return False def auth_html(self): key, secret = self.get_key_and_secret() tpl = self.setting('TEMPLATE', 'loginradius.html') return self.strategy.render_html(tpl=tpl, context={ 'backend': self, 'LOGINRADIUS_KEY': key, 'LOGINRADIUS_REDIRECT_URL': self.get_redirect_uri() }) def request_access_token(self, *args, **kwargs): return self.get_json(params={ 'token': self.data.get('token'), 'secret': self.setting('SECRET') }, *args, **kwargs) def user_data(self, access_token, *args, **kwargs): """Loads user data from service. Implement in subclass.""" return self.get_json( self.PROFILE_URL, params={'access_token': access_token}, data=self.auth_complete_params(self.validate_state()), headers=self.auth_headers(), method=self.ACCESS_TOKEN_METHOD ) def get_user_details(self, response): """Must return user details in a know internal struct: {'username': , 'email': , 'fullname': , 'first_name': , 'last_name': } """ profile = { 'username': response['NickName'] or '', 'email': response['Email'][0]['Value'] or '', 'fullname': response['FullName'] or '', 'first_name': response['FirstName'] or '', 'last_name': response['LastName'] or '' } return profile def get_user_id(self, details, response): """Return a unique ID for the current user, by default from server response. Since LoginRadius handles multiple providers, we need to distinguish them to prevent conflicts.""" return '{0}-{1}'.format(response.get('Provider'), response.get(self.ID_KEY)) social-auth-core-3.1.0/social_core/backends/evernote.py0000644000175000017500000000524613336542652023005 0ustar omabomab00000000000000""" Evernote OAuth1 backend (with sandbox mode support), docs at: https://python-social-auth.readthedocs.io/en/latest/backends/evernote.html """ from requests import HTTPError from ..exceptions import AuthCanceled from .oauth import BaseOAuth1 class EvernoteOAuth(BaseOAuth1): """ Evernote OAuth authentication backend. Possible Values: {'edam_expires': ['1367525289541'], 'edam_noteStoreUrl': [ 'https://sandbox.evernote.com/shard/s1/notestore' ], 'edam_shard': ['s1'], 'edam_userId': ['123841'], 'edam_webApiUrlPrefix': ['https://sandbox.evernote.com/shard/s1/'], 'oauth_token': [ 'S=s1:U=1e3c1:E=13e66dbee45:C=1370f2ac245:P=185:A=my_user:' \ 'H=411443c5e8b20f8718ed382a19d4ae38' ]} """ name = 'evernote' ID_KEY = 'edam_userId' AUTHORIZATION_URL = 'https://www.evernote.com/OAuth.action' REQUEST_TOKEN_URL = 'https://www.evernote.com/oauth' ACCESS_TOKEN_URL = 'https://www.evernote.com/oauth' EXTRA_DATA = [ ('access_token', 'access_token'), ('oauth_token', 'oauth_token'), ('edam_noteStoreUrl', 'store_url'), ('edam_expires', 'expires') ] def get_user_details(self, response): """Return user details from Evernote account""" return {'username': response['edam_userId'], 'email': ''} def access_token(self, token): """Return request for access token value""" try: return self.get_querystring(self.ACCESS_TOKEN_URL, auth=self.oauth_auth(token)) except HTTPError as err: # Evernote returns a 401 error when AuthCanceled if err.response.status_code == 401: raise AuthCanceled(self, response=err.response) else: raise def extra_data(self, user, uid, response, details=None, *args, **kwargs): data = super(EvernoteOAuth, self).extra_data(user, uid, response, details, *args, **kwargs) # Evernote returns expiration timestamp in milliseconds, so it needs to # be normalized. if 'expires' in data: data['expires'] = int(data['expires']) / 1000 return data def user_data(self, access_token, *args, **kwargs): """Return user data provided""" return access_token.copy() class EvernoteSandboxOAuth(EvernoteOAuth): name = 'evernote-sandbox' AUTHORIZATION_URL = 'https://sandbox.evernote.com/OAuth.action' REQUEST_TOKEN_URL = 'https://sandbox.evernote.com/oauth' ACCESS_TOKEN_URL = 'https://sandbox.evernote.com/oauth' social-auth-core-3.1.0/social_core/backends/keycloak.py0000644000175000017500000001075213336542652022756 0ustar omabomab00000000000000import jwt from social_core.backends.oauth import BaseOAuth2 class KeycloakOAuth2(BaseOAuth2): # pylint: disable=abstract-method """Keycloak OAuth2 authentication backend This backend has been tested working with a standard Keycloak installation, but you might have to specialize it and tune the parameters per your configuration. This setup specializes the OAuth2 backend which, strictly speaking, offers authorization without authentication capabilities. Keycloak does offer a full OpenID Connect implementation, but the implementation is rather labor intensive to implement. This backend is configured to get an access token instead, and assume that the access token contains the necessary user details for authentication. The integrity of the authentication process is followed by public key verification for the `access_token` along with OpenID Connect specification `aud` field checking. To set up, please take the following steps: 1. Create a new Keycloak client in the Clients section. 2. Configure the following parameters in the Client setup: Settings > Client ID (copy to settings as `KEY` value) Credentials > Client Authenticator > Secret (copy to settings as `SECRET` value) 3. For the tokens to work with the JWT setup the following configuration has to be made in Keycloak: Settings > Access Type > confidential Settings > Fine Grain OpenID Connect Configuration > User Info Signed Response Algorithm > RS256 Settings > Fine Grain OpenID Connect Configuration > Request Object Signature Algorithm > RS256 4. Get the public key (copy to settings as `PUBLIC_KEY` value) to be used with the backend: Realm Settings > Keys > Public key 5. Configure access token fields are configured via the Keycloak Client mappers: Clients > Client ID > Mappers They have to include at least the `ID_KEY` value and the dictionary keys defined in the `get_user_details` method. 6. Configure your web backend. Example setting values for Django settings could be: SOCIAL_AUTH_KEYCLOAK_KEY = 'example' SOCIAL_AUTH_KEYCLOAK_SECRET = '1234abcd-1234-abcd-1234-abcd1234adcd' SOCIAL_AUTH_KEYCLOAK_PUBLIC_KEY = 'pempublickeythatis2048bitsinbase64andhaseg392characters' SOCIAL_AUTH_KEYCLOAK_AUTHORIZATION_URL = 'https://sso.example.com/auth/realms/example/protocol/openid-connect/auth' SOCIAL_AUTH_KEYCLOAK_ACCESS_TOKEN_URL = 'https://sso.example.com/auth/realms/example/protocol/openid-connect/token' 7. The default behaviour is to associate users via username field, but you can change the key with e.g. SOCIAL_AUTH_KEYCLOAK_ID_KEY = 'email' Please make sure your Keycloak user database and Django user database do not conflict and that there is no risk of user account hijacking by false account association. """ name = 'keycloak' ID_KEY = 'username' ACCESS_TOKEN_METHOD = 'POST' def authorization_url(self): return self.setting('AUTHORIZATION_URL') def access_token_url(self): return self.setting('ACCESS_TOKEN_URL') def audience(self): return self.setting('KEY') def algorithm(self): return self.setting('ALGORITHM', default='RS256') def public_key(self): return '\n'.join([ '-----BEGIN PUBLIC KEY-----', self.setting('PUBLIC_KEY'), '-----END PUBLIC KEY-----', ]) def user_data(self, access_token, *args, **kwargs): # pylint: disable=unused-argument """Decode user data from the access_token You can specialize this method to e.g. get information from the Keycloak backend if you do not want to include the user information in the access_token. """ return jwt.decode( access_token, key=self.public_key(), algorithms=self.algorithm(), audience=self.audience(), ) def get_user_details(self, response): """Map fields in user_data into Django User fields """ return { 'username': response.get('preferred_username'), 'email': response.get('email'), 'fullname': response.get('name'), 'first_name': response.get('given_name'), 'last_name': response.get('family_name'), } def get_user_id(self, details, response): """Get and associate Django User by the field indicated by ID_KEY """ return details.get(self.ID_KEY) social-auth-core-3.1.0/social_core/backends/mediawiki.py0000644000175000017500000001374713374653300023121 0ustar omabomab00000000000000""" MediaWiki OAuth1 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/mediawiki.html """ import re import time import six import requests import jwt from six import b from six.moves.urllib.parse import parse_qs, urlencode, urlparse from requests_oauthlib import OAuth1 from .oauth import BaseOAuth1 from ..exceptions import AuthException def force_unicode(value): """ Return string in unicode. """ if isinstance(value, six.text_type): return value else: if six.PY3: return str(value, "unicode-escape") else: return unicode(value, "unicode-escape") class MediaWiki(BaseOAuth1): """ Handles the handshake with Mediawiki and fetching of user data. """ name = 'mediawiki' MEDIAWIKI_URL = 'https://meta.wikimedia.org/w/index.php' SOCIAL_AUTH_MEDIAWIKI_CALLBACK = 'oob' LEEWAY = 10.0 def unauthorized_token(self): """ Return request for unauthorized token (first stage) Mediawiki request token is requested from e.g.: * https://en.wikipedia.org/w/index.php?title=Special:OAuth/initiate """ params = self.request_token_extra_arguments() params.update(self.get_scope_argument()) params['title'] = 'Special:OAuth/initiate' key, secret = self.get_key_and_secret() decoding = None if six.PY3 else 'utf-8' response = self.request( self.setting('MEDIAWIKI_URL'), params=params, auth=OAuth1( key, secret, callback_uri=self.setting('CALLBACK'), decoding=decoding ), method=self.REQUEST_TOKEN_METHOD ) if response.content.decode().startswith('Error'): raise AuthException(self, response.content.decode()) return response.content.decode() def oauth_authorization_request(self, token): """ Generates the URL for the authorization link """ if not isinstance(token, dict): token = parse_qs(token) oauth_token = token.get(self.OAUTH_TOKEN_PARAMETER_NAME)[0] state = self.get_or_create_state() base_url = self.setting('MEDIAWIKI_URL') return '{0}?{1}'.format(base_url, urlencode({ 'title': 'Special:Oauth/authenticate', self.OAUTH_TOKEN_PARAMETER_NAME: oauth_token, self.REDIRECT_URI_PARAMETER_NAME: self.get_redirect_uri(state) })) def access_token(self, token): """ Fetches the Mediawiki access token. """ auth_token = self.oauth_auth(token) response = requests.post( url=self.setting('MEDIAWIKI_URL'), params={'title': 'Special:Oauth/token'}, auth=auth_token ) credentials = parse_qs(response.content) oauth_token_key = credentials.get(b('oauth_token'))[0] oauth_token_secret = credentials.get(b('oauth_token_secret'))[0] oauth_token_key = oauth_token_key.decode() oauth_token_secret = oauth_token_secret.decode() return { 'oauth_token': oauth_token_key, 'oauth_token_secret': oauth_token_secret } def get_user_details(self, response): """ Gets the user details from Special:OAuth/identify """ key, secret = self.get_key_and_secret() access_token = response['access_token'] auth = OAuth1(key, client_secret=secret, resource_owner_key=access_token['oauth_token'], resource_owner_secret=access_token['oauth_token_secret']) req_resp = requests.post(url=self.setting('MEDIAWIKI_URL'), params={'title': 'Special:OAuth/identify'}, auth=auth) try: identity = jwt.decode(req_resp.content, secret, audience=key, algorithms=['HS256'], leeway=self.LEEWAY) except jwt.InvalidTokenError as exception: raise AuthException( self, 'An error occurred while trying to read json ' + 'content: {0}'.format(exception) ) issuer = urlparse(identity['iss']).netloc expected_domain = urlparse(self.setting('MEDIAWIKI_URL')).netloc if not issuer == expected_domain: raise AuthException( self, 'Unexpected issuer {0}, expected {1}'.format( issuer, expected_domain ) ) now = time.time() issued_at = float(identity['iat']) if not now >= (issued_at - self.LEEWAY): raise AuthException( self, 'Identity issued {0} seconds in the future'.format( issued_at - now ) ) authorization_header = force_unicode( req_resp.request.headers['Authorization'] ) request_nonce = re.search(r'oauth_nonce="(.*?)"', authorization_header).group(1) if identity['nonce'] != request_nonce: raise AuthException( self, 'Replay attack detected: {0} != {1}'.format( identity['nonce'], request_nonce ) ) return { 'username': identity['username'], 'userID': identity['sub'], 'email': identity.get('email'), 'confirmed_email': identity.get('confirmed_email'), 'editcount': identity.get('editcount'), 'rights': identity.get('rights'), 'groups': identity.get('groups'), 'registered': identity.get('registered'), 'blocked': identity.get('blocked') } def get_user_id(self, details, response): """ Get the unique Mediawiki user ID. """ return details['userID'] social-auth-core-3.1.0/social_core/backends/stocktwits.py0000644000175000017500000000252013336542652023364 0ustar omabomab00000000000000""" Stocktwits OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/stocktwits.html """ from .oauth import BaseOAuth2 class StocktwitsOAuth2(BaseOAuth2): """Stockwiths OAuth2 backend""" name = 'stocktwits' AUTHORIZATION_URL = 'https://api.stocktwits.com/api/2/oauth/authorize' ACCESS_TOKEN_URL = 'https://api.stocktwits.com/api/2/oauth/token' ACCESS_TOKEN_METHOD = 'POST' SCOPE_SEPARATOR = ',' DEFAULT_SCOPE = ['read', 'publish_messages', 'publish_watch_lists', 'follow_users', 'follow_stocks'] def get_user_id(self, details, response): return response['user']['id'] def get_user_details(self, response): """Return user details from Stocktwits account""" fullname, first_name, last_name = self.get_user_names( response['user']['name'] ) return {'username': response['user']['username'], 'email': '', # not supplied 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" return self.get_json( 'https://api.stocktwits.com/api/2/account/verify.json', params={'access_token': access_token} ) social-auth-core-3.1.0/social_core/backends/docker.py0000644000175000017500000000312213336542652022414 0ustar omabomab00000000000000""" Docker Hub OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/docker.html """ from .oauth import BaseOAuth2 class DockerOAuth2(BaseOAuth2): name = 'docker' ID_KEY = 'user_id' AUTHORIZATION_URL = 'https://hub.docker.com/api/v1.1/o/authorize/' ACCESS_TOKEN_URL = 'https://hub.docker.com/api/v1.1/o/token/' REFRESH_TOKEN_URL = 'https://hub.docker.com/api/v1.1/o/token/' ACCESS_TOKEN_METHOD = 'POST' REDIRECT_STATE = False EXTRA_DATA = [ ('refresh_token', 'refresh_token', True), ('user_id', 'user_id'), ('email', 'email'), ('full_name', 'fullname'), ('location', 'location'), ('url', 'url'), ('company', 'company'), ('gravatar_email', 'gravatar_email'), ] def get_user_details(self, response): """Return user details from Docker Hub account""" fullname, first_name, last_name = self.get_user_names( response.get('full_name') or response.get('username') or '' ) return { 'username': response.get('username'), 'fullname': fullname, 'first_name': first_name, 'last_name': last_name, 'email': response.get('email', '') } def user_data(self, access_token, *args, **kwargs): """Grab user profile information from Docker Hub.""" username = kwargs['response']['username'] return self.get_json( 'https://hub.docker.com/api/v1.1/users/%s/' % username, headers={'Authorization': 'Bearer %s' % access_token} ) social-auth-core-3.1.0/social_core/backends/openstreetmap.py0000644000175000017500000000356613336542652024047 0ustar omabomab00000000000000""" OpenStreetMap OAuth support. This adds support for OpenStreetMap OAuth service. An application must be registered first on OpenStreetMap and the settings SOCIAL_AUTH_OPENSTREETMAP_KEY and SOCIAL_AUTH_OPENSTREETMAP_SECRET must be defined with the corresponding values. More info: https://wiki.openstreetmap.org/wiki/OAuth """ from xml.dom import minidom from .oauth import BaseOAuth1 class OpenStreetMapOAuth(BaseOAuth1): """OpenStreetMap OAuth authentication backend""" name = 'openstreetmap' AUTHORIZATION_URL = 'https://www.openstreetmap.org/oauth/authorize' REQUEST_TOKEN_URL = 'https://www.openstreetmap.org/oauth/request_token' ACCESS_TOKEN_URL = 'https://www.openstreetmap.org/oauth/access_token' EXTRA_DATA = [ ('id', 'id'), ('avatar', 'avatar'), ('account_created', 'account_created') ] def get_user_details(self, response): """Return user details from OpenStreetMap account""" return { 'username': response['username'], 'email': '', 'fullname': '', 'first_name': '', 'last_name': '' } def user_data(self, access_token, *args, **kwargs): """Return user data provided""" response = self.oauth_request( access_token, 'https://api.openstreetmap.org/api/0.6/user/details' ) try: dom = minidom.parseString(response.content) except ValueError: return None user = dom.getElementsByTagName('user')[0] try: avatar = dom.getElementsByTagName('img')[0].getAttribute('href') except IndexError: avatar = None return { 'id': user.getAttribute('id'), 'username': user.getAttribute('display_name'), 'account_created': user.getAttribute('account_created'), 'avatar': avatar } social-auth-core-3.1.0/social_core/backends/open_id.py0000644000175000017500000002461213336542652022571 0ustar omabomab00000000000000import datetime from calendar import timegm import six from openid.consumer.consumer import Consumer, SUCCESS, CANCEL, FAILURE from openid.consumer.discover import DiscoveryFailure from openid.extensions import sreg, ax, pape from ..utils import url_add_parameters, cache from .base import BaseAuth from .oauth import BaseOAuth2 from ..exceptions import AuthException, AuthFailed, AuthCanceled, \ AuthUnknownError, AuthMissingParameter, \ AuthTokenError # OpenID configuration OLD_AX_ATTRS = [ ('http://schema.openid.net/contact/email', 'old_email'), ('http://schema.openid.net/namePerson', 'old_fullname'), ('http://schema.openid.net/namePerson/friendly', 'old_nickname') ] AX_SCHEMA_ATTRS = [ # Request both the full name and first/last components since some # providers offer one but not the other. ('http://axschema.org/contact/email', 'email'), ('http://axschema.org/namePerson', 'fullname'), ('http://axschema.org/namePerson/first', 'first_name'), ('http://axschema.org/namePerson/last', 'last_name'), ('http://axschema.org/namePerson/friendly', 'nickname'), ] SREG_ATTR = [ ('email', 'email'), ('fullname', 'fullname'), ('nickname', 'nickname') ] OPENID_ID_FIELD = 'openid_identifier' SESSION_NAME = 'openid' class OpenIdAuth(BaseAuth): """Generic OpenID authentication backend""" name = 'openid' URL = None USERNAME_KEY = 'username' def get_user_id(self, details, response): """Return user unique id provided by service""" return response.identity_url def get_ax_attributes(self): attrs = self.setting('AX_SCHEMA_ATTRS', []) if attrs and self.setting('IGNORE_DEFAULT_AX_ATTRS', True): return attrs return attrs + AX_SCHEMA_ATTRS + OLD_AX_ATTRS def get_sreg_attributes(self): return self.setting('SREG_ATTR') or SREG_ATTR def values_from_response(self, response, sreg_names=None, ax_names=None): """Return values from SimpleRegistration response or AttributeExchange response if present. @sreg_names and @ax_names must be a list of name and aliases for such name. The alias will be used as mapping key. """ values = {} # Use Simple Registration attributes if provided if sreg_names: resp = sreg.SRegResponse.fromSuccessResponse(response) if resp: values.update((alias, resp.get(name) or '') for name, alias in sreg_names) # Use Attribute Exchange attributes if provided if ax_names: resp = ax.FetchResponse.fromSuccessResponse(response) if resp: for src, alias in ax_names: name = alias.replace('old_', '') values[name] = resp.getSingle(src, '') or values.get(name) return values def get_user_details(self, response): """Return user details from an OpenID request""" values = {'username': '', 'email': '', 'fullname': '', 'first_name': '', 'last_name': ''} # update values using SimpleRegistration or AttributeExchange # values values.update(self.values_from_response( response, self.get_sreg_attributes(), self.get_ax_attributes() )) fullname = values.get('fullname') or '' first_name = values.get('first_name') or '' last_name = values.get('last_name') or '' email = values.get('email') or '' if not fullname and first_name and last_name: fullname = first_name + ' ' + last_name elif fullname: try: first_name, last_name = fullname.rsplit(' ', 1) except ValueError: last_name = fullname username_key = self.setting('USERNAME_KEY') or self.USERNAME_KEY values.update({'fullname': fullname, 'first_name': first_name, 'last_name': last_name, 'username': values.get(username_key) or (first_name.title() + last_name.title()), 'email': email}) return values def extra_data(self, user, uid, response, details=None, *args, **kwargs): """Return defined extra data names to store in extra_data field. Settings will be inspected to get more values names that should be stored on extra_data field. Setting name is created from current backend name (all uppercase) plus _SREG_EXTRA_DATA and _AX_EXTRA_DATA because values can be returned by SimpleRegistration or AttributeExchange schemas. Both list must be a value name and an alias mapping similar to SREG_ATTR, OLD_AX_ATTRS or AX_SCHEMA_ATTRS """ sreg_names = self.setting('SREG_EXTRA_DATA') ax_names = self.setting('AX_EXTRA_DATA') values = self.values_from_response(response, sreg_names, ax_names) from_details = super(OpenIdAuth, self).extra_data( user, uid, {}, details, *args, **kwargs ) values.update(from_details) return values def auth_url(self): """Return auth URL returned by service""" openid_request = self.setup_request(self.auth_extra_arguments()) # Construct completion URL, including page we should redirect to return_to = self.strategy.absolute_uri(self.redirect_uri) return openid_request.redirectURL(self.trust_root(), return_to) def auth_html(self): """Return auth HTML returned by service""" openid_request = self.setup_request(self.auth_extra_arguments()) return_to = self.strategy.absolute_uri(self.redirect_uri) form_tag = {'id': 'openid_message'} return openid_request.htmlMarkup(self.trust_root(), return_to, form_tag_attrs=form_tag) def trust_root(self): """Return trust-root option""" return self.setting('OPENID_TRUST_ROOT') or \ self.strategy.absolute_uri('/') def continue_pipeline(self, partial): """Continue previous halted pipeline""" response = self.consumer().complete(dict(self.data.items()), self.strategy.absolute_uri( self.redirect_uri )) return self.strategy.authenticate(self, response=response, pipeline_index=partial.next_step, *partial.args, **partial.kwargs) def auth_complete(self, *args, **kwargs): """Complete auth process""" response = self.consumer().complete(dict(self.data.items()), self.strategy.absolute_uri( self.redirect_uri )) self.process_error(response) return self.strategy.authenticate(self, response=response, *args, **kwargs) def process_error(self, data): if not data: raise AuthException(self, 'OpenID relying party endpoint') elif data.status == FAILURE: raise AuthFailed(self, data.message) elif data.status == CANCEL: raise AuthCanceled(self) elif data.status != SUCCESS: raise AuthUnknownError(self, data.status) def setup_request(self, params=None): """Setup request""" request = self.openid_request(params) # Request some user details. Use attribute exchange if provider # advertises support. if request.endpoint.supportsType(ax.AXMessage.ns_uri): fetch_request = ax.FetchRequest() # Mark all attributes as required, Google ignores optional ones for attr, alias in self.get_ax_attributes(): fetch_request.add(ax.AttrInfo(attr, alias=alias, required=True)) else: fetch_request = sreg.SRegRequest( optional=list(dict(self.get_sreg_attributes()).keys()) ) request.addExtension(fetch_request) # Add PAPE Extension for if configured preferred_policies = self.setting( 'OPENID_PAPE_PREFERRED_AUTH_POLICIES' ) preferred_level_types = self.setting( 'OPENID_PAPE_PREFERRED_AUTH_LEVEL_TYPES' ) max_age = self.setting('OPENID_PAPE_MAX_AUTH_AGE') if max_age is not None: try: max_age = int(max_age) except (ValueError, TypeError): max_age = None if max_age is not None or preferred_policies or preferred_level_types: pape_request = pape.Request( max_auth_age=max_age, preferred_auth_policies=preferred_policies, preferred_auth_level_types=preferred_level_types ) request.addExtension(pape_request) return request def consumer(self): """Create an OpenID Consumer object for the given Django request.""" if not hasattr(self, '_consumer'): self._consumer = self.create_consumer(self.strategy.openid_store()) return self._consumer def create_consumer(self, store=None): return Consumer(self.strategy.openid_session_dict(SESSION_NAME), store) def uses_redirect(self): """Return true if openid request will be handled with redirect or HTML content will be returned. """ return self.openid_request().shouldSendRedirect() def openid_request(self, params=None): """Return openid request""" try: return self.consumer().begin(url_add_parameters(self.openid_url(), params)) except DiscoveryFailure as err: raise AuthException(self, 'OpenID discovery error: {0}'.format( err )) def openid_url(self): """Return service provider URL. This base class is generic accepting a POST parameter that specifies provider URL.""" if self.URL: return self.URL elif OPENID_ID_FIELD in self.data: return self.data[OPENID_ID_FIELD] else: raise AuthMissingParameter(self, OPENID_ID_FIELD) social-auth-core-3.1.0/social_core/backends/drip.py0000644000175000017500000000151513336542652022107 0ustar omabomab00000000000000""" Drip OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/drip.html """ from .oauth import BaseOAuth2 class DripOAuth(BaseOAuth2): name = 'drip' AUTHORIZATION_URL = 'https://www.getdrip.com/oauth/authorize' ACCESS_TOKEN_URL = 'https://www.getdrip.com/oauth/token' ACCESS_TOKEN_METHOD = 'POST' def get_user_id(self, details, response): return details['email'] def get_user_details(self, response): return {'email': response['users'][0]['email'], 'fullname': response['users'][0]['name'], 'username': response['users'][0]['email']} def user_data(self, access_token, *args, **kwargs): return self.get_json('https://api.getdrip.com/v2/user', headers={ 'Authorization': 'Bearer %s' % access_token }) social-auth-core-3.1.0/social_core/backends/instagram.py0000644000175000017500000000403013336542652023131 0ustar omabomab00000000000000""" Instagram OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/instagram.html """ import hmac from hashlib import sha256 from .oauth import BaseOAuth2 class InstagramOAuth2(BaseOAuth2): name = 'instagram' AUTHORIZATION_URL = 'https://api.instagram.com/oauth/authorize' ACCESS_TOKEN_URL = 'https://api.instagram.com/oauth/access_token' ACCESS_TOKEN_METHOD = 'POST' def get_user_id(self, details, response): # Sometimes Instagram returns 'user', sometimes 'data', but API docs # says 'data' http://instagram.com/developer/endpoints/users/#get_users user = response.get('user') or response.get('data') or {} return user.get('id') def get_user_details(self, response): """Return user details from Instagram account""" # Sometimes Instagram returns 'user', sometimes 'data', but API docs # says 'data' http://instagram.com/developer/endpoints/users/#get_users user = response.get('user') or response.get('data') or {} username = user['username'] email = user.get('email', '') fullname, first_name, last_name = self.get_user_names( user.get('full_name', '') ) return {'username': username, 'fullname': fullname, 'first_name': first_name, 'last_name': last_name, 'email': email} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" key, secret = self.get_key_and_secret() params = {'access_token': access_token} sig = self._generate_sig("/users/self", params, secret) params['sig'] = sig return self.get_json('https://api.instagram.com/v1/users/self', params=params) def _generate_sig(self, endpoint, params, secret): sig = endpoint for key in sorted(params.keys()): sig += '|%s=%s' % (key, params[key]) return hmac.new(secret.encode(), sig.encode(), sha256).hexdigest() social-auth-core-3.1.0/social_core/backends/digitalocean.py0000644000175000017500000000262113336542652023573 0ustar omabomab00000000000000from .oauth import BaseOAuth2 class DigitalOceanOAuth(BaseOAuth2): """ DigitalOcean OAuth authentication backend. Docs: https://developers.digitalocean.com/documentation/oauth/ """ name = 'digitalocean' AUTHORIZATION_URL = 'https://cloud.digitalocean.com/v1/oauth/authorize' ACCESS_TOKEN_URL = 'https://cloud.digitalocean.com/v1/oauth/token' ACCESS_TOKEN_METHOD = 'POST' SCOPE_SEPARATOR = ' ' EXTRA_DATA = [ ('expires_in', 'expires_in') ] def get_user_id(self, details, response): """Return user unique id provided by service""" return response['account'].get('uuid') def get_user_details(self, response): """Return user details from DigitalOcean account""" fullname, first_name, last_name = self.get_user_names( response.get('name') or '') return {'username': response['account'].get('email'), 'email': response['account'].get('email'), 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, token, *args, **kwargs): """Loads user data from service""" url = 'https://api.digitalocean.com/v2/account' auth_header = {"Authorization": "Bearer %s" % token} try: return self.get_json(url, headers=auth_header) except ValueError: return None social-auth-core-3.1.0/social_core/backends/github_enterprise.py0000644000175000017500000000250013336542652024666 0ustar omabomab00000000000000""" Github Enterprise OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/github_enterprise.html """ from six.moves.urllib.parse import urljoin from ..utils import append_slash from .github import GithubOAuth2, GithubOrganizationOAuth2, \ GithubTeamOAuth2 class GithubEnterpriseMixin(object): def api_url(self): return append_slash(self.setting('API_URL')) def authorization_url(self): return self._url('login/oauth/authorize') def access_token_url(self): return self._url('login/oauth/access_token') def _url(self, path): return urljoin(append_slash(self.setting('URL')), path) class GithubEnterpriseOAuth2(GithubEnterpriseMixin, GithubOAuth2): """Github Enterprise OAuth authentication backend""" name = 'github-enterprise' class GithubEnterpriseOrganizationOAuth2(GithubEnterpriseMixin, GithubOrganizationOAuth2): """Github Enterprise OAuth2 authentication backend for organizations""" name = 'github-enterprise-org' DEFAULT_SCOPE = ['read:org'] class GithubEnterpriseTeamOAuth2(GithubEnterpriseMixin, GithubTeamOAuth2): """Github Enterprise OAuth2 authentication backend for teams""" name = 'github-enterprise-team' DEFAULT_SCOPE = ['read:org'] social-auth-core-3.1.0/social_core/backends/patreon.py0000644000175000017500000000247313336542652022625 0ustar omabomab00000000000000""" Patreon OAuth2 backend https://www.patreon.com/platform/documentation/oauth """ from .oauth import BaseOAuth2 class PatreonOAuth2(BaseOAuth2): """Patreon OAuth2 authentication backend""" name = 'patreon' AUTHORIZATION_URL = 'https://www.patreon.com/oauth2/authorize' ACCESS_TOKEN_URL = 'https://api.patreon.com/oauth2/token' REVOKE_TOKEN_URL = 'https://api.patreon.com/oauth2/revoke' ACCESS_TOKEN_METHOD = 'POST' REDIRECT_STATE = False ID_KEY = 'id' EXTRA_DATA = [ ('id', 'id'), ] def get_user_details(self, response): details = response['attributes'] return { 'username': details.get('full_name'), 'email': details.get('email'), 'fullname': details.get('full_name'), 'first_name': details.get('first_name'), 'last_name': details.get('last_name'), } def user_data(self, access_token, *args, **kwargs): return self.get_api(access_token, 'current_user')['data'] def get_api(self, access_token, suffix): return self.get_json( 'https://api.patreon.com/oauth2/api/{}'.format(suffix), headers=self.get_auth_header(access_token) ) def get_auth_header(self, access_token): return {'Authorization': 'Bearer {0}'.format(access_token)} social-auth-core-3.1.0/social_core/backends/utils.py0000644000175000017500000000615013336542652022311 0ustar omabomab00000000000000from collections import OrderedDict from .base import BaseAuth from ..exceptions import MissingBackend from ..utils import module_member, user_is_authenticated # Cache for discovered backends. BACKENDSCACHE = OrderedDict() def load_backends(backends, force_load=False): """ Load backends defined on SOCIAL_AUTH_AUTHENTICATION_BACKENDS, backends will be imported and cached on BACKENDSCACHE. The key in that dict will be the backend name, and the value is the backend class. Only subclasses of BaseAuth (and sub-classes) are considered backends. Previously there was a BACKENDS attribute expected on backends modules, this is not needed anymore since it's enough with the AUTHENTICATION_BACKENDS setting. BACKENDS was used because backends used to be split on two classes the authentication backend and another class that dealt with the auth mechanism with the provider, those classes are joined now. A force_load boolean argument is also provided so that get_backend below can retry a requested backend that may not yet be discovered. """ global BACKENDSCACHE if force_load: BACKENDSCACHE = OrderedDict() if not BACKENDSCACHE: for auth_backend in backends: backend = module_member(auth_backend) if issubclass(backend, BaseAuth): BACKENDSCACHE[backend.name] = backend return BACKENDSCACHE def get_backend(backends, name): """Returns a backend by name. Backends are stored in the BACKENDSCACHE cache dict. If not found, each of the modules referenced in AUTHENTICATION_BACKENDS is imported and checked for a BACKENDS definition. If the named backend is found in the module's BACKENDS definition, it's then stored in the cache for future access. """ try: # Cached backend which has previously been discovered return BACKENDSCACHE[name] except KeyError: # Reload BACKENDS to ensure a missing backend hasn't been missed load_backends(backends, force_load=True) try: return BACKENDSCACHE[name] except KeyError: raise MissingBackend(name) def user_backends_data(user, backends, storage): """ Will return backends data for given user, the return value will have the following keys: associated: UserSocialAuth model instances for currently associated accounts not_associated: Not associated (yet) backend names backends: All backend names. If user is not authenticated, then 'associated' list is empty, and there's no difference between 'not_associated' and 'backends'. """ available = list(load_backends(backends).keys()) values = {'associated': [], 'not_associated': available, 'backends': available} if user_is_authenticated(user): associated = storage.user.get_social_auth_for_user(user) not_associated = list(set(available) - set(assoc.provider for assoc in associated)) values['associated'] = associated values['not_associated'] = not_associated return values social-auth-core-3.1.0/social_core/backends/bitbucket.py0000644000175000017500000000673413336542652023135 0ustar omabomab00000000000000""" Bitbucket OAuth2 and OAuth1 backends, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/bitbucket.html """ from ..exceptions import AuthForbidden from .oauth import BaseOAuth1, BaseOAuth2 class BitbucketOAuthBase(object): ID_KEY = 'uuid' def get_user_id(self, details, response): id_key = self.ID_KEY if self.setting('USERNAME_AS_ID', False): id_key = 'username' return response.get(id_key) def get_user_details(self, response): """Return user details from Bitbucket account""" fullname, first_name, last_name = self.get_user_names( response['display_name'] ) return {'username': response.get('username', ''), 'email': response.get('email', ''), 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token, *args, **kwargs): """Return user data provided""" emails = self._get_emails(access_token) email = None for address in reversed(emails['values']): email = address['email'] if address['is_primary']: break if self.setting('VERIFIED_EMAILS_ONLY', False) and \ not address['is_confirmed']: raise AuthForbidden( self, 'Bitbucket account has no verified email' ) user = self._get_user(access_token) if email: user['email'] = email return user def _get_user(self, access_token=None): raise NotImplementedError('Implement in subclass') def _get_emails(self, access_token=None): raise NotImplementedError('Implement in subclass') class BitbucketOAuth2(BitbucketOAuthBase, BaseOAuth2): name = 'bitbucket-oauth2' SCOPE_SEPARATOR = ' ' AUTHORIZATION_URL = 'https://bitbucket.org/site/oauth2/authorize' ACCESS_TOKEN_URL = 'https://bitbucket.org/site/oauth2/access_token' ACCESS_TOKEN_METHOD = 'POST' REDIRECT_STATE = False EXTRA_DATA = [ ('scopes', 'scopes'), ('expires_in', 'expires'), ('token_type', 'token_type'), ('refresh_token', 'refresh_token') ] def auth_complete_credentials(self): return self.get_key_and_secret() def _get_user(self, access_token=None): return self.get_json('https://api.bitbucket.org/2.0/user', params={'access_token': access_token}) def _get_emails(self, access_token=None): return self.get_json('https://api.bitbucket.org/2.0/user/emails', params={'access_token': access_token}) class BitbucketOAuth(BitbucketOAuthBase, BaseOAuth1): """Bitbucket OAuth authentication backend""" name = 'bitbucket' AUTHORIZATION_URL = 'https://bitbucket.org/api/1.0/oauth/authenticate' REQUEST_TOKEN_URL = 'https://bitbucket.org/api/1.0/oauth/request_token' ACCESS_TOKEN_URL = 'https://bitbucket.org/api/1.0/oauth/access_token' def oauth_auth(self, *args, **kwargs): return super(BitbucketOAuth, self).oauth_auth(*args, **kwargs) def _get_user(self, access_token=None): return self.get_json('https://api.bitbucket.org/2.0/user', auth=self.oauth_auth(access_token)) def _get_emails(self, access_token=None): return self.get_json('https://api.bitbucket.org/2.0/user/emails', auth=self.oauth_auth(access_token)) social-auth-core-3.1.0/social_core/backends/five_hundred_px.py0000644000175000017500000000222413336542652024320 0ustar omabomab00000000000000""" 500px OAuth1 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/five_hundred_px.html """ from .oauth import BaseOAuth1 class FiveHundredPxOAuth(BaseOAuth1): """500px OAuth authentication backend""" name = '500px' AUTHORIZATION_URL = 'https://api.500px.com/v1/oauth/authorize' REQUEST_TOKEN_URL = 'https://api.500px.com/v1/oauth/request_token' ACCESS_TOKEN_URL = 'https://api.500px.com/v1/oauth/access_token' def get_user_details(self, user): """Return user details from 500px account""" fullname, first_name, last_name = self.get_user_names( user.get('fullname') ) return { 'username': user.get('username') or user.get('id'), 'email': user.get('email'), 'fullname': fullname, 'first_name': first_name, 'last_name': last_name } def user_data(self, access_token, *args, **kwargs): """Return user data provided""" response = self.get_json( 'https://api.500px.com/v1/users', auth=self.oauth_auth(access_token) ) return response.get('user') social-auth-core-3.1.0/social_core/backends/odnoklassniki.py0000644000175000017500000001556013336542652024026 0ustar omabomab00000000000000""" Odnoklassniki OAuth2 and Iframe Application backends, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/odnoklassnikiru.html """ from hashlib import md5 from six.moves.urllib_parse import unquote from .base import BaseAuth from .oauth import BaseOAuth2 from ..exceptions import AuthFailed class OdnoklassnikiOAuth2(BaseOAuth2): """Odnoklassniki authentication backend""" name = 'odnoklassniki-oauth2' ID_KEY = 'uid' ACCESS_TOKEN_METHOD = 'POST' SCOPE_SEPARATOR = ';' AUTHORIZATION_URL = 'https://connect.ok.ru/oauth/authorize' ACCESS_TOKEN_URL = 'https://api.ok.ru/oauth/token.do' EXTRA_DATA = [('refresh_token', 'refresh_token'), ('expires_in', 'expires')] def get_user_details(self, response): """Return user details from Odnoklassniki request""" fullname, first_name, last_name = self.get_user_names( fullname=unquote(response['name']), first_name=unquote(response['first_name']), last_name=unquote(response['last_name']) ) return { 'username': response['uid'], 'email': response.get('email', ''), 'fullname': fullname, 'first_name': first_name, 'last_name': last_name } def user_data(self, access_token, *args, **kwargs): """Return user data from Odnoklassniki REST API""" data = {'access_token': access_token, 'method': 'users.getCurrentUser'} key, secret = self.get_key_and_secret() public_key = self.setting('PUBLIC_NAME') return odnoklassniki_api(self, data, 'https://api.ok.ru/', public_key, secret, 'oauth') class OdnoklassnikiApp(BaseAuth): """Odnoklassniki iframe app authentication backend""" name = 'odnoklassniki-app' ID_KEY = 'uid' def extra_data(self, user, uid, response, details=None, *args, **kwargs): return dict([(key, value) for key, value in response.items() if key in response['extra_data_list']]) def get_user_details(self, response): fullname, first_name, last_name = self.get_user_names( fullname=unquote(response['name']), first_name=unquote(response['first_name']), last_name=unquote(response['last_name']) ) return { 'username': response['uid'], 'email': '', 'fullname': fullname, 'first_name': first_name, 'last_name': last_name } def auth_complete(self, *args, **kwargs): self.verify_auth_sig() response = self.get_response() fields = ('uid', 'first_name', 'last_name', 'name') + \ self.setting('EXTRA_USER_DATA_LIST', ()) data = { 'method': 'users.getInfo', 'uids': '{0}'.format(response['logged_user_id']), 'fields': ','.join(fields), } client_key, client_secret = self.get_key_and_secret() public_key = self.setting('PUBLIC_NAME') details = odnoklassniki_api(self, data, response['api_server'], public_key, client_secret, 'iframe_nosession') if len(details) == 1 and 'uid' in details[0]: details = details[0] auth_data_fields = self.setting('EXTRA_AUTH_DATA_LIST', ('api_server', 'apiconnection', 'session_key', 'authorized', 'session_secret_key')) for field in auth_data_fields: details[field] = response[field] details['extra_data_list'] = fields + auth_data_fields kwargs.update({'backend': self, 'response': details}) else: raise AuthFailed(self, 'Cannot get user details: API error') return self.strategy.authenticate(*args, **kwargs) def get_auth_sig(self): secret_key = self.setting('SECRET') hash_source = '{0:s}{1:s}{2:s}'.format(self.data['logged_user_id'], self.data['session_key'], secret_key) return md5(hash_source.encode('utf-8')).hexdigest() def get_response(self): fields = ('logged_user_id', 'api_server', 'application_key', 'session_key', 'session_secret_key', 'authorized', 'apiconnection') return dict((name, self.data[name]) for name in fields if name in self.data) def verify_auth_sig(self): correct_key = self.get_auth_sig() key = self.data['auth_sig'].lower() if correct_key != key: raise AuthFailed(self, 'Wrong authorization key') def odnoklassniki_oauth_sig(data, client_secret): """ Calculates signature of request data access_token value must be included Algorithm is described at https://apiok.ru/wiki/pages/viewpage.action?pageId=12878032, search for "little bit different way" """ suffix = md5( '{0:s}{1:s}'.format(data['access_token'], client_secret).encode('utf-8') ).hexdigest() check_list = sorted(['{0:s}={1:s}'.format(key, value) for key, value in data.items() if key != 'access_token']) return md5((''.join(check_list) + suffix).encode('utf-8')).hexdigest() def odnoklassniki_iframe_sig(data, client_secret_or_session_secret): """ Calculates signature as described at: https://apiok.ru/wiki/display/ok/Authentication+and+Authorization If API method requires session context, request is signed with session secret key. Otherwise it is signed with application secret key """ param_list = sorted(['{0:s}={1:s}'.format(key, value) for key, value in data.items()]) return md5( (''.join(param_list) + client_secret_or_session_secret).encode('utf-8') ).hexdigest() def odnoklassniki_api(backend, data, api_url, public_key, client_secret, request_type='oauth'): """Calls Odnoklassniki REST API method https://apiok.ru/wiki/display/ok/Odnoklassniki+Rest+API""" data.update({ 'application_key': public_key, 'format': 'JSON' }) if request_type == 'oauth': data['sig'] = odnoklassniki_oauth_sig(data, client_secret) elif request_type == 'iframe_session': data['sig'] = odnoklassniki_iframe_sig(data, data['session_secret_key']) elif request_type == 'iframe_nosession': data['sig'] = odnoklassniki_iframe_sig(data, client_secret) else: msg = 'Unknown request type {0}. How should it be signed?' raise AuthFailed(backend, msg.format(request_type)) return backend.get_json(api_url + 'fb.do', params=data) social-auth-core-3.1.0/social_core/backends/yahoo.py0000644000175000017500000001353613342002520022254 0ustar omabomab00000000000000""" Yahoo OpenId, OAuth1 and OAuth2 backends, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/yahoo.html """ from requests.auth import HTTPBasicAuth from ..utils import handle_http_errors from .open_id import OpenIdAuth from .oauth import BaseOAuth2, BaseOAuth1 class YahooOpenId(OpenIdAuth): """Yahoo OpenID authentication backend""" name = 'yahoo' URL = 'http://me.yahoo.com' class YahooOAuth(BaseOAuth1): """Yahoo OAuth authentication backend. DEPRECATED""" name = 'yahoo-oauth' ID_KEY = 'guid' AUTHORIZATION_URL = 'https://api.login.yahoo.com/oauth/v2/request_auth' REQUEST_TOKEN_URL = \ 'https://api.login.yahoo.com/oauth/v2/get_request_token' ACCESS_TOKEN_URL = 'https://api.login.yahoo.com/oauth/v2/get_token' EXTRA_DATA = [ ('guid', 'id'), ('access_token', 'access_token'), ('expires', 'expires') ] def get_user_details(self, response): """Return user details from Yahoo Profile""" fullname, first_name, last_name = self.get_user_names( first_name=response.get('givenName'), last_name=response.get('familyName') ) emails = [email for email in response.get('emails', []) if email.get('handle')] emails.sort(key=lambda e: e.get('primary', False), reverse=True) return {'username': response.get('nickname'), 'email': emails[0]['handle'] if emails else '', 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" url = 'https://social.yahooapis.com/v1/user/{0}/profile?format=json' return self.get_json( url.format(self._get_guid(access_token)), auth=self.oauth_auth(access_token) )['profile'] def _get_guid(self, access_token): """ Beause you have to provide GUID for every API request it's also returned during one of OAuth calls """ return self.get_json( 'https://social.yahooapis.com/v1/me/guid?format=json', auth=self.oauth_auth(access_token) )['guid']['value'] class YahooOAuth2(BaseOAuth2): """Yahoo OAuth2 authentication backend""" name = 'yahoo-oauth2' ID_KEY = 'guid' AUTHORIZATION_URL = 'https://api.login.yahoo.com/oauth2/request_auth' ACCESS_TOKEN_URL = 'https://api.login.yahoo.com/oauth2/get_token' ACCESS_TOKEN_METHOD = 'POST' EXTRA_DATA = [ ('xoauth_yahoo_guid', 'id'), ('access_token', 'access_token'), ('expires_in', 'expires'), ('refresh_token', 'refresh_token'), ('token_type', 'token_type'), ] def get_user_names(self, first_name, last_name): if first_name or last_name: return ' '.join((first_name, last_name)), first_name, last_name return None, None, None def get_user_details(self, response): """ Return user details from Yahoo Profile. To Get user email you need the profile private read permission. """ fullname, first_name, last_name = self.get_user_names( first_name=response.get('givenName'), last_name=response.get('familyName') ) emails = [email for email in response.get('emails', []) if 'handle' in email] emails.sort(key=lambda e: e.get('primary', False), reverse=True) email = emails[0]['handle'] if emails else response.get('guid', '') return { 'username': response.get('nickname'), 'email': email, 'fullname': fullname, 'first_name': first_name, 'last_name': last_name } def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" url = 'https://social.yahooapis.com/v1/user/{0}/profile?format=json' \ .format(kwargs['response']['xoauth_yahoo_guid']) return self.get_json(url, headers={ 'Authorization': 'Bearer {0}'.format(access_token) }, method='GET')['profile'] @handle_http_errors def auth_complete(self, *args, **kwargs): """Completes login process, must return user instance""" self.process_error(self.data) response = self.request_access_token( self.ACCESS_TOKEN_URL, auth=HTTPBasicAuth(*self.get_key_and_secret()), data=self.auth_complete_params(self.validate_state()), headers=self.auth_headers(), method=self.ACCESS_TOKEN_METHOD ) self.process_error(response) return self.do_auth(response['access_token'], response=response, *args, **kwargs) def refresh_token_params(self, token, *args, **kwargs): return { 'refresh_token': token, 'grant_type': 'refresh_token', 'redirect_uri': 'oob', # out of bounds } def refresh_token(self, token, *args, **kwargs): params = self.refresh_token_params(token, *args, **kwargs) url = self.REFRESH_TOKEN_URL or self.ACCESS_TOKEN_URL method = self.REFRESH_TOKEN_METHOD key = 'params' if method == 'GET' else 'data' request_args = { 'headers': self.auth_headers(), 'method': method, key: params } request = self.request( url, auth=HTTPBasicAuth(*self.get_key_and_secret()), **request_args ) return self.process_refresh_token_response(request, *args, **kwargs) def auth_complete_params(self, state=None): return { 'grant_type': 'authorization_code', # request auth code 'code': self.data.get('code', ''), # server response code 'redirect_uri': self.get_redirect_uri(state) } social-auth-core-3.1.0/social_core/backends/vimeo.py0000644000175000017500000000536513336542652022277 0ustar omabomab00000000000000from .oauth import BaseOAuth1, BaseOAuth2 class VimeoOAuth1(BaseOAuth1): """Vimeo OAuth authentication backend""" name = 'vimeo' AUTHORIZATION_URL = 'https://vimeo.com/oauth/authorize' REQUEST_TOKEN_URL = 'https://vimeo.com/oauth/request_token' ACCESS_TOKEN_URL = 'https://vimeo.com/oauth/access_token' def get_user_id(self, details, response): return response.get('person', {}).get('id') def get_user_details(self, response): """Return user details from Twitter account""" person = response.get('person', {}) fullname, first_name, last_name = self.get_user_names( person.get('display_name', '') ) return {'username': person.get('username', ''), 'email': '', 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token, *args, **kwargs): """Return user data provided""" return self.get_json( 'https://vimeo.com/api/rest/v2', params={'format': 'json', 'method': 'vimeo.people.getInfo'}, auth=self.oauth_auth(access_token) ) class VimeoOAuth2(BaseOAuth2): """Vimeo OAuth2 authentication backend""" name = 'vimeo-oauth2' AUTHORIZATION_URL = 'https://api.vimeo.com/oauth/authorize' ACCESS_TOKEN_URL = 'https://api.vimeo.com/oauth/access_token' REFRESH_TOKEN_URL = 'https://api.vimeo.com/oauth/request_token' ACCESS_TOKEN_METHOD = 'POST' SCOPE_SEPARATOR = ',' API_ACCEPT_HEADER = {'Accept': 'application/vnd.vimeo.*+json;version=3.0'} def get_redirect_uri(self, state=None): """ Build redirect with redirect_state parameter. @Vimeo API 3 requires exact redirect uri without additional additional state parameter included """ return self.redirect_uri def get_user_id(self, details, response): """Return user id""" try: user_id = response.get('user', {})['uri'].split('/')[-1] except KeyError: user_id = None return user_id def get_user_details(self, response): """Return user details from account""" user = response.get('user', {}) fullname, first_name, last_name = self.get_user_names( user.get('name', '') ) return {'username': fullname, 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token, *args, **kwargs): """Return user data provided""" return self.get_json( 'https://api.vimeo.com/me', params={'access_token': access_token}, headers=VimeoOAuth2.API_ACCEPT_HEADER, ) social-auth-core-3.1.0/social_core/backends/yandex.py0000644000175000017500000000610213417122006022421 0ustar omabomab00000000000000""" Yandex OpenID and OAuth2 support. This contribution adds support for Yandex.ru OpenID service in the form openid.yandex.ru/user. Username is retrieved from the identity url. If username is not specified, OpenID 2.0 url used for authentication. """ from six.moves.urllib_parse import urlsplit from .open_id import OpenIdAuth from .oauth import BaseOAuth2 class YandexOpenId(OpenIdAuth): """Yandex OpenID authentication backend""" name = 'yandex-openid' URL = 'http://openid.yandex.ru' def get_user_id(self, details, response): return details['email'] or response.identity_url def get_user_details(self, response): """Generate username from identity url""" values = super(YandexOpenId, self).get_user_details(response) values['username'] = values.get('username') or\ urlsplit(response.identity_url)\ .path.strip('/') values['email'] = values.get('email', '') return values class YandexOAuth2(BaseOAuth2): """Legacy Yandex OAuth2 authentication backend""" name = 'yandex-oauth2' AUTHORIZATION_URL = 'https://oauth.yandex.com/authorize' ACCESS_TOKEN_URL = 'https://oauth.yandex.com/token' ACCESS_TOKEN_METHOD = 'POST' REDIRECT_STATE = False def get_user_details(self, response): fullname, first_name, last_name = self.get_user_names( response.get('real_name') or response.get('display_name') or '' ) email = response.get('default_email') if not email: emails = response.get('emails') email = emails[0] if emails else '' return {'username': response.get('display_name'), 'email': email, 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token, *args, **kwargs): return self.get_json('https://login.yandex.ru/info', params={'oauth_token': access_token, 'format': 'json'}) class YaruOAuth2(BaseOAuth2): name = 'yaru' AUTHORIZATION_URL = 'https://oauth.yandex.com/authorize' ACCESS_TOKEN_URL = 'https://oauth.yandex.com/token' ACCESS_TOKEN_METHOD = 'POST' REDIRECT_STATE = False def get_user_details(self, response): fullname, first_name, last_name = self.get_user_names( response.get('real_name') or response.get('display_name') or '' ) email = response.get('default_email') if not email: emails = response.get('emails') email = emails[0] if emails else '' return {'username': response.get('display_name'), 'email': email, 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token, *args, **kwargs): return self.get_json('https://login.yandex.ru/info', params={'oauth_token': access_token, 'format': 'json'}) social-auth-core-3.1.0/social_core/backends/moves.py0000644000175000017500000000175113336542652022304 0ustar omabomab00000000000000""" Moves OAuth2 backend, docs at: https://dev.moves-app.com/docs/authentication Written by Avi Alkalay Certified to work with Django 1.6 """ from .oauth import BaseOAuth2 class MovesOAuth2(BaseOAuth2): """Moves OAuth authentication backend""" name = 'moves' ID_KEY = 'user_id' AUTHORIZATION_URL = 'https://api.moves-app.com/oauth/v1/authorize' ACCESS_TOKEN_URL = 'https://api.moves-app.com/oauth/v1/access_token' ACCESS_TOKEN_METHOD = 'POST' EXTRA_DATA = [ ('refresh_token', 'refresh_token', True), ('expires_in', 'expires'), ] def get_user_details(self, response): """Return user details Moves account""" return {'username': str(response.get('user_id'))} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" return self.get_json('https://api.moves-app.com/api/1.1/user/profile', params={'access_token': access_token}) social-auth-core-3.1.0/social_core/backends/twitter.py0000644000175000017500000000273713336542652022662 0ustar omabomab00000000000000""" Twitter OAuth1 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/twitter.html """ from .oauth import BaseOAuth1 from ..exceptions import AuthCanceled class TwitterOAuth(BaseOAuth1): """Twitter OAuth authentication backend""" name = 'twitter' EXTRA_DATA = [('id', 'id')] REQUEST_TOKEN_METHOD = 'POST' ACCESS_TOKEN_METHOD = 'POST' AUTHORIZATION_URL = 'https://api.twitter.com/oauth/authenticate' REQUEST_TOKEN_URL = 'https://api.twitter.com/oauth/request_token' ACCESS_TOKEN_URL = 'https://api.twitter.com/oauth/access_token' REDIRECT_STATE = True def process_error(self, data): if 'denied' in data: raise AuthCanceled(self) else: super(TwitterOAuth, self).process_error(data) def get_user_details(self, response): """Return user details from Twitter account""" fullname, first_name, last_name = self.get_user_names(response['name']) return {'username': response['screen_name'], 'email': response.get('email', ''), 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token, *args, **kwargs): """Return user data provided""" return self.get_json( 'https://api.twitter.com/1.1/account/verify_credentials.json', params={'include_email': 'true'}, auth=self.oauth_auth(access_token) ) social-auth-core-3.1.0/social_core/backends/nk.py0000644000175000017500000000532713336542652021566 0ustar omabomab00000000000000from urllib import urlencode import six from requests_oauthlib import OAuth1 from .oauth import BaseOAuth2 class NKOAuth2(BaseOAuth2): """NK OAuth authentication backend""" name = 'nk' AUTHORIZATION_URL = 'https://nk.pl/oauth2/login' ACCESS_TOKEN_URL = 'https://nk.pl/oauth2/token' SCOPE_SEPARATOR = ',' ACCESS_TOKEN_METHOD = 'POST' SIGNATURE_TYPE_AUTH_HEADER = 'AUTH_HEADER' EXTRA_DATA = [ ('id', 'id'), ] def get_user_details(self, response): """Return user details from NK account""" entry = response['entry'] return { 'username': entry.get('displayName'), 'email': entry['emails'][0]['value'], 'first_name': entry.get('displayName').split(' ')[0], 'id': entry.get('id') } def auth_complete_params(self, state=None): client_id, client_secret = self.get_key_and_secret() return { 'grant_type': 'authorization_code', # request auth code 'code': self.data.get('code', ''), # server response code 'client_id': client_id, 'client_secret': client_secret, 'redirect_uri': self.get_redirect_uri(state), 'scope': self.get_scope_argument() } def get_user_id(self, details, response): """Return a unique ID for the current user, by default from server response.""" return details.get(self.ID_KEY) def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" url = 'http://opensocial.nk-net.pl/v09/social/rest/people/@me?' + \ urlencode({ 'nk_token': access_token, 'fields': 'name,surname,avatar,localization,age,' + 'gender,emails,birthdate' }) return self.get_json( url, auth=self.oauth_auth(access_token) ) def oauth_auth(self, token=None, oauth_verifier=None, signature_type=SIGNATURE_TYPE_AUTH_HEADER): key, secret = self.get_key_and_secret() oauth_verifier = oauth_verifier or self.data.get('oauth_verifier') token = token or {} # decoding='utf-8' produces errors with python-requests on Python3 # since the final URL will be of type bytes decoding = None if six.PY3 else 'utf-8' state = self.get_or_create_state() return OAuth1(key, secret, resource_owner_key=None, resource_owner_secret=None, callback_uri=self.get_redirect_uri(state), verifier=oauth_verifier, signature_type=signature_type, decoding=decoding) social-auth-core-3.1.0/social_core/backends/shopify.py0000644000175000017500000000645513336542652022642 0ustar omabomab00000000000000""" Shopify OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/shopify.html """ import imp import six from ..utils import handle_http_errors from .oauth import BaseOAuth2 from ..exceptions import AuthFailed, AuthCanceled class ShopifyOAuth2(BaseOAuth2): """Shopify OAuth2 authentication backend""" name = 'shopify' ID_KEY = 'shop' EXTRA_DATA = [ ('shop', 'shop'), ('website', 'website'), ('expires', 'expires') ] REDIRECT_STATE = False @property def shopifyAPI(self): if not hasattr(self, '_shopify_api'): fp, pathname, description = imp.find_module('shopify') self._shopify_api = imp.load_module('shopify', fp, pathname, description) return self._shopify_api def get_user_details(self, response): """Use the shopify store name as the username""" return { 'username': six.text_type(response.get('shop', '')).replace( '.myshopify.com', '' ) } def extra_data(self, user, uid, response, details=None, *args, **kwargs): """Return access_token and extra defined names to store in extra_data field""" data = super(ShopifyOAuth2, self).extra_data(user, uid, response, details, *args, **kwargs) session = self.shopifyAPI.Session(self.data.get('shop').strip()) # Get, and store the permanent token token = session.request_token(data['access_token']) data['access_token'] = token return dict(data) def auth_url(self): key, secret = self.get_key_and_secret() self.shopifyAPI.Session.setup(api_key=key, secret=secret) scope = self.get_scope() state = self.state_token() self.strategy.session_set(self.name + '_state', state) redirect_uri = self.get_redirect_uri(state) session = self.shopifyAPI.Session(self.data.get('shop').strip()) return session.create_permission_url( scope=scope, redirect_uri=redirect_uri ) @handle_http_errors def auth_complete(self, *args, **kwargs): """Completes login process, must return user instance""" self.process_error(self.data) access_token = None key, secret = self.get_key_and_secret() try: shop_url = self.data.get('shop') self.shopifyAPI.Session.setup(api_key=key, secret=secret) shopify_session = self.shopifyAPI.Session(shop_url, self.data) access_token = shopify_session.token except self.shopifyAPI.ValidationException: raise AuthCanceled(self) else: if not access_token: raise AuthFailed(self, 'Authentication Failed') return self.do_auth(access_token, shop_url, shopify_session.url, *args, **kwargs) def do_auth(self, access_token, shop_url, website, *args, **kwargs): kwargs.update({ 'backend': self, 'response': { 'shop': shop_url, 'website': 'http://{0}'.format(website), 'access_token': access_token } }) return self.strategy.authenticate(*args, **kwargs) social-auth-core-3.1.0/social_core/backends/behance.py0000644000175000017500000000306013336542652022533 0ustar omabomab00000000000000""" Behance OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/behance.html """ from .oauth import BaseOAuth2 class BehanceOAuth2(BaseOAuth2): """Behance OAuth authentication backend""" name = 'behance' AUTHORIZATION_URL = 'https://www.behance.net/v2/oauth/authenticate' ACCESS_TOKEN_URL = 'https://www.behance.net/v2/oauth/token' ACCESS_TOKEN_METHOD = 'POST' SCOPE_SEPARATOR = '|' EXTRA_DATA = [('username', 'username')] REDIRECT_STATE = False def get_user_id(self, details, response): return response['user']['id'] def get_user_details(self, response): """Return user details from Behance account""" user = response['user'] fullname, first_name, last_name = self.get_user_names( user['display_name'], user['first_name'], user['last_name'] ) return {'username': user['username'], 'fullname': fullname, 'first_name': first_name, 'last_name': last_name, 'email': ''} def extra_data(self, user, uid, response, details=None, *args, **kwargs): # Pull up the embedded user attributes so they can be found as extra # data. See the example token response for possible attributes: # http://www.behance.net/dev/authentication#step-by-step data = response.copy() data.update(response['user']) return super(BehanceOAuth2, self).extra_data(user, uid, data, details, *args, **kwargs) social-auth-core-3.1.0/social_core/backends/qiita.py0000644000175000017500000000423613336542652022263 0ustar omabomab00000000000000""" Qiita OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/qiita.html http://qiita.com/api/v2/docs#get-apiv2oauthauthorize """ import json from .oauth import BaseOAuth2 class QiitaOAuth2(BaseOAuth2): """Qiita OAuth authentication backend""" name = 'qiita' AUTHORIZATION_URL = 'https://qiita.com/api/v2/oauth/authorize' ACCESS_TOKEN_URL = 'https://qiita.com/api/v2/access_tokens' ACCESS_TOKEN_METHOD = 'POST' SCOPE_SEPARATOR = ' ' REDIRECT_STATE = True EXTRA_DATA = [ ('description', 'description'), ('facebook_id', 'facebook_id'), ('followees_count', 'followees_count'), ('followers_count', 'followers_count'), ('github_login_name', 'github_login_name'), ('id', 'id'), ('items_count', 'items_count'), ('linkedin_id', 'linkedin_id'), ('location', 'location'), ('name', 'name'), ('organization', 'organization'), ('profile_image_url', 'profile_image_url'), ('twitter_screen_name', 'twitter_screen_name'), ('website_url', 'website_url'), ] def auth_complete_params(self, state=None): data = super(QiitaOAuth2, self).auth_complete_params(state) if "grant_type" in data: del data["grant_type"] if "redirect_uri" in data: del data["redirect_uri"] return json.dumps(data) def auth_headers(self): return {'Content-Type': 'application/json'} def request_access_token(self, *args, **kwargs): data = super(QiitaOAuth2, self).request_access_token(*args, **kwargs) data.update({'access_token': data['token']}) return data def get_user_details(self, response): """Return user details from Qiita account""" return { 'username': response['id'], 'fullname': response['name'], } def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" return self.get_json( 'https://qiita.com/api/v2/authenticated_user', headers={ 'Authorization': 'Bearer {0}'.format(access_token) }) social-auth-core-3.1.0/social_core/backends/naver.py0000644000175000017500000000372113336542652022265 0ustar omabomab00000000000000from xml.dom import minidom from .oauth import BaseOAuth2 class NaverOAuth2(BaseOAuth2): """Naver OAuth authentication backend""" name = 'naver' AUTHORIZATION_URL = 'https://nid.naver.com/oauth2.0/authorize' ACCESS_TOKEN_URL = 'https://nid.naver.com/oauth2.0/token' ACCESS_TOKEN_METHOD = 'POST' EXTRA_DATA = [ ('id', 'id'), ] def get_user_id(self, details, response): return response.get('id') def get_user_details(self, response): """Return user details from Naver account""" return { 'username': response.get('username'), 'email': response.get('email'), 'fullname': response.get('username'), } def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" response = self.request( 'https://openapi.naver.com/v1/nid/getUserProfile.xml', headers={ 'Authorization': 'Bearer {0}'.format(access_token), 'Content_Type': 'text/xml' } ) dom = minidom.parseString(response.text.encode('utf-8').strip()) return { 'id': self._dom_value(dom, 'id'), 'email': self._dom_value(dom, 'email'), 'username': self._dom_value(dom, 'name'), 'nickname': self._dom_value(dom, 'nickname'), 'gender': self._dom_value(dom, 'gender'), 'age': self._dom_value(dom, 'age'), 'birthday': self._dom_value(dom, 'birthday'), 'profile_image': self._dom_value(dom, 'profile_image') } def auth_headers(self): client_id, client_secret = self.get_key_and_secret() return { 'grant_type': 'authorization_code', 'code': self.data.get('code'), 'client_id': client_id, 'client_secret': client_secret, } def _dom_value(self, dom, key): return dom.getElementsByTagName(key)[0].childNodes[0].data social-auth-core-3.1.0/social_core/backends/weixin.py0000644000175000017500000001421413342002520022432 0ustar omabomab00000000000000# -*- coding: utf-8 -*- # author:duoduo3369@gmail.com https://github.com/duoduo369 """ Weixin OAuth2 backend """ from six.moves.urllib_parse import urlencode from requests import HTTPError from .oauth import BaseOAuth2 from ..exceptions import AuthCanceled, AuthUnknownError class WeixinOAuth2(BaseOAuth2): """Weixin OAuth authentication backend""" name = 'weixin' ID_KEY = 'openid' AUTHORIZATION_URL = 'https://open.weixin.qq.com/connect/qrconnect' ACCESS_TOKEN_URL = 'https://api.weixin.qq.com/sns/oauth2/access_token' ACCESS_TOKEN_METHOD = 'POST' DEFAULT_SCOPE = ['snsapi_login'] REDIRECT_STATE = False EXTRA_DATA = [ ('nickname', 'username'), ('headimgurl', 'profile_image_url'), ] def get_user_details(self, response): """Return user details from Weixin. API URL is: https://api.weixin.qq.com/sns/userinfo """ if self.setting('DOMAIN_AS_USERNAME'): username = response.get('domain', '') else: username = response.get('nickname', '') return { 'username': username, 'profile_image_url': response.get('headimgurl', '') } def user_data(self, access_token, *args, **kwargs): data = self.get_json('https://api.weixin.qq.com/sns/userinfo', params={ 'access_token': access_token, 'openid': kwargs['response']['openid'] }) nickname = data.get('nickname') if nickname: # weixin api has some encode bug, here need handle data['nickname'] = nickname.encode( 'raw_unicode_escape' ).decode('utf-8') return data def auth_params(self, state=None): appid, secret = self.get_key_and_secret() params = { 'appid': appid, 'redirect_uri': self.get_redirect_uri(state) } if self.STATE_PARAMETER and state: params['state'] = state if self.RESPONSE_TYPE: params['response_type'] = self.RESPONSE_TYPE return params def auth_complete_params(self, state=None): appid, secret = self.get_key_and_secret() return { 'grant_type': 'authorization_code', # request auth code 'code': self.data.get('code', ''), # server response code 'appid': appid, 'secret': secret, 'redirect_uri': self.get_redirect_uri(state) } def refresh_token_params(self, token, *args, **kwargs): appid, secret = self.get_key_and_secret() return { 'refresh_token': token, 'grant_type': 'refresh_token', 'appid': appid, 'secret': secret } def auth_complete(self, *args, **kwargs): """Completes login process, must return user instance""" self.process_error(self.data) try: response = self.request_access_token( self.ACCESS_TOKEN_URL, data=self.auth_complete_params(self.validate_state()), headers=self.auth_headers(), method=self.ACCESS_TOKEN_METHOD ) except HTTPError as err: if err.response.status_code == 400: raise AuthCanceled(self, response=err.response) else: raise except KeyError: raise AuthUnknownError(self) if 'errcode' in response: raise AuthCanceled(self) self.process_error(response) return self.do_auth(response['access_token'], response=response, *args, **kwargs) class WeixinOAuth2APP(WeixinOAuth2): """ Weixin OAuth authentication backend Can't use in web, only in weixin app """ name = 'weixinapp' ID_KEY = 'openid' AUTHORIZATION_URL = 'https://open.weixin.qq.com/connect/oauth2/authorize' ACCESS_TOKEN_URL = 'https://api.weixin.qq.com/sns/oauth2/access_token' ACCESS_TOKEN_METHOD = 'POST' REDIRECT_STATE = False def auth_url(self): if self.STATE_PARAMETER or self.REDIRECT_STATE: # Store state in session for further request validation. The state # value is passed as state parameter (as specified in OAuth2 spec), # but also added to redirect, that way we can still verify the # request if the provider doesn't implement the state parameter. # Reuse token if any. name = self.name + '_state' state = self.strategy.session_get(name) if state is None: state = self.state_token() self.strategy.session_set(name, state) else: state = None params = self.auth_params(state) params.update(self.get_scope_argument()) params.update(self.auth_extra_arguments()) params = urlencode(sorted(params.items())) return '{}#wechat_redirect'.format( self.AUTHORIZATION_URL + '?' + params ) def auth_complete_params(self, state=None): appid, secret = self.get_key_and_secret() return { 'grant_type': 'authorization_code', # request auth code 'code': self.data.get('code', ''), # server response code 'appid': appid, 'secret': secret, } def validate_state(self): return None def auth_complete(self, *args, **kwargs): """Completes login process, must return user instance""" self.process_error(self.data) try: response = self.request_access_token( self.ACCESS_TOKEN_URL, data=self.auth_complete_params(self.validate_state()), headers=self.auth_headers(), method=self.ACCESS_TOKEN_METHOD ) except HTTPError as err: if err.response.status_code == 400: raise AuthCanceled(self) else: raise except KeyError: raise AuthUnknownError(self) if 'errcode' in response: raise AuthCanceled(self) self.process_error(response) return self.do_auth(response['access_token'], response=response, *args, **kwargs) social-auth-core-3.1.0/social_core/backends/spotify.py0000644000175000017500000000301713336542652022645 0ustar omabomab00000000000000""" Spotify backend, docs at: https://developer.spotify.com/spotify-web-api/ https://developer.spotify.com/spotify-web-api/authorization-guide/ """ import base64 from .oauth import BaseOAuth2 class SpotifyOAuth2(BaseOAuth2): """Spotify OAuth2 authentication backend""" name = 'spotify' ID_KEY = 'id' AUTHORIZATION_URL = 'https://accounts.spotify.com/authorize' ACCESS_TOKEN_URL = 'https://accounts.spotify.com/api/token' ACCESS_TOKEN_METHOD = 'POST' SCOPE_SEPARATOR = ' ' REDIRECT_STATE = False EXTRA_DATA = [ ('refresh_token', 'refresh_token'), ] def auth_headers(self): auth_str = '{0}:{1}'.format(*self.get_key_and_secret()) b64_auth_str = base64.urlsafe_b64encode(auth_str.encode()).decode() return { 'Authorization': 'Basic {0}'.format(b64_auth_str) } def get_user_details(self, response): """Return user details from Spotify account""" fullname, first_name, last_name = self.get_user_names( response.get('display_name') ) return {'username': response.get('id'), 'email': response.get('email'), 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" return self.get_json( 'https://api.spotify.com/v1/me', headers={'Authorization': 'Bearer {0}'.format(access_token)} ) social-auth-core-3.1.0/social_core/backends/mapmyfitness.py0000644000175000017500000000277013336542652023674 0ustar omabomab00000000000000""" MapMyFitness OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/mapmyfitness.html """ from .oauth import BaseOAuth2 class MapMyFitnessOAuth2(BaseOAuth2): """MapMyFitness OAuth authentication backend""" name = 'mapmyfitness' AUTHORIZATION_URL = 'https://www.mapmyfitness.com/v7.0/oauth2/authorize' ACCESS_TOKEN_URL = \ 'https://oauth2-api.mapmyapi.com/v7.0/oauth2/access_token' REQUEST_TOKEN_METHOD = 'POST' ACCESS_TOKEN_METHOD = 'POST' REDIRECT_STATE = False EXTRA_DATA = [ ('refresh_token', 'refresh_token'), ] def auth_headers(self): key = self.get_key_and_secret()[0] return { 'Api-Key': key } def get_user_id(self, details, response): return response['id'] def get_user_details(self, response): first = response.get('first_name', '') last = response.get('last_name', '') full = (first + last).strip() return { 'username': response['username'], 'email': response['email'], 'fullname': full, 'first_name': first, 'last_name': last, } def user_data(self, access_token, *args, **kwargs): key = self.get_key_and_secret()[0] url = 'https://oauth2-api.mapmyapi.com/v7.0/user/self/' headers = { 'Authorization': 'Bearer {0}'.format(access_token), 'Api-Key': key } return self.get_json(url, headers=headers) social-auth-core-3.1.0/social_core/backends/scistarter.py0000644000175000017500000000376013342000533023320 0ustar omabomab00000000000000""" SciStarter OAuth2 Auth """ from six.moves.urllib_parse import unquote, urlencode from .oauth import BaseOAuth2 class SciStarterOAuth2(BaseOAuth2): name = 'scistarter' ID_KEY = 'email' SCOPE_PARAMETER_NAME = 'scope' DEFAULT_SCOPE = ['login', 'extensive'] SCOPE_SEPARATOR = ' ' AUTHORIZATION_URL = 'https://scistarter.com/authorize' ACCESS_TOKEN_URL = 'https://scistarter.com/token?key={key}' ACCESS_TOKEN_METHOD = 'POST' USER_ACCESS_URL = \ 'https://scistarter.com/api/user_info?client_id={clientid}&key={key}' REFRESH_TOKEN_URL = None RESPONSE_TYPE = 'code' STATE_PARAMETER = True REDIRECT_STATE = True EXTRA_DATA = [ ('profile_id', 'profile_id'), ('expires', 'expires') ] def get_redirect_uri(self, state=None): """Build redirect with redirect_state parameter.""" return self.redirect_uri.rstrip('/') def authorization_url(self): return self.AUTHORIZATION_URL def get_user_details(self, response): return { 'username': response.get('handle'), 'email': response.get('email') or '', 'first_name': response.get('first_name'), 'last_name': response.get('last_name') } def user_data(self, access_token, *args, **kwards): client_id, client_secret = self.get_key_and_secret() return self.get_json( self.USER_ACCESS_URL.format(clientid=client_id, key=client_secret), headers={ 'Authorization': 'Bearer ' + access_token } ) def access_token(self, token): """Return request for access token value""" return self.get_querystring(self.access_token_url(), auth=self.oauth_auth(token), method=self.ACCESS_TOKEN_METHOD) def access_token_url(self): client_id, client_secret = self.get_key_and_secret() return self.ACCESS_TOKEN_URL.format(key=client_secret) social-auth-core-3.1.0/social_core/backends/weibo.py0000644000175000017500000000422713336542652022261 0ustar omabomab00000000000000# coding:utf-8 # author:hepochen@gmail.com https://github.com/hepochen """ Weibo OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/weibo.html """ from .oauth import BaseOAuth2 class WeiboOAuth2(BaseOAuth2): """Weibo (of sina) OAuth authentication backend""" name = 'weibo' ID_KEY = 'uid' AUTHORIZATION_URL = 'https://api.weibo.com/oauth2/authorize' REQUEST_TOKEN_URL = 'https://api.weibo.com/oauth2/request_token' ACCESS_TOKEN_URL = 'https://api.weibo.com/oauth2/access_token' ACCESS_TOKEN_METHOD = 'POST' REDIRECT_STATE = False EXTRA_DATA = [ ('id', 'id'), ('name', 'username'), ('profile_image_url', 'profile_image_url'), ('gender', 'gender') ] def get_user_details(self, response): """Return user details from Weibo. API URL is: https://api.weibo.com/2/users/show.json/?uid=&access_token= """ if self.setting('DOMAIN_AS_USERNAME'): username = response.get('domain', '') else: username = response.get('name', '') fullname, first_name, last_name = self.get_user_names( first_name=response.get('screen_name', '') ) return {'username': username, 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def get_uid(self, access_token): """Return uid by access_token""" data = self.get_json( 'https://api.weibo.com/oauth2/get_token_info', method='POST', params={'access_token': access_token} ) return data['uid'] def user_data(self, access_token, response=None, *args, **kwargs): """Return user data""" # If user id was not retrieved in the response, then get it directly # from weibo get_token_info endpoint uid = response and response.get('uid') or self.get_uid(access_token) user_data = self.get_json( 'https://api.weibo.com/2/users/show.json', params={'access_token': access_token, 'uid': uid} ) user_data['uid'] = uid return user_data social-auth-core-3.1.0/social_core/backends/dailymotion.py0000644000175000017500000000157413336542652023506 0ustar omabomab00000000000000""" DailyMotion OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/dailymotion.html """ from .oauth import BaseOAuth2 class DailymotionOAuth2(BaseOAuth2): """Dailymotion OAuth authentication backend""" name = 'dailymotion' EXTRA_DATA = [('id', 'id')] ID_KEY = 'username' AUTHORIZATION_URL = 'https://api.dailymotion.com/oauth/authorize' REQUEST_TOKEN_URL = 'https://api.dailymotion.com/oauth/token' ACCESS_TOKEN_URL = 'https://api.dailymotion.com/oauth/token' ACCESS_TOKEN_METHOD = 'POST' def get_user_details(self, response): return {'username': response.get('screenname')} def user_data(self, access_token, *args, **kwargs): """Return user data provided""" return self.get_json('https://api.dailymotion.com/auth/', params={'access_token': access_token}) social-auth-core-3.1.0/social_core/backends/itembase.py0000644000175000017500000000663113336542652022746 0ustar omabomab00000000000000import time from .oauth import BaseOAuth2 from ..utils import handle_http_errors class ItembaseOAuth2(BaseOAuth2): name = 'itembase' ID_KEY = 'uuid' AUTHORIZATION_URL = 'https://accounts.itembase.com/oauth/v2/auth' ACCESS_TOKEN_URL = 'https://accounts.itembase.com/oauth/v2/token' USER_DETAILS_URL = 'https://users.itembase.com/v1/me' ACTIVATION_ENDPOINT = 'https://solutionservice.itembase.com/activate' DEFAULT_SCOPE = ['user.minimal'] EXTRA_DATA = [ ('access_token', 'access_token'), ('token_type', 'token_type'), ('refresh_token', 'refresh_token'), ('expires_in', 'expires_in'), # seconds to expiration ('expires', 'expires'), # expiration timestamp in UTC ('uuid', 'uuid'), ('username', 'username'), ('email', 'email'), ('first_name', 'first_name'), ('middle_name', 'middle_name'), ('last_name', 'last_name'), ('name_format', 'name_format'), ('locale', 'locale'), ('preferred_currency', 'preferred_currency'), ] def add_expires(self, data): data['expires'] = int(time.time()) + data.get('expires_in', 0) return data def extra_data(self, user, uid, response, details=None, *args, **kwargs): data = BaseOAuth2.extra_data(self, user, uid, response, details=details, *args, **kwargs) return self.add_expires(data) def process_refresh_token_response(self, response, *args, **kwargs): data = BaseOAuth2.process_refresh_token_response(self, response, *args, **kwargs) return self.add_expires(data) def get_user_details(self, response): """Return user details from Itembase account""" return response def user_data(self, access_token, *args, **kwargs): return self.get_json(self.USER_DETAILS_URL, headers={ 'Authorization': 'Bearer {0}'.format(access_token) }) def activation_data(self, response): # returns activation_data dict with activation_url inside # see http://developers.itembase.com/authentication/activation return self.get_json(self.ACTIVATION_ENDPOINT, headers={ 'Authorization': 'Bearer {0}'.format(response['access_token']) }) @handle_http_errors def auth_complete(self, *args, **kwargs): """Completes login process, must return user instance""" state = self.validate_state() self.process_error(self.data) # itembase needs GET request with params instead of just data response = self.request_access_token( self.access_token_url(), params=self.auth_complete_params(state), headers=self.auth_headers(), auth=self.auth_complete_credentials(), method=self.ACCESS_TOKEN_METHOD ) self.process_error(response) return self.do_auth(response['access_token'], response=response, *args, **kwargs) class ItembaseOAuth2Sandbox(ItembaseOAuth2): name = 'itembase-sandbox' AUTHORIZATION_URL = 'http://sandbox.accounts.itembase.io/oauth/v2/auth' ACCESS_TOKEN_URL = 'http://sandbox.accounts.itembase.io/oauth/v2/token' USER_DETAILS_URL = 'http://sandbox.users.itembase.io/v1/me' ACTIVATION_ENDPOINT = 'http://sandbox.solutionservice.itembase.io/activate' social-auth-core-3.1.0/social_core/backends/username.py0000644000175000017500000000040613336542652022766 0ustar omabomab00000000000000""" Legacy Username backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/username.html """ from .legacy import LegacyAuth class UsernameAuth(LegacyAuth): name = 'username' ID_KEY = 'username' EXTRA_DATA = ['username'] social-auth-core-3.1.0/social_core/backends/untappd.py0000644000175000017500000000731713336542652022632 0ustar omabomab00000000000000import requests from .oauth import BaseOAuth2 from ..exceptions import AuthFailed from ..utils import handle_http_errors class UntappdOAuth2(BaseOAuth2): """Untappd OAuth2 authentication backend""" name = 'untappd' AUTHORIZATION_URL = 'https://untappd.com/oauth/authenticate/' ACCESS_TOKEN_URL = 'https://untappd.com/oauth/authorize/' BASE_API_URL = 'https://api.untappd.com' USER_INFO_URL = BASE_API_URL + '/v4/user/info/' ACCESS_TOKEN_METHOD = 'GET' STATE_PARAMETER = False REDIRECT_STATE = False EXTRA_DATA = [ ('id', 'id'), ('bio', 'bio'), ('date_joined', 'date_joined'), ('location', 'location'), ('url', 'url'), ('user_avatar', 'user_avatar'), ('user_avatar_hd', 'user_avatar_hd'), ('user_cover_photo', 'user_cover_photo') ] def auth_params(self, state=None): client_id, client_secret = self.get_key_and_secret() params = { 'client_id': client_id, 'redirect_url': self.get_redirect_uri(), 'response_type': self.RESPONSE_TYPE } return params def process_error(self, data): """ All errors from Untappd are contained in the 'meta' key of the response. """ response_code = data.get('meta', {}).get('http_code') if response_code is not None and response_code != requests.codes.ok: raise AuthFailed(self, data['meta']['error_detail']) @handle_http_errors def auth_complete(self, *args, **kwargs): """Completes login process, must return user instance""" client_id, client_secret = self.get_key_and_secret() code = self.data.get('code') self.process_error(self.data) # Untapped sends the access token request with URL parameters, # not a body response = self.request_access_token( self.access_token_url(), method=self.ACCESS_TOKEN_METHOD, params={ 'response_type': 'code', 'code': code, 'client_id': client_id, 'client_secret': client_secret, 'redirect_url': self.get_redirect_uri() } ) self.process_error(response) # Both the access_token and the rest of the response are # buried in the 'response' key return self.do_auth( response['response']['access_token'], response=response['response'], *args, **kwargs ) def get_user_details(self, response): """Return user details from an Untappd account""" # Start with the user data as it was returned user_data = response['user'] # Make a few updates to match expected key names user_data.update({ 'username': user_data.get('user_name'), 'email': user_data.get('settings', {}).get('email_address', ''), 'first_name': user_data.get('first_name'), 'last_name': user_data.get('last_name'), 'fullname': user_data.get('first_name') + ' ' + user_data.get('last_name') }) return user_data def get_user_id(self, details, response): """ Return a unique ID for the current user, by default from server response. """ return response['user'].get(self.ID_KEY) def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" response = self.get_json(self.USER_INFO_URL, params={ 'access_token': access_token, 'compact': 'true' }) self.process_error(response) # The response data is buried in the 'response' key return response['response'] social-auth-core-3.1.0/social_core/backends/stripe.py0000644000175000017500000000315213336542652022456 0ustar omabomab00000000000000""" Stripe OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/stripe.html """ from .oauth import BaseOAuth2 class StripeOAuth2(BaseOAuth2): """Stripe OAuth2 authentication backend""" name = 'stripe' ID_KEY = 'stripe_user_id' AUTHORIZATION_URL = 'https://connect.stripe.com/oauth/authorize' ACCESS_TOKEN_URL = 'https://connect.stripe.com/oauth/token' ACCESS_TOKEN_METHOD = 'POST' REDIRECT_STATE = False EXTRA_DATA = [ ('stripe_publishable_key', 'stripe_publishable_key'), ('access_token', 'access_token'), ('livemode', 'livemode'), ('token_type', 'token_type'), ('refresh_token', 'refresh_token'), ('stripe_user_id', 'stripe_user_id'), ] def get_user_details(self, response): """Return user details from Stripe account""" return {'username': response.get('stripe_user_id'), 'email': ''} def auth_complete_params(self, state=None): client_id, client_secret = self.get_key_and_secret() return { 'grant_type': 'authorization_code', 'client_id': client_id, 'scope': self.SCOPE_SEPARATOR.join(self.get_scope()), 'code': self.data['code'] } def auth_headers(self): client_id, client_secret = self.get_key_and_secret() return {'Accept': 'application/json', 'Authorization': 'Bearer {0}'.format(client_secret)} def refresh_token_params(self, refresh_token, *args, **kwargs): return {'refresh_token': refresh_token, 'grant_type': 'refresh_token'} social-auth-core-3.1.0/social_core/backends/launchpad.py0000644000175000017500000000030413336542652023103 0ustar omabomab00000000000000""" Launchpad OpenId backend """ from .open_id import OpenIdAuth class LaunchpadOpenId(OpenIdAuth): name = 'launchpad' URL = 'https://login.launchpad.net' USERNAME_KEY = 'nickname' social-auth-core-3.1.0/social_core/backends/azuread.py0000644000175000017500000001131513425033045022571 0ustar omabomab00000000000000import time from jwt import DecodeError, ExpiredSignature, decode as jwt_decode from ..exceptions import AuthTokenError from .oauth import BaseOAuth2 """ Copyright (c) 2015 Microsoft Open Technologies, Inc. All rights reserved. MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ """ Azure AD OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/azuread.html """ class AzureADOAuth2(BaseOAuth2): name = 'azuread-oauth2' SCOPE_SEPARATOR = ' ' AUTHORIZATION_URL = \ 'https://login.microsoftonline.com/common/oauth2/authorize' ACCESS_TOKEN_URL = 'https://login.microsoftonline.com/common/oauth2/token' ACCESS_TOKEN_METHOD = 'POST' REDIRECT_STATE = False DEFAULT_SCOPE = ['openid', 'profile', 'user_impersonation'] EXTRA_DATA = [ ('access_token', 'access_token'), ('id_token', 'id_token'), ('refresh_token', 'refresh_token'), ('expires_in', 'expires'), ('expires_on', 'expires_on'), ('not_before', 'not_before'), ('given_name', 'first_name'), ('family_name', 'last_name'), ('token_type', 'token_type') ] def get_user_id(self, details, response): """Use upn as unique id""" return response.get('upn') def get_user_details(self, response): """Return user details from Azure AD account""" fullname, first_name, last_name = ( response.get('name', ''), response.get('given_name', ''), response.get('family_name', '') ) return {'username': fullname, 'email': response.get('upn'), 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token, *args, **kwargs): response = kwargs.get('response') if response and response.get('id_token'): id_token = response.get('id_token') else: id_token = access_token try: decoded_id_token = jwt_decode(id_token, verify=False) except (DecodeError, ExpiredSignature) as de: raise AuthTokenError(self, de) return decoded_id_token def auth_extra_arguments(self): """Return extra arguments needed on auth process. The defaults can be overriden by GET parameters.""" extra_arguments = super(AzureADOAuth2, self).auth_extra_arguments() resource = self.setting('RESOURCE') if resource: extra_arguments.update({'resource': resource}) return extra_arguments def extra_data(self, user, uid, response, details=None, *args, **kwargs): """Return access_token and extra defined names to store in extra_data field""" data = super(AzureADOAuth2, self).extra_data(user, uid, response, details, *args, **kwargs) data['resource'] = self.setting('RESOURCE') return data def refresh_token_params(self, token, *args, **kwargs): return { 'client_id': self.setting('KEY'), 'client_secret': self.setting('SECRET'), 'refresh_token': token, 'grant_type': 'refresh_token', 'resource': self.setting('RESOURCE') } def get_auth_token(self, user_id): """Return the access token for the given user, after ensuring that it has not expired, or refreshing it if so.""" user = self.get_user(user_id=user_id) access_token = user.social_user.access_token expires_on = user.social_user.extra_data['expires_on'] if expires_on <= int(time.time()): new_token_response = self.refresh_token(token=access_token) access_token = new_token_response['access_token'] return access_token social-auth-core-3.1.0/social_core/backends/eventbrite.py0000644000175000017500000000204513336542652023317 0ustar omabomab00000000000000from .oauth import BaseOAuth2 class EventbriteOAuth2(BaseOAuth2): """Eventbrite OAuth2 authentication backend""" name = 'eventbrite' AUTHORIZATION_URL = 'https://www.eventbrite.com/oauth/authorize' ACCESS_TOKEN_URL = 'https://www.eventbrite.com/oauth/token' METADATA_URL = 'https://www.eventbriteapi.com/v3/users/me' ACCESS_TOKEN_METHOD = 'POST' STATE_PARAMETER = False REDIRECT_STATE = False def get_user_details(self, response): """Return user details from an Eventbrite metadata response""" email = next(iter(filter(lambda x: x['primary'], response['emails'])))['email'] return { 'username': email, 'email': email, 'first_name': response['first_name'], 'last_name': response['last_name'] } def user_data(self, access_token, *args, **kwargs): """Loads user data and datacenter information from service""" return self.get_json(self.METADATA_URL, headers={ 'Authorization': 'Bearer ' + access_token }) social-auth-core-3.1.0/social_core/backends/sketchfab.py0000644000175000017500000000252113336542652023101 0ustar omabomab00000000000000""" Sketchfab OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/sketchfab.html https://sketchfab.com/developers/oauth """ from .oauth import BaseOAuth2 class SketchfabOAuth2(BaseOAuth2): name = 'sketchfab' ID_KEY = 'uid' AUTHORIZATION_URL = 'https://sketchfab.com/oauth2/authorize/' ACCESS_TOKEN_URL = 'https://sketchfab.com/oauth2/token/' ACCESS_TOKEN_METHOD = 'POST' REDIRECT_STATE = False REQUIRES_EMAIL_VALIDATION = False EXTRA_DATA = [ ('username', 'username'), ('apiToken', 'apiToken') ] def get_user_details(self, response): """Return user details from Sketchfab account""" user_data = response email = user_data.get('email', '') username = user_data['username'] name = user_data.get('displayName', '') fullname, first_name, last_name = self.get_user_names(name) return {'username': username, 'fullname': fullname, 'first_name': first_name, 'last_name': last_name, 'email': email} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" return self.get_json('https://sketchfab.com/v2/users/me', headers={ 'Authorization': 'Bearer {0}'.format(access_token) }) social-auth-core-3.1.0/social_core/backends/beats.py0000644000175000017500000000430413342002520022224 0ustar omabomab00000000000000""" Beats backend, docs at: https://developer.beatsmusic.com/docs """ import base64 from ..utils import handle_http_errors from .oauth import BaseOAuth2 class BeatsOAuth2(BaseOAuth2): name = 'beats' SCOPE_SEPARATOR = ' ' ID_KEY = 'user_context' AUTHORIZATION_URL = \ 'https://partner.api.beatsmusic.com/v1/oauth2/authorize' ACCESS_TOKEN_URL = 'https://partner.api.beatsmusic.com/oauth2/token' ACCESS_TOKEN_METHOD = 'POST' REDIRECT_STATE = False def get_user_id(self, details, response): return response['result'][BeatsOAuth2.ID_KEY] def auth_headers(self): return { 'Authorization': 'Basic {0}'.format(base64.urlsafe_b64encode( ('{0}:{1}'.format(*self.get_key_and_secret()).encode()) )) } @handle_http_errors def auth_complete(self, *args, **kwargs): """Completes login process, must return user instance""" self.process_error(self.data) response = self.request_access_token( self.ACCESS_TOKEN_URL, data=self.auth_complete_params(self.validate_state()), headers=self.auth_headers(), method=self.ACCESS_TOKEN_METHOD ) self.process_error(response) # mashery wraps in jsonrpc if response.get('jsonrpc', None): response = response.get('result', None) return self.do_auth(response['access_token'], response=response, *args, **kwargs) def get_user_details(self, response): """Return user details from Beats account""" response = response['result'] fullname, first_name, last_name = self.get_user_names( response.get('display_name') ) return {'username': response.get('id'), 'email': response.get('email'), 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" return self.get_json( 'https://partner.api.beatsmusic.com/v1/api/me', headers={'Authorization': 'Bearer {0}'.format(access_token)} ) social-auth-core-3.1.0/social_core/backends/trello.py0000644000175000017500000000275413336542652022460 0ustar omabomab00000000000000""" Trello OAuth1 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/trello.html """ from .oauth import BaseOAuth1 class TrelloOAuth(BaseOAuth1): """Trello OAuth authentication backend""" name = 'trello' ID_KEY = 'username' AUTHORIZATION_URL = 'https://trello.com/1/OAuthAuthorizeToken' REQUEST_TOKEN_URL = 'https://trello.com/1/OAuthGetRequestToken' ACCESS_TOKEN_URL = 'https://trello.com/1/OAuthGetAccessToken' EXTRA_DATA = [ ('username', 'username'), ('email', 'email'), ('fullName', 'fullName') ] def get_user_details(self, response): """Return user details from Trello account""" fullname, first_name, last_name = self.get_user_names( response.get('fullName') ) return {'username': response.get('username'), 'email': response.get('email'), 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token): """Return user data provided""" url = 'https://trello.com/1/members/me' try: return self.get_json(url, auth=self.oauth_auth(access_token)) except ValueError: return None def auth_extra_arguments(self): return { 'name': self.setting('APP_NAME', ''), # trello default expiration is '30days' 'expiration': self.setting('EXPIRATION', 'never') } social-auth-core-3.1.0/social_core/backends/changetip.py0000644000175000017500000000154713336542652023120 0ustar omabomab00000000000000from .oauth import BaseOAuth2 class ChangeTipOAuth2(BaseOAuth2): """ChangeTip OAuth authentication backend https://www.changetip.com/api """ name = 'changetip' AUTHORIZATION_URL = 'https://www.changetip.com/o/authorize/' ACCESS_TOKEN_URL = 'https://www.changetip.com/o/token/' ACCESS_TOKEN_METHOD = 'POST' SCOPE_SEPARATOR = ' ' def get_user_details(self, response): """Return user details from ChangeTip account""" return { 'username': response['username'], 'email': response.get('email', ''), 'first_name': '', 'last_name': '', } def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" return self.get_json('https://api.changetip.com/v2/me/', params={ 'access_token': access_token }) social-auth-core-3.1.0/social_core/backends/saml.py0000644000175000017500000003144613365410437022110 0ustar omabomab00000000000000""" Backend for SAML 2.0 support Terminology: "Service Provider" (SP): Your web app "Identity Provider" (IdP): The third-party site that is authenticating users via SAML """ from onelogin.saml2.auth import OneLogin_Saml2_Auth from onelogin.saml2.settings import OneLogin_Saml2_Settings from .base import BaseAuth from ..exceptions import AuthFailed, AuthMissingParameter # Helpful constants: OID_COMMON_NAME = "urn:oid:2.5.4.3" OID_EDU_PERSON_PRINCIPAL_NAME = "urn:oid:1.3.6.1.4.1.5923.1.1.1.6" OID_EDU_PERSON_ENTITLEMENT = "urn:oid:1.3.6.1.4.1.5923.1.1.1.7" OID_GIVEN_NAME = "urn:oid:2.5.4.42" OID_MAIL = "urn:oid:0.9.2342.19200300.100.1.3" OID_SURNAME = "urn:oid:2.5.4.4" OID_USERID = "urn:oid:0.9.2342.19200300.100.1.1" class SAMLIdentityProvider(object): """Wrapper around configuration for a SAML Identity provider""" def __init__(self, name, **kwargs): """Load and parse configuration""" self.name = name # name should be a slug and must not contain a colon, which # could conflict with uid prefixing: assert ':' not in self.name and ' ' not in self.name, \ 'IdP "name" should be a slug (short, no spaces)' self.conf = kwargs def get_user_permanent_id(self, attributes): """ The most important method: Get a permanent, unique identifier for this user from the attributes supplied by the IdP. If you want to use the NameID, it's available via attributes['name_id'] """ uid = attributes[self.conf.get('attr_user_permanent_id', OID_USERID)] if isinstance(uid, list): uid = uid[0] return uid # Attributes processing: def get_user_details(self, attributes): """ Given the SAML attributes extracted from the SSO response, get the user data like name. """ return { 'fullname': self.get_attr(attributes, 'attr_full_name', OID_COMMON_NAME), 'first_name': self.get_attr(attributes, 'attr_first_name', OID_GIVEN_NAME), 'last_name': self.get_attr(attributes, 'attr_last_name', OID_SURNAME), 'username': self.get_attr(attributes, 'attr_username', OID_USERID), 'email': self.get_attr(attributes, 'attr_email', OID_MAIL), } def get_attr(self, attributes, conf_key, default_attribute): """ Internal helper method. Get the attribute 'default_attribute' out of the attributes, unless self.conf[conf_key] overrides the default by specifying another attribute to use. """ key = self.conf.get(conf_key, default_attribute) value = attributes[key] if key in attributes else None if isinstance(value, list): value = value[0] return value @property def entity_id(self): """Get the entity ID for this IdP""" # Required. e.g. "https://idp.testshib.org/idp/shibboleth" return self.conf['entity_id'] @property def sso_url(self): """Get the SSO URL for this IdP""" # Required. e.g. # "https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO" return self.conf['url'] @property def saml_config_dict(self): """Get the IdP configuration dict in the format required by python-saml""" result = { 'entityId': self.entity_id, 'singleSignOnService': { 'url': self.sso_url, # python-saml only supports Redirect 'binding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect' }, } cert = self.conf.get('x509cert', None) if cert: result['x509cert'] = cert return result cert = self.conf.get('x509certMulti', None) if cert: result['x509certMulti'] = cert return result raise KeyError("IDP must contain x509cert or x509certMulti") class DummySAMLIdentityProvider(SAMLIdentityProvider): """ A placeholder IdP used when we must specify something, e.g. when generating SP metadata. If OneLogin_Saml2_Auth is modified to not always require IdP config, this can be removed. """ def __init__(self): super(DummySAMLIdentityProvider, self).__init__( 'dummy', entity_id='https://dummy.none/saml2', url='https://dummy.none/SSO', x509cert='' ) class SAMLAuth(BaseAuth): """ PSA Backend that implements SAML 2.0 Service Provider (SP) functionality. Unlike all of the other backends, this one can be configured to work with many identity providers (IdPs). For example, a University that belongs to a Shibboleth federation may support authentication via ~100 partner universities. Also, the IdP configuration can be changed at runtime if you require that functionality - just subclass this and override `get_idp()`. Several settings are required. Here's an example: SOCIAL_AUTH_SAML_SP_ENTITY_ID = "https://saml.example.com/" SOCIAL_AUTH_SAML_SP_PUBLIC_CERT = "... X.509 certificate string ..." SOCIAL_AUTH_SAML_SP_PRIVATE_KEY = "... private key ..." SOCIAL_AUTH_SAML_ORG_INFO = { "en-US": { "name": "example", "displayname": "Example Inc.", "url": "http://example.com" } } SOCIAL_AUTH_SAML_TECHNICAL_CONTACT = { "givenName": "Tech Gal", "emailAddress": "technical@example.com" } SOCIAL_AUTH_SAML_SUPPORT_CONTACT = { "givenName": "Support Guy", "emailAddress": "support@example.com" } SOCIAL_AUTH_SAML_ENABLED_IDPS = { "testshib": { "entity_id": "https://idp.testshib.org/idp/shibboleth", "url": "https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO", "x509cert": "MIIEDjCCAvagAwIBAgIBADANBgkqhkiG9w0B... ...8Bbnl+ev0peYzxFyF5sQA==", } } Optional settings: SOCIAL_AUTH_SAML_SP_EXTRA = {} SOCIAL_AUTH_SAML_SECURITY_CONFIG = {} """ name = "saml" EXTRA_DATA = [] def get_idp(self, idp_name): """Given the name of an IdP, get a SAMLIdentityProvider instance""" idp_config = self.setting('ENABLED_IDPS')[idp_name] return SAMLIdentityProvider(idp_name, **idp_config) def generate_saml_config(self, idp=None): """ Generate the configuration required to instantiate OneLogin_Saml2_Auth """ # The shared absolute URL that all IdPs redirect back to - # this is specified in our metadata.xml: abs_completion_url = self.redirect_uri config = { 'contactPerson': { 'technical': self.setting('TECHNICAL_CONTACT'), 'support': self.setting('SUPPORT_CONTACT') }, 'debug': True, 'idp': idp.saml_config_dict if idp else {}, 'organization': self.setting('ORG_INFO'), 'security': { 'metadataValidUntil': '', 'metadataCacheDuration': 'P10D', # metadata valid for ten days }, 'sp': { 'assertionConsumerService': { 'url': abs_completion_url, # python-saml only supports HTTP-POST 'binding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST' }, 'entityId': self.setting('SP_ENTITY_ID'), 'x509cert': self.setting('SP_PUBLIC_CERT'), 'privateKey': self.setting('SP_PRIVATE_KEY'), }, 'strict': True, # We must force strict mode - for security } config["security"].update(self.setting("SECURITY_CONFIG", {})) config["sp"].update(self.setting("SP_EXTRA", {})) return config def generate_metadata_xml(self): """ Helper method that can be used from your web app to generate the XML metadata required to link your web app as a Service Provider. Returns (metadata XML string, list of errors) Example usage (Django): from ..apps.django_app.utils import load_strategy, \ load_backend def saml_metadata_view(request): complete_url = reverse('social:complete', args=("saml", )) saml_backend = load_backend(load_strategy(request), "saml", complete_url) metadata, errors = saml_backend.generate_metadata_xml() if not errors: return HttpResponse(content=metadata, content_type='text/xml') return HttpResponseServerError(content=', '.join(errors)) """ config = self.generate_saml_config() saml_settings = OneLogin_Saml2_Settings( config, sp_validation_only=True ) metadata = saml_settings.get_sp_metadata() errors = saml_settings.validate_metadata(metadata) return metadata, errors def _create_saml_auth(self, idp): """Get an instance of OneLogin_Saml2_Auth""" config = self.generate_saml_config(idp) request_info = { 'https': 'on' if self.strategy.request_is_secure() else 'off', 'http_host': self.strategy.request_host(), 'script_name': self.strategy.request_path(), 'server_port': self.strategy.request_port(), 'get_data': self.strategy.request_get(), 'post_data': self.strategy.request_post(), } return OneLogin_Saml2_Auth(request_info, config) def auth_url(self): """Get the URL to which we must redirect in order to authenticate the user""" try: idp_name = self.strategy.request_data()['idp'] except KeyError: raise AuthMissingParameter(self, 'idp') auth = self._create_saml_auth(idp=self.get_idp(idp_name)) # Below, return_to sets the RelayState, which can contain # arbitrary data. We use it to store the specific SAML IdP # name, since we multiple IdPs share the same auth_complete # URL. return auth.login(return_to=idp_name) def get_user_details(self, response): """Get user details like full name, email, etc. from the response - see auth_complete""" idp = self.get_idp(response['idp_name']) return idp.get_user_details(response['attributes']) def get_user_id(self, details, response): """ Get the permanent ID for this user from the response. We prefix each ID with the name of the IdP so that we can connect multiple IdPs to this user. """ idp = self.get_idp(response['idp_name']) uid = idp.get_user_permanent_id(response['attributes']) return '{0}:{1}'.format(idp.name, uid) def auth_complete(self, *args, **kwargs): """ The user has been redirected back from the IdP and we should now log them in, if everything checks out. """ idp_name = self.strategy.request_data()['RelayState'] idp = self.get_idp(idp_name) auth = self._create_saml_auth(idp) auth.process_response() errors = auth.get_errors() if errors or not auth.is_authenticated(): reason = auth.get_last_error_reason() raise AuthFailed( self, 'SAML login failed: {0} ({1})'.format(errors, reason) ) attributes = auth.get_attributes() attributes['name_id'] = auth.get_nameid() self._check_entitlements(idp, attributes) response = { 'idp_name': idp_name, 'attributes': attributes, 'session_index': auth.get_session_index(), } kwargs.update({'response': response, 'backend': self}) return self.strategy.authenticate(*args, **kwargs) def extra_data(self, user, uid, response, details=None, *args, **kwargs): return super(SAMLAuth, self).extra_data(user, uid, response['attributes'], details=details, *args, **kwargs) def _check_entitlements(self, idp, attributes): """ Additional verification of a SAML response before authenticating the user. Subclasses can override this method if they need custom validation code, such as requiring the presence of an eduPersonEntitlement. raise social_core.exceptions.AuthForbidden if the user should not be authenticated, or do nothing to allow the login pipeline to continue. """ pass social-auth-core-3.1.0/social_core/backends/edmodo.py0000644000175000017500000000214413336542652022417 0ustar omabomab00000000000000""" Edmodo OAuth2 Sign-in backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/edmodo.html """ from .oauth import BaseOAuth2 class EdmodoOAuth2(BaseOAuth2): """Edmodo OAuth2""" name = 'edmodo' AUTHORIZATION_URL = 'https://api.edmodo.com/oauth/authorize' ACCESS_TOKEN_URL = 'https://api.edmodo.com/oauth/token' ACCESS_TOKEN_METHOD = 'POST' def get_user_details(self, response): """Return user details from Edmodo account""" fullname, first_name, last_name = self.get_user_names( first_name=response.get('first_name'), last_name=response.get('last_name') ) return { 'username': response.get('username'), 'email': response.get('email'), 'fullname': fullname, 'first_name': first_name, 'last_name': last_name } def user_data(self, access_token, *args, **kwargs): """Loads user data from Edmodo""" return self.get_json( 'https://api.edmodo.com/users/me', params={'access_token': access_token} ) social-auth-core-3.1.0/social_core/backends/stackoverflow.py0000644000175000017500000000266313336542652024047 0ustar omabomab00000000000000""" Stackoverflow OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/stackoverflow.html """ from .oauth import BaseOAuth2 class StackoverflowOAuth2(BaseOAuth2): """Stackoverflow OAuth2 authentication backend""" name = 'stackoverflow' ID_KEY = 'user_id' AUTHORIZATION_URL = 'https://stackexchange.com/oauth' ACCESS_TOKEN_URL = 'https://stackexchange.com/oauth/access_token' ACCESS_TOKEN_METHOD = 'POST' SCOPE_SEPARATOR = ',' EXTRA_DATA = [ ('id', 'id'), ('expires', 'expires') ] def get_user_details(self, response): """Return user details from Stackoverflow account""" fullname, first_name, last_name = self.get_user_names( response.get('display_name') ) return {'username': response.get('link').rsplit('/', 1)[-1], 'full_name': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" return self.get_json( 'https://api.stackexchange.com/2.1/me', params={ 'site': 'stackoverflow', 'access_token': access_token, 'key': self.setting('API_KEY') } )['items'][0] def request_access_token(self, *args, **kwargs): return self.get_querystring(*args, **kwargs) social-auth-core-3.1.0/social_core/backends/echosign.py0000644000175000017500000000151413336542652022747 0ustar omabomab00000000000000from .oauth import BaseOAuth2 class EchosignOAuth2(BaseOAuth2): name = 'echosign' REDIRECT_STATE = False ACCESS_TOKEN_METHOD = 'POST' REFRESH_TOKEN_METHOD = 'POST' REVOKE_TOKEN_METHOD = 'POST' AUTHORIZATION_URL = 'https://secure.echosign.com/public/oauth' ACCESS_TOKEN_URL = 'https://secure.echosign.com/oauth/token' REFRESH_TOKEN_URL = 'https://secure.echosign.com/oauth/refresh' REVOKE_TOKEN_URL = 'https://secure.echosign.com/oauth/revoke' def get_user_details(self, response): return response def get_user_id(self, details, response): return details['userInfoList'][0]['userId'] def user_data(self, access_token, *args, **kwargs): return self.get_json( 'https://api.echosign.com/api/rest/v3/users', headers={'Access-Token': access_token}) social-auth-core-3.1.0/social_core/backends/udata.py0000644000175000017500000000217113336542652022246 0ustar omabomab00000000000000""" Udata related backends. Docs at: https://python-social-auth.readthedocs.io/en/latest/backends/udata.html """ from .oauth import BaseOAuth2 class UdataBaseOAuth2(BaseOAuth2): """Udata base OAuth authentication backend.""" SCOPE_SEPARATOR = ',' REDIRECT_STATE = False DEFAULT_SCOPE = ['default'] ACCESS_TOKEN_METHOD = 'POST' def get_user_details(self, response): """Return user details from Udata account.""" return { 'username': response.get('first_name'), 'email': response.get('email') or '', 'first_name': response.get('first_name') } def user_data(self, access_token, *args, **kwargs): """Load user data from service.""" return self.get_json(self.USER_DATA_URL, params={ 'access_token': access_token }) class DatagouvfrOAuth2(UdataBaseOAuth2): """Datagouvfr OAuth authentication backend.""" name = 'datagouv' ACCESS_TOKEN_URL = 'https://www.data.gouv.fr/oauth/token' AUTHORIZATION_URL = 'https://www.data.gouv.fr/oauth/authorize' USER_DATA_URL = 'https://www.data.gouv.fr/api/1/me/' social-auth-core-3.1.0/social_core/backends/belgiumeid.py0000644000175000017500000000052613336542652023260 0ustar omabomab00000000000000""" Belgium EID OpenId backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/belgium_eid.html """ from .open_id import OpenIdAuth class BelgiumEIDOpenId(OpenIdAuth): """Belgium e-ID OpenID authentication backend""" name = 'belgiumeid' URL = 'https://www.e-contract.be/eid-idp/endpoints/openid/auth' social-auth-core-3.1.0/social_core/backends/professionali.py0000644000175000017500000000353713336542652024034 0ustar omabomab00000000000000# -*- coding: utf-8 -*- """ Professionaly OAuth 2.0 support. This contribution adds support for professionaly.ru OAuth 2.0. Username is retrieved from the identity returned by server. """ from time import time from ..utils import parse_qs from .oauth import BaseOAuth2 class ProfessionaliOAuth2(BaseOAuth2): name = 'professionali' ID_KEY = 'user_id' AUTHORIZATION_URL = 'https://api.professionali.ru/oauth/authorize.html' ACCESS_TOKEN_URL = 'https://api.professionali.ru/oauth/getToken.json' ACCESS_TOKEN_METHOD = 'POST' EXTRA_DATA = [ ('avatar_big', 'avatar_big'), ('link', 'link') ] def get_user_details(self, response): first_name, last_name = map(response.get, ('firstname', 'lastname')) email = '' if self.setting('FAKE_EMAIL'): email = '{0}@professionali.ru'.format(time()) return { 'username': '{0}_{1}'.format(last_name, first_name), 'first_name': first_name, 'last_name': last_name, 'email': email } def user_data(self, access_token, response, *args, **kwargs): url = 'https://api.professionali.ru/v6/users/get.json' fields = list(set(['firstname', 'lastname', 'avatar_big', 'link'] + self.setting('EXTRA_DATA', []))) params = { 'fields': ','.join(fields), 'access_token': access_token, 'ids[]': response['user_id'] } try: return self.get_json(url, params)[0] except (TypeError, KeyError, IOError, ValueError, IndexError): return None def get_json(self, url, *args, **kwargs): return self.request(url, verify=False, *args, **kwargs).json() def get_querystring(self, url, *args, **kwargs): return parse_qs(self.request(url, verify=False, *args, **kwargs).text) social-auth-core-3.1.0/social_core/backends/rdio.py0000644000175000017500000000464713336542652022117 0ustar omabomab00000000000000""" Rdio OAuth1 and OAuth2 backends, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/rdio.html """ from .oauth import BaseOAuth1, BaseOAuth2, OAuthAuth RDIO_API = 'https://www.rdio.com/api/1/' class BaseRdio(OAuthAuth): ID_KEY = 'key' def get_user_details(self, response): fullname, first_name, last_name = self.get_user_names( fullname=response['displayName'], first_name=response['firstName'], last_name=response['lastName'] ) return { 'username': response['username'], 'fullname': fullname, 'first_name': first_name, 'last_name': last_name } class RdioOAuth1(BaseRdio, BaseOAuth1): """Rdio OAuth authentication backend""" name = 'rdio-oauth1' REQUEST_TOKEN_URL = 'http://api.rdio.com/oauth/request_token' AUTHORIZATION_URL = 'https://www.rdio.com/oauth/authorize' ACCESS_TOKEN_URL = 'http://api.rdio.com/oauth/access_token' EXTRA_DATA = [ ('key', 'rdio_id'), ('icon', 'rdio_icon_url'), ('url', 'rdio_profile_url'), ('username', 'rdio_username'), ('streamRegion', 'rdio_stream_region'), ] def user_data(self, access_token, *args, **kwargs): """Return user data provided""" params = {'method': 'currentUser', 'extras': 'username,displayName,streamRegion'} request = self.oauth_request(access_token, RDIO_API, params, method='POST') return self.get_json(request.url, method='POST', data=request.to_postdata())['result'] class RdioOAuth2(BaseRdio, BaseOAuth2): name = 'rdio-oauth2' AUTHORIZATION_URL = 'https://www.rdio.com/oauth2/authorize' ACCESS_TOKEN_URL = 'https://www.rdio.com/oauth2/token' ACCESS_TOKEN_METHOD = 'POST' EXTRA_DATA = [ ('key', 'rdio_id'), ('icon', 'rdio_icon_url'), ('url', 'rdio_profile_url'), ('username', 'rdio_username'), ('streamRegion', 'rdio_stream_region'), ('refresh_token', 'refresh_token', True), ('token_type', 'token_type', True), ] def user_data(self, access_token, *args, **kwargs): return self.get_json(RDIO_API, method='POST', data={ 'method': 'currentUser', 'extras': 'username,displayName,streamRegion', 'access_token': access_token })['result'] social-auth-core-3.1.0/social_core/backends/khanacademy.py0000644000175000017500000001167413336542652023425 0ustar omabomab00000000000000""" Khan Academy OAuth backend, docs at: https://github.com/Khan/khan-api/wiki/Khan-Academy-API-Authentication """ import six from six.moves.urllib_parse import urlencode from oauthlib.oauth1 import SIGNATURE_HMAC, SIGNATURE_TYPE_QUERY from requests_oauthlib import OAuth1 from .oauth import BaseOAuth1 class BrowserBasedOAuth1(BaseOAuth1): """Browser based mechanism OAuth authentication, fill the needed parameters to communicate properly with authentication service. REQUEST_TOKEN_URL Request token URL (opened in web browser) ACCESS_TOKEN_URL Access token URL """ REQUEST_TOKEN_URL = '' OAUTH_TOKEN_PARAMETER_NAME = 'oauth_token' REDIRECT_URI_PARAMETER_NAME = 'redirect_uri' ACCESS_TOKEN_URL = '' def auth_url(self): """Return redirect url""" return self.unauthorized_token_request() def get_unauthorized_token(self): return self.strategy.request_data() def unauthorized_token_request(self): """Return request for unauthorized token (first stage)""" params = self.request_token_extra_arguments() params.update(self.get_scope_argument()) key, secret = self.get_key_and_secret() # decoding='utf-8' produces errors with python-requests on Python3 # since the final URL will be of type bytes decoding = None if six.PY3 else 'utf-8' state = self.get_or_create_state() auth = OAuth1( key, secret, callback_uri=self.get_redirect_uri(state), decoding=decoding, signature_method=SIGNATURE_HMAC, signature_type=SIGNATURE_TYPE_QUERY ) url = self.REQUEST_TOKEN_URL + '?' + urlencode(params) url, _, _ = auth.client.sign(url) return url def oauth_auth(self, token=None, oauth_verifier=None): key, secret = self.get_key_and_secret() oauth_verifier = oauth_verifier or self.data.get('oauth_verifier') token = token or {} # decoding='utf-8' produces errors with python-requests on Python3 # since the final URL will be of type bytes decoding = None if six.PY3 else 'utf-8' state = self.get_or_create_state() return OAuth1(key, secret, resource_owner_key=token.get('oauth_token'), resource_owner_secret=token.get('oauth_token_secret'), callback_uri=self.get_redirect_uri(state), verifier=oauth_verifier, signature_method=SIGNATURE_HMAC, signature_type=SIGNATURE_TYPE_QUERY, decoding=decoding) class KhanAcademyOAuth1(BrowserBasedOAuth1): """ Class used for autorising with Khan Academy. Flow of Khan Academy is a bit different than most OAuth 1.0 and consinsts of the following steps: 1. Create signed params to attach to the REQUEST_TOKEN_URL 2. Redirect user to the REQUEST_TOKEN_URL that will respond with oauth_secret, oauth_token, oauth_verifier that should be used with ACCESS_TOKEN_URL 3. Go to ACCESS_TOKEN_URL and grab oauth_token_secret. Note that we don't use the AUTHORIZATION_URL. REQUEST_TOKEN_URL requires the following arguments: oauth_consumer_key - Your app's consumer key oauth_nonce - Random 64-bit, unsigned number encoded as an ASCII string in decimal format. The nonce/timestamp pair should always be unique. oauth_version - OAuth version used by your app. Must be "1.0" for now. oauth_signature - String generated using the referenced signature method. oauth_signature_method - Signature algorithm (currently only support "HMAC-SHA1") oauth_timestamp - Integer representing the time the request is sent. The timestamp should be expressed in number of seconds after January 1, 1970 00:00:00 GMT. oauth_callback (optional) - URL to redirect to after request token is received and authorized by the user's chosen identity provider. """ name = 'khanacademy-oauth1' ID_KEY = 'user_id' REQUEST_TOKEN_URL = 'http://www.khanacademy.org/api/auth/request_token' ACCESS_TOKEN_URL = 'https://www.khanacademy.org/api/auth/access_token' REDIRECT_URI_PARAMETER_NAME = 'oauth_callback' USER_DATA_URL = 'https://www.khanacademy.org/api/v1/user' EXTRA_DATA = [('user_id', 'user_id')] def get_user_details(self, response): """Return user details from Khan Academy account""" return { 'username': response.get('email'), 'email': response.get('email'), 'fullname': response.get('nickname'), 'first_name': '', 'last_name': '', 'user_id': response.get('user_id') } def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" auth = self.oauth_auth(access_token) url, _, _ = auth.client.sign(self.USER_DATA_URL) return self.get_json(url) social-auth-core-3.1.0/social_core/backends/meetup.py0000644000175000017500000000225513336542652022452 0ustar omabomab00000000000000""" Meetup OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/meetup.html """ from .oauth import BaseOAuth2 class MeetupOAuth2(BaseOAuth2): """Meetup OAuth2 authentication backend""" name = 'meetup' AUTHORIZATION_URL = 'https://secure.meetup.com/oauth2/authorize' ACCESS_TOKEN_URL = 'https://secure.meetup.com/oauth2/access' ACCESS_TOKEN_METHOD = 'POST' DEFAULT_SCOPE = ['basic'] SCOPE_SEPARATOR = ',' REDIRECT_STATE = False STATE_PARAMETER = 'state' def get_user_details(self, response): """Return user details from Meetup account""" fullname, first_name, last_name = self.get_user_names( response.get('name') ) return {'username': response.get('username'), 'email': response.get('email') or '', 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" return self.get_json('https://api.meetup.com/2/member/self', params={'access_token': access_token}) social-auth-core-3.1.0/social_core/backends/lastfm.py0000644000175000017500000000351413336542652022440 0ustar omabomab00000000000000import hashlib from ..utils import handle_http_errors from .base import BaseAuth class LastFmAuth(BaseAuth): """ Last.Fm authentication backend. Requires two settings: SOCIAL_AUTH_LASTFM_KEY SOCIAL_AUTH_LASTFM_SECRET Don't forget to set the Last.fm callback to something sensible like http://your.site/lastfm/complete """ name = 'lastfm' AUTH_URL = 'http://www.last.fm/api/auth/?api_key={api_key}' EXTRA_DATA = [ ('key', 'session_key') ] def auth_url(self): return self.AUTH_URL.format(api_key=self.setting('KEY')) @handle_http_errors def auth_complete(self, *args, **kwargs): """Completes login process, must return user instance""" key, secret = self.get_key_and_secret() token = self.data['token'] signature = hashlib.md5(''.join( ('api_key', key, 'methodauth.getSession', 'token', token, secret) ).encode()).hexdigest() response = self.get_json('http://ws.audioscrobbler.com/2.0/', data={ 'method': 'auth.getSession', 'api_key': key, 'token': token, 'api_sig': signature, 'format': 'json' }, method='POST') kwargs.update({'response': response['session'], 'backend': self}) return self.strategy.authenticate(*args, **kwargs) def get_user_id(self, details, response): """Return a unique ID for the current user, by default from server response.""" return response.get('name') def get_user_details(self, response): fullname, first_name, last_name = self.get_user_names(response['name']) return { 'username': response['name'], 'email': '', 'fullname': fullname, 'first_name': first_name, 'last_name': last_name } social-auth-core-3.1.0/social_core/backends/soundcloud.py0000644000175000017500000000417513336542652023335 0ustar omabomab00000000000000""" Soundcloud OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/soundcloud.html """ from six.moves.urllib_parse import urlencode from .oauth import BaseOAuth2 class SoundcloudOAuth2(BaseOAuth2): """Soundcloud OAuth authentication backend""" name = 'soundcloud' AUTHORIZATION_URL = 'https://soundcloud.com/connect' ACCESS_TOKEN_URL = 'https://api.soundcloud.com/oauth2/token' ACCESS_TOKEN_METHOD = 'POST' SCOPE_SEPARATOR = ',' REDIRECT_STATE = False EXTRA_DATA = [ ('id', 'id'), ('refresh_token', 'refresh_token'), ('expires', 'expires') ] def get_user_details(self, response): """Return user details from Soundcloud account""" fullname, first_name, last_name = self.get_user_names( response.get('full_name') ) return {'username': response.get('username'), 'email': response.get('email') or '', 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" return self.get_json('https://api.soundcloud.com/me.json', params={'oauth_token': access_token}) def auth_url(self): """Return redirect url""" state = None if self.STATE_PARAMETER or self.REDIRECT_STATE: # Store state in session for further request validation. The state # value is passed as state parameter (as specified in OAuth2 spec), # but also added to redirect_uri, that way we can still verify the # request if the provider doesn't implement the state parameter. # Reuse token if any. name = self.name + '_state' state = self.strategy.session_get(name) or self.state_token() self.strategy.session_set(name, state) params = self.auth_params(state) params.update(self.get_scope_argument()) params.update(self.auth_extra_arguments()) return self.AUTHORIZATION_URL + '?' + urlencode(params) social-auth-core-3.1.0/social_core/backends/azuread_tenant.py0000644000175000017500000001027113417122006024137 0ustar omabomab00000000000000import base64 import json from cryptography.x509 import load_pem_x509_certificate from cryptography.hazmat.backends import default_backend from jwt import DecodeError, ExpiredSignature, decode as jwt_decode from ..exceptions import AuthTokenError from .azuread import AzureADOAuth2 """ Copyright (c) 2015 Microsoft Open Technologies, Inc. All rights reserved. MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ """ Azure AD OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/azuread.html See https://nicksnettravels.builttoroam.com/post/2017/01/24/Verifying-Azure-Active-Directory-JWT-Tokens.aspx for verifying JWT tokens. """ class AzureADTenantOAuth2(AzureADOAuth2): name = 'azuread-tenant-oauth2' OPENID_CONFIGURATION_URL = \ 'https://login.microsoftonline.com/{tenant_id}/.well-known/openid-configuration' AUTHORIZATION_URL = \ 'https://login.microsoftonline.com/{tenant_id}/oauth2/authorize' ACCESS_TOKEN_URL = 'https://login.microsoftonline.com/{tenant_id}/oauth2/token' JWKS_URL = 'https://login.microsoftonline.com/{tenant_id}/discovery/keys' @property def tenant_id(self): return self.setting('TENANT_ID', 'common') def openid_configuration_url(self): return self.OPENID_CONFIGURATION_URL.format(tenant_id=self.tenant_id) def authorization_url(self): return self.AUTHORIZATION_URL.format(tenant_id=self.tenant_id) def access_token_url(self): return self.ACCESS_TOKEN_URL.format(tenant_id=self.tenant_id) def jwks_url(self): return self.JWKS_URL.format(tenant_id=self.tenant_id) def get_certificate(self, kid): # retrieve keys from jwks_url resp = self.request(self.jwks_url(), method='GET') resp.raise_for_status() # find the proper key for the kid for key in resp.json()['keys']: if key['kid'] == kid: x5c = key['x5c'][0] break else: raise DecodeError('Cannot find kid={}'.format(kid)) certificate = '-----BEGIN CERTIFICATE-----\n' \ '{}\n' \ '-----END CERTIFICATE-----'.format(x5c) return load_pem_x509_certificate(certificate.encode(), default_backend()) def get_user_id(self, details, response): """Use subject (sub) claim as unique id.""" return response.get('sub') def user_data(self, access_token, *args, **kwargs): response = kwargs.get('response') id_token = response.get('id_token') # decode the JWT header as JSON dict jwt_header = json.loads( base64.b64decode(id_token.split('.', 1)[0]).decode() ) # get key id and algorithm key_id = jwt_header['kid'] algorithm = jwt_header['alg'] try: # retrieve certificate for key_id certificate = self.get_certificate(key_id) return jwt_decode( id_token, key=certificate.public_key(), algorithms=algorithm, audience=self.setting('KEY') ) except (DecodeError, ExpiredSignature) as error: raise AuthTokenError(self, error) social-auth-core-3.1.0/social_core/backends/coinbase.py0000644000175000017500000000252113336542652022732 0ustar omabomab00000000000000""" Coinbase OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/coinbase.html """ from .oauth import BaseOAuth2 class CoinbaseOAuth2(BaseOAuth2): name = 'coinbase' SCOPE_SEPARATOR = '+' DEFAULT_SCOPE = ['user', 'balance'] AUTHORIZATION_URL = 'https://www.coinbase.com/oauth/authorize' ACCESS_TOKEN_URL = 'https://api.coinbase.com/oauth/token' REVOKE_TOKEN_URL = 'https://api.coinbase.com/oauth/revoke' ACCESS_TOKEN_METHOD = 'POST' REDIRECT_STATE = False def get_user_id(self, details, response): return response['data']['id'] def get_user_details(self, response): """Return user details from Coinbase account""" user_data = response['data'] email = user_data.get('email', '') name = user_data['name'] username = user_data.get('username') fullname, first_name, last_name = self.get_user_names(name) return {'username': username, 'fullname': fullname, 'first_name': first_name, 'last_name': last_name, 'email': email} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" return self.get_json('https://api.coinbase.com/v2/user', headers={'Authorization': 'Bearer ' + access_token}) social-auth-core-3.1.0/social_core/backends/yammer.py0000644000175000017500000000300313336542652022435 0ustar omabomab00000000000000""" Yammer OAuth2 production and staging backends, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/yammer.html """ from .oauth import BaseOAuth2 class YammerOAuth2(BaseOAuth2): name = 'yammer' AUTHORIZATION_URL = 'https://www.yammer.com/dialog/oauth' ACCESS_TOKEN_URL = 'https://www.yammer.com/oauth2/access_token' EXTRA_DATA = [ ('id', 'id'), ('expires', 'expires'), ('mugshot_url', 'mugshot_url') ] def get_user_id(self, details, response): return response['user']['id'] def get_user_details(self, response): username = response['user']['name'] fullname, first_name, last_name = self.get_user_names( fullname=response['user']['full_name'], first_name=response['user']['first_name'], last_name=response['user']['last_name'] ) email = response['user']['contact']['email_addresses'][0]['address'] mugshot_url = response['user']['mugshot_url'] return { 'username': username, 'email': email, 'fullname': fullname, 'first_name': first_name, 'last_name': last_name, 'picture_url': mugshot_url } class YammerStagingOAuth2(YammerOAuth2): name = 'yammer-staging' AUTHORIZATION_URL = 'https://www.staging.yammer.com/dialog/oauth' ACCESS_TOKEN_URL = 'https://www.staging.yammer.com/oauth2/access_token' REQUEST_TOKEN_URL = 'https://www.staging.yammer.com/oauth2/request_token' social-auth-core-3.1.0/social_core/backends/mendeley.py0000644000175000017500000000431013336542652022747 0ustar omabomab00000000000000""" Mendeley OAuth1 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/mendeley.html """ from .oauth import BaseOAuth1, BaseOAuth2 class MendeleyMixin(object): SCOPE_SEPARATOR = '+' EXTRA_DATA = [('profile_id', 'profile_id'), ('name', 'name'), ('bio', 'bio')] def get_user_id(self, details, response): return response['id'] def get_user_details(self, response): """Return user details from Mendeley account""" profile_id = response['id'] name = response['display_name'] bio = response['link'] return {'profile_id': profile_id, 'name': name, 'bio': bio} def user_data(self, access_token, *args, **kwargs): """Return user data provided""" values = self.get_user_data(access_token) values.update(values) return values def get_user_data(self, access_token): raise NotImplementedError('Implement in subclass') class MendeleyOAuth(MendeleyMixin, BaseOAuth1): name = 'mendeley' AUTHORIZATION_URL = 'http://api.mendeley.com/oauth/authorize/' REQUEST_TOKEN_URL = 'http://api.mendeley.com/oauth/request_token/' ACCESS_TOKEN_URL = 'http://api.mendeley.com/oauth/access_token/' def get_user_data(self, access_token): return self.get_json( 'http://api.mendeley.com/oapi/profiles/info/me/', auth=self.oauth_auth(access_token) ) class MendeleyOAuth2(MendeleyMixin, BaseOAuth2): name = 'mendeley-oauth2' AUTHORIZATION_URL = 'https://api-oauth2.mendeley.com/oauth/authorize' ACCESS_TOKEN_URL = 'https://api-oauth2.mendeley.com/oauth/token' ACCESS_TOKEN_METHOD = 'POST' DEFAULT_SCOPE = ['all'] REDIRECT_STATE = False EXTRA_DATA = MendeleyMixin.EXTRA_DATA + [ ('refresh_token', 'refresh_token'), ('expires_in', 'expires_in'), ('token_type', 'token_type'), ] def get_user_data(self, access_token, *args, **kwargs): """Loads user data from service""" return self.get_json( 'https://api.mendeley.com/profiles/me/', headers={'Authorization': 'Bearer {0}'.format(access_token)} ) social-auth-core-3.1.0/social_core/backends/vend.py0000644000175000017500000000231313336542652022102 0ustar omabomab00000000000000""" Vend OAuth2 backend: """ from .oauth import BaseOAuth2 class VendOAuth2(BaseOAuth2): name = 'vend' AUTHORIZATION_URL = 'https://secure.vendhq.com/connect' ACCESS_TOKEN_URL = 'https://{0}.vendhq.com/api/1.0/token' ACCESS_TOKEN_METHOD = 'POST' REDIRECT_STATE = False EXTRA_DATA = [ ('refresh_token', 'refresh_token'), ('domain_prefix', 'domain_prefix') ] def access_token_url(self): return self.ACCESS_TOKEN_URL.format(self.data['domain_prefix']) def get_user_details(self, response): email = response['email'] username = response.get('username') or email.split('@', 1)[0] return { 'username': username, 'email': email, 'fullname': '', 'first_name': '', 'last_name': '' } def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" prefix = kwargs['response']['domain_prefix'] url = 'https://{0}.vendhq.com/api/users'.format(prefix) data = self.get_json(url, headers={ 'Authorization': 'Bearer {0}'.format(access_token) }) return data['users'][0] if data.get('users') else {} social-auth-core-3.1.0/social_core/backends/fitbit.py0000644000175000017500000000451313336542652022433 0ustar omabomab00000000000000""" Fitbit OAuth backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/fitbit.html """ import base64 from .oauth import BaseOAuth1, BaseOAuth2 class FitbitOAuth1(BaseOAuth1): """Fitbit OAuth1 authentication backend""" name = 'fitbit' AUTHORIZATION_URL = 'https://www.fitbit.com/oauth/authorize' REQUEST_TOKEN_URL = 'https://api.fitbit.com/oauth/request_token' ACCESS_TOKEN_URL = 'https://api.fitbit.com/oauth/access_token' ID_KEY = 'encodedId' EXTRA_DATA = [('encodedId', 'id'), ('displayName', 'username')] def get_user_details(self, response): """Return user details from Fitbit account""" return {'username': response.get('displayName'), 'email': ''} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" return self.get_json( 'https://api.fitbit.com/1/user/-/profile.json', auth=self.oauth_auth(access_token) )['user'] class FitbitOAuth2(BaseOAuth2): """Fitbit OAuth2 authentication backend""" name = 'fitbit' AUTHORIZATION_URL = 'https://www.fitbit.com/oauth2/authorize' ACCESS_TOKEN_URL = 'https://api.fitbit.com/oauth2/token' ACCESS_TOKEN_METHOD = 'POST' REFRESH_TOKEN_URL = 'https://api.fitbit.com/oauth2/token' DEFAULT_SCOPE = ['profile'] ID_KEY = 'encodedId' REDIRECT_STATE = False EXTRA_DATA = [('expires_in', 'expires'), ('refresh_token', 'refresh_token', True), ('encodedId', 'id'), ('displayName', 'username')] def get_user_details(self, response): """Return user details from Fitbit account""" return {'username': response.get('displayName'), 'email': ''} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" auth_header = {"Authorization": "Bearer %s" % access_token} return self.get_json( 'https://api.fitbit.com/1/user/-/profile.json', headers=auth_header )['user'] def auth_headers(self): tokens = '{0}:{1}'.format(*self.get_key_and_secret()) tokens = base64.urlsafe_b64encode(tokens.encode()) tokens = tokens.decode() return { 'Authorization': 'Basic {0}'.format(tokens) } social-auth-core-3.1.0/social_core/backends/steam.py0000644000175000017500000000301713336542652022261 0ustar omabomab00000000000000""" Steam OpenId backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/steam.html """ from .open_id import OpenIdAuth from ..exceptions import AuthFailed USER_INFO = 'http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?' class SteamOpenId(OpenIdAuth): name = 'steam' URL = 'https://steamcommunity.com/openid' def get_user_id(self, details, response): """Return user unique id provided by service""" return self._user_id(response) def get_user_details(self, response): player = self.get_json(USER_INFO, params={ 'key': self.setting('API_KEY'), 'steamids': self._user_id(response) }) if len(player['response']['players']) > 0: player = player['response']['players'][0] details = {'username': player.get('personaname'), 'email': '', 'fullname': '', 'first_name': '', 'last_name': '', 'player': player} else: details = {} return details def consumer(self): # Steam seems to support stateless mode only, ignore store if not hasattr(self, '_consumer'): self._consumer = self.create_consumer() return self._consumer def _user_id(self, response): user_id = response.identity_url.rsplit('/', 1)[-1] if not user_id.isdigit(): raise AuthFailed(self, 'Missing Steam Id') return user_id social-auth-core-3.1.0/social_core/backends/arcgis.py0000644000175000017500000000204313336542652022416 0ustar omabomab00000000000000""" ArcGIS OAuth2 backend """ from .oauth import BaseOAuth2 class ArcGISOAuth2(BaseOAuth2): name = 'arcgis' ID_KEY = 'username' AUTHORIZATION_URL = 'https://www.arcgis.com/sharing/rest/oauth2/authorize' ACCESS_TOKEN_URL = 'https://www.arcgis.com/sharing/rest/oauth2/token' ACCESS_TOKEN_METHOD = 'POST' EXTRA_DATA = [ ('expires_in', 'expires_in') ] def get_user_details(self, response): """Return user details from ArcGIS account""" return {'username': response.get('username'), 'email': response.get('email'), 'fullname': response.get('fullName'), 'first_name': response.get('firstName'), 'last_name': response.get('lastName')} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" return self.get_json( 'https://www.arcgis.com/sharing/rest/community/self', params={ 'token': access_token, 'f': 'json' } ) social-auth-core-3.1.0/social_core/backends/base.py0000644000175000017500000002347613342002520022053 0ustar omabomab00000000000000import time from requests import request, ConnectionError from ..utils import SSLHttpAdapter, module_member, parse_qs, user_agent from ..exceptions import AuthFailed class BaseAuth(object): """A authentication backend that authenticates the user based on the provider response""" name = '' # provider name, it's stored in database supports_inactive_user = False # Django auth ID_KEY = None EXTRA_DATA = None GET_ALL_EXTRA_DATA = False REQUIRES_EMAIL_VALIDATION = False SEND_USER_AGENT = False SSL_PROTOCOL = None def __init__(self, strategy, redirect_uri=None): self.strategy = strategy self.redirect_uri = redirect_uri self.data = self.strategy.request_data() self.redirect_uri = self.strategy.absolute_uri( self.redirect_uri ) def setting(self, name, default=None): """Return setting value from strategy""" return self.strategy.setting(name, default=default, backend=self) def start(self): if self.uses_redirect(): return self.strategy.redirect(self.auth_url()) else: return self.strategy.html(self.auth_html()) def complete(self, *args, **kwargs): return self.auth_complete(*args, **kwargs) def auth_url(self): """Must return redirect URL to auth provider""" raise NotImplementedError('Implement in subclass') def auth_html(self): """Must return login HTML content returned by provider""" raise NotImplementedError('Implement in subclass') def auth_complete(self, *args, **kwargs): """Completes login process, must return user instance""" raise NotImplementedError('Implement in subclass') def process_error(self, data): """Process data for errors, raise exception if needed. Call this method on any override of auth_complete.""" pass def authenticate(self, *args, **kwargs): """Authenticate user using social credentials Authentication is made if this is the correct backend, backend verification is made by kwargs inspection for current backend name presence. """ # Validate backend and arguments. Require that the Social Auth # response be passed in as a keyword argument, to make sure we # don't match the username/password calling conventions of # authenticate. if 'backend' not in kwargs or kwargs['backend'].name != self.name or \ 'strategy' not in kwargs or 'response' not in kwargs: return None self.strategy = kwargs.get('strategy') or self.strategy self.redirect_uri = kwargs.get('redirect_uri') or self.redirect_uri self.data = self.strategy.request_data() kwargs.setdefault('is_new', False) pipeline = self.strategy.get_pipeline(self) args, kwargs = self.strategy.clean_authenticate_args(*args, **kwargs) return self.pipeline(pipeline, *args, **kwargs) def pipeline(self, pipeline, pipeline_index=0, *args, **kwargs): out = self.run_pipeline(pipeline, pipeline_index, *args, **kwargs) if not isinstance(out, dict): return out user = out.get('user') if user: user.social_user = out.get('social') user.is_new = out.get('is_new') return user def disconnect(self, *args, **kwargs): pipeline = self.strategy.get_disconnect_pipeline(self) kwargs['name'] = self.name kwargs['user_storage'] = self.strategy.storage.user return self.run_pipeline(pipeline, *args, **kwargs) def run_pipeline(self, pipeline, pipeline_index=0, *args, **kwargs): out = kwargs.copy() out.setdefault('strategy', self.strategy) out.setdefault('backend', out.pop(self.name, None) or self) out.setdefault('request', self.strategy.request_data()) out.setdefault('details', {}) if not isinstance(pipeline_index, int) or \ pipeline_index < 0 or \ pipeline_index >= len(pipeline): pipeline_index = 0 for idx, name in enumerate(pipeline[pipeline_index:]): out['pipeline_index'] = pipeline_index + idx func = module_member(name) result = func(*args, **out) or {} if not isinstance(result, dict): return result out.update(result) return out def extra_data(self, user, uid, response, details=None, *args, **kwargs): """Return default extra data to store in extra_data field""" data = { # store the last time authentication toke place 'auth_time': int(time.time()) } extra_data_entries = [] if self.GET_ALL_EXTRA_DATA or self.setting('GET_ALL_EXTRA_DATA', False): extra_data_entries = response.keys() else: extra_data_entries = (self.EXTRA_DATA or []) + self.setting('EXTRA_DATA', []) for entry in extra_data_entries: if not isinstance(entry, (list, tuple)): entry = (entry,) size = len(entry) if size >= 1 and size <= 3: if size == 3: name, alias, discard = entry elif size == 2: (name, alias), discard = entry, False elif size == 1: name = alias = entry[0] discard = False value = response.get(name) or details.get(name) or details.get(alias) if discard and not value: continue data[alias] = value return data def auth_allowed(self, response, details): """Return True if the user should be allowed to authenticate, by default check if email is whitelisted (if there's a whitelist)""" emails = self.setting('WHITELISTED_EMAILS', []) domains = self.setting('WHITELISTED_DOMAINS', []) email = details.get('email') allowed = True if email and (emails or domains): domain = email.split('@', 1)[1] allowed = email in emails or domain in domains return allowed def get_user_id(self, details, response): """Return a unique ID for the current user, by default from server response.""" return response.get(self.ID_KEY) def get_user_details(self, response): """Must return user details in a know internal struct: {'username': , 'email': , 'fullname': , 'first_name': , 'last_name': } """ raise NotImplementedError('Implement in subclass') def get_user_names(self, fullname='', first_name='', last_name=''): # Avoid None values fullname = fullname or '' first_name = first_name or '' last_name = last_name or '' if fullname and not (first_name or last_name): try: first_name, last_name = fullname.split(' ', 1) except ValueError: first_name = first_name or fullname or '' last_name = last_name or '' fullname = fullname or ' '.join((first_name, last_name)) return fullname.strip(), first_name.strip(), last_name.strip() def get_user(self, user_id): """ Return user with given ID from the User model used by this backend. This is called by django.contrib.auth.middleware. """ return self.strategy.get_user(user_id) def continue_pipeline(self, partial): """Continue previous halted pipeline""" return self.strategy.authenticate(self, pipeline_index=partial.next_step, *partial.args, **partial.kwargs) def auth_extra_arguments(self): """Return extra arguments needed on auth process. The defaults can be overridden by GET parameters.""" extra_arguments = self.setting('AUTH_EXTRA_ARGUMENTS', {}).copy() extra_arguments.update((key, self.data[key]) for key in extra_arguments if key in self.data) return extra_arguments def uses_redirect(self): """Return True if this provider uses redirect url method, otherwise return false.""" return True def request(self, url, method='GET', *args, **kwargs): kwargs.setdefault('headers', {}) if self.setting('VERIFY_SSL') is not None: kwargs.setdefault('verify', self.setting('VERIFY_SSL')) kwargs.setdefault('timeout', self.setting('REQUESTS_TIMEOUT') or self.setting('URLOPEN_TIMEOUT')) if self.SEND_USER_AGENT and 'User-Agent' not in kwargs['headers']: kwargs['headers']['User-Agent'] = self.setting('USER_AGENT') or \ user_agent() try: if self.SSL_PROTOCOL: session = SSLHttpAdapter.ssl_adapter_session(self.SSL_PROTOCOL) response = session.request(method, url, *args, **kwargs) else: response = request(method, url, *args, **kwargs) except ConnectionError as err: raise AuthFailed(self, str(err)) response.raise_for_status() return response def get_json(self, url, *args, **kwargs): return self.request(url, *args, **kwargs).json() def get_querystring(self, url, *args, **kwargs): return parse_qs(self.request(url, *args, **kwargs).text) def get_key_and_secret(self): """Return tuple with Consumer Key and Consumer Secret for current service provider. Must return (key, secret), order *must* be respected. """ return self.setting('KEY'), self.setting('SECRET') social-auth-core-3.1.0/social_core/backends/lyft.py0000644000175000017500000000345713336542652022136 0ustar omabomab00000000000000""" Lyft OAuth2 backend. Read more about the API at https://developer.lyft.com/docs """ from .oauth import BaseOAuth2 class LyftOAuth2(BaseOAuth2): name = 'lyft' ID_KEY = 'id' SCOPE_SEPARATOR = ' ' AUTHORIZATION_URL = 'https://api.lyft.com/oauth/authorize' ACCESS_TOKEN_URL = 'https://api.lyft.com/oauth/token' ACCESS_TOKEN_METHOD = 'POST' REFRESH_TOKEN_URL = 'https://api.lyft.com/oauth/token' USER_DATA_URL = 'https://api.lyft.com/v1/profile' DEFAULT_SCOPE = ['public', 'profile', 'rides.read', 'rides.request'] RESPONSE_TYPE = 'code' STATE_PARAMETER = 'asdf' EXTRA_DATA = [ ('id', 'id'), ('username', 'username'), ('access_token', 'access_token'), ('refresh_token', 'refresh_token'), ('token_type', 'token_type'), ('expires_in', 'expires_in'), ('scope', 'scope'), ] def get_user_details(self, response): """Return user details from Lyft account""" return { 'id': response['id'], 'username': response['id'] } def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" response = kwargs.pop('response') return self.get_json(self.USER_DATA_URL, headers={ 'Authorization': 'Bearer {0}'.format(access_token) }) def auth_complete_params(self, state=None): client_id, client_secret = self.get_key_and_secret() return { 'grant_type': 'authorization_code', 'code': self.data['code'] } def auth_complete_credentials(self): return self.get_key_and_secret() def refresh_token_params(self, refresh_token, *args, **kwargs): return { 'refresh_token': refresh_token, 'grant_type': 'refresh_token' } social-auth-core-3.1.0/social_core/backends/dropbox.py0000644000175000017500000000777313336542652022642 0ustar omabomab00000000000000""" Dropbox OAuth1 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/dropbox.html """ import warnings from .oauth import BaseOAuth1, BaseOAuth2 class DropboxOAuth(BaseOAuth1): """Dropbox OAuth authentication backend""" name = 'dropbox' ID_KEY = 'uid' AUTHORIZATION_URL = 'https://www.dropbox.com/1/oauth/authorize' REQUEST_TOKEN_URL = 'https://api.dropbox.com/1/oauth/request_token' REQUEST_TOKEN_METHOD = 'POST' ACCESS_TOKEN_URL = 'https://api.dropbox.com/1/oauth/access_token' ACCESS_TOKEN_METHOD = 'POST' REDIRECT_URI_PARAMETER_NAME = 'oauth_callback' EXTRA_DATA = [ ('id', 'id'), ('expires', 'expires') ] def __init__(self, *args, **kwargs): warnings.warn( 'Dropbox V1 api is deprecated and will be shute down 2017-06-28 ' 'https://blogs.dropbox.com/developers/2016/06/api-v1-deprecated/', DeprecationWarning, stacklevel=2 ) super(DropboxOAuth, self).__init__(*args, **kwargs) def get_user_details(self, response): """Return user details from Dropbox account""" fullname, first_name, last_name = self.get_user_names( response.get('display_name') ) return {'username': str(response.get('uid')), 'email': response.get('email'), 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" return self.get_json('https://api.dropbox.com/1/account/info', auth=self.oauth_auth(access_token)) class DropboxOAuth2(BaseOAuth2): name = 'dropbox-oauth2' ID_KEY = 'uid' AUTHORIZATION_URL = 'https://www.dropbox.com/1/oauth2/authorize' ACCESS_TOKEN_URL = 'https://api.dropbox.com/1/oauth2/token' ACCESS_TOKEN_METHOD = 'POST' REDIRECT_STATE = False EXTRA_DATA = [ ('uid', 'username'), ] def __init__(self, *args, **kwargs): warnings.warn( 'Dropbox V1 api is deprecated and will be shute down 2017-06-28 ' 'https://blogs.dropbox.com/developers/2016/06/api-v1-deprecated/', DeprecationWarning, stacklevel=2 ) super(DropboxOAuth2, self).__init__(*args, **kwargs) def get_user_details(self, response): """Return user details from Dropbox account""" fullname, first_name, last_name = self.get_user_names( response.get('display_name') ) return {'username': str(response.get('uid')), 'email': response.get('email'), 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" return self.get_json( 'https://api.dropbox.com/1/account/info', headers={'Authorization': 'Bearer {0}'.format(access_token)} ) class DropboxOAuth2V2(BaseOAuth2): name = 'dropbox-oauth2' ID_KEY = 'uid' AUTHORIZATION_URL = 'https://www.dropbox.com/oauth2/authorize' ACCESS_TOKEN_URL = 'https://api.dropboxapi.com/oauth2/token' ACCESS_TOKEN_METHOD = 'POST' REDIRECT_STATE = False def get_user_details(self, response): """Return user details from Dropbox account""" name = response.get('name') return {'username': str(response.get('account_id')), 'email': response.get('email'), 'fullname': name.get('display_name'), 'first_name': name.get('given_name'), 'last_name': name.get('surname')} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" return self.get_json( 'https://api.dropboxapi.com/2/users/get_current_account', headers={'Authorization': 'Bearer {0}'.format(access_token)}, method='POST' ) social-auth-core-3.1.0/social_core/backends/tumblr.py0000644000175000017500000000212213336542652022451 0ustar omabomab00000000000000""" Tumblr OAuth1 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/tumblr.html """ from ..utils import first from .oauth import BaseOAuth1 class TumblrOAuth(BaseOAuth1): name = 'tumblr' ID_KEY = 'name' AUTHORIZATION_URL = 'http://www.tumblr.com/oauth/authorize' REQUEST_TOKEN_URL = 'http://www.tumblr.com/oauth/request_token' REQUEST_TOKEN_METHOD = 'POST' ACCESS_TOKEN_URL = 'http://www.tumblr.com/oauth/access_token' def get_user_id(self, details, response): return response['response']['user'][self.ID_KEY] def get_user_details(self, response): # http://www.tumblr.com/docs/en/api/v2#user-methods user_info = response['response']['user'] data = {'username': user_info['name']} blog = first(lambda blog: blog['primary'], user_info['blogs']) if blog: data['fullname'] = blog['title'] return data def user_data(self, access_token): return self.get_json('http://api.tumblr.com/v2/user/info', auth=self.oauth_auth(access_token)) social-auth-core-3.1.0/social_core/backends/orcid.py0000644000175000017500000000636213336542652022256 0ustar omabomab00000000000000""" ORCID OAuth2 Application backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/orcid.html """ from .oauth import BaseOAuth2 class ORCIDOAuth2(BaseOAuth2): """ORCID OAuth2 authentication backend""" name = 'orcid' ID_KEY = 'orcid' AUTHORIZATION_URL = 'https://orcid.org/oauth/authorize' ACCESS_TOKEN_URL = 'https://orcid.org/oauth/token' USER_DATA_URL = 'https://pub.orcid.org/v2.0/{}' DEFAULT_SCOPE = ['/authenticate'] ACCESS_TOKEN_METHOD = 'POST' EXTRA_DATA = [ ('orcid', 'id'), ('expires_in', 'expires'), ('refresh_token', 'refresh_token') ] def auth_params(self, state=None): params = super(ORCIDOAuth2, self).auth_params(state) return params def get_user_details(self, response): """Return user details from ORCID account""" fullname= response.get('name', '') first_name = last_name = email = '' person = response.get('person') if person: name = person.get('name') if name: first_name = name.get('given-names', {}).get('value', '') last_name = name.get('family-name', {}).get('value', '') emails = person.get('emails') if emails: emails_list = emails.get('email') if emails_list and len(emails_list) > 0: email = emails_list[0].get('email', '') if len(emails_list) > 1: for email_dict in emails_list: if email_dict.get('primary','') == True: email = email_dict.get('email', '') break else: mail = emails_list[0].get('email', '') return {'username': response.get('orcid'), 'email': email, 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" params = self.setting('PROFILE_EXTRA_PARAMS', {}) params['access_token'] = access_token try: return self.get_json(self.USER_DATA_URL.format( kwargs['response']['orcid']), headers={'Content-Type': 'application/json'}, params=params) except ValueError as e: return None class ORCIDOAuth2Sandbox(ORCIDOAuth2): """ORCID OAuth2 Sandbox authentication backend""" name = 'orcid-sandbox' AUTHORIZATION_URL = 'https://sandbox.orcid.org/oauth/authorize' ACCESS_TOKEN_URL = 'https://sandbox.orcid.org/oauth/token' USER_DATA_URL = 'https://pub.sandbox.orcid.org/v2.0/{}' class ORCIDMemberOAuth2(ORCIDOAuth2): """ORCID OAuth2 authentication backend that uses ORCID Member API""" USER_DATA_URL = 'https://api.orcid.org/v2.0/{}' DEFAULT_SCOPE = ['/authenticate', '/read-limited'] class ORCIDMemberOAuth2Sandbox(ORCIDOAuth2Sandbox): """ORCID OAuth2 Sandbox authentication backend that uses ORCID Member Sandbox API""" USER_DATA_URL = 'https://api.sandbox.orcid.org/v2.0/{}' DEFAULT_SCOPE = ['/authenticate', '/read-limited'] social-auth-core-3.1.0/social_core/backends/fedora.py0000644000175000017500000000042213336542652022405 0ustar omabomab00000000000000""" Fedora OpenId backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/fedora.html """ from .open_id import OpenIdAuth class FedoraOpenId(OpenIdAuth): name = 'fedora' URL = 'https://id.fedoraproject.org' USERNAME_KEY = 'nickname' social-auth-core-3.1.0/social_core/backends/uber.py0000644000175000017500000000246713336542652022115 0ustar omabomab00000000000000""" Uber OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/uber.html """ from .oauth import BaseOAuth2 class UberOAuth2(BaseOAuth2): name = 'uber' ID_KEY = 'uuid' SCOPE_SEPARATOR = ' ' AUTHORIZATION_URL = 'https://login.uber.com/oauth/authorize' ACCESS_TOKEN_URL = 'https://login.uber.com/oauth/token' ACCESS_TOKEN_METHOD = 'POST' def auth_complete_credentials(self): return self.get_key_and_secret() def get_user_details(self, response): """Return user details from Uber account""" email = response.get('email', '') fullname, first_name, last_name = self.get_user_names( '', response.get('first_name', ''), response.get('last_name', '') ) return {'username': email, 'email': email, 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" response = kwargs.pop('response') return self.get_json('https://api.uber.com/v1/me', headers={ 'Authorization': '{0} {1}'.format(response.get('token_type'), access_token) }) social-auth-core-3.1.0/social_core/backends/wunderlist.py0000644000175000017500000000207213336542652023350 0ustar omabomab00000000000000from .oauth import BaseOAuth2 class WunderlistOAuth2(BaseOAuth2): """Wunderlist OAuth2 authentication backend""" name = 'wunderlist' AUTHORIZATION_URL = 'https://www.wunderlist.com/oauth/authorize' ACCESS_TOKEN_URL = 'https://www.wunderlist.com/oauth/access_token' ACCESS_TOKEN_METHOD = 'POST' REDIRECT_STATE = False def get_user_details(self, response): """Return user details from Wunderlist account""" fullname, first_name, last_name = self.get_user_names( response.get('name') ) return {'username': str(response.get('id')), 'email': response.get('email'), 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" headers = { 'X-Access-Token': access_token, 'X-Client-ID': self.setting('KEY')} return self.get_json( 'https://a.wunderlist.com/api/v1/user', headers=headers) social-auth-core-3.1.0/social_core/backends/asana.py0000644000175000017500000000275113336542652022237 0ustar omabomab00000000000000import datetime from .oauth import BaseOAuth2 class AsanaOAuth2(BaseOAuth2): name = 'asana' AUTHORIZATION_URL = 'https://app.asana.com/-/oauth_authorize' ACCESS_TOKEN_METHOD = 'POST' ACCESS_TOKEN_URL = 'https://app.asana.com/-/oauth_token' REFRESH_TOKEN_URL = 'https://app.asana.com/-/oauth_token' REDIRECT_STATE = False USER_DATA_URL = 'https://app.asana.com/api/1.0/users/me' EXTRA_DATA = [ ('expires_in', 'expires'), ('refresh_token', 'refresh_token'), ('name', 'name'), ] def get_user_details(self, response): data = response['data'] fullname, first_name, last_name = self.get_user_names(data['name']) return {'email': data['email'], 'username': data['email'], 'fullname': fullname, 'last_name': last_name, 'first_name': first_name} def user_data(self, access_token, *args, **kwargs): return self.get_json(self.USER_DATA_URL, headers={ 'Authorization': 'Bearer {}'.format(access_token) }) def extra_data(self, user, uid, response, details=None, *args, **kwargs): data = super(AsanaOAuth2, self).extra_data( user, uid, response, details ) if self.setting('ESTIMATE_EXPIRES_ON'): expires_on = datetime.datetime.utcnow() + \ datetime.timedelta(seconds=data['expires']) data['expires_on'] = expires_on.isoformat() return data social-auth-core-3.1.0/social_core/backends/clef.py0000644000175000017500000000333113336542652022060 0ustar omabomab00000000000000""" Clef OAuth support. This contribution adds support for Clef OAuth service. The settings SOCIAL_AUTH_CLEF_KEY and SOCIAL_AUTH_CLEF_SECRET must be defined with the values given by Clef application registration process. """ from .oauth import BaseOAuth2 class ClefOAuth2(BaseOAuth2): """Clef OAuth authentication backend""" name = 'clef' AUTHORIZATION_URL = 'https://clef.io/iframes/qr' ACCESS_TOKEN_URL = 'https://clef.io/api/v1/authorize' ACCESS_TOKEN_METHOD = 'POST' SCOPE_SEPARATOR = ',' def auth_params(self, *args, **kwargs): params = super(ClefOAuth2, self).auth_params(*args, **kwargs) params['app_id'] = params.pop('client_id') params['redirect_url'] = params.pop('redirect_uri') return params def get_user_id(self, response, details): return details.get('info').get('id') def get_user_details(self, response): """Return user details from Github account""" info = response.get('info') fullname, first_name, last_name = self.get_user_names( first_name=info.get('first_name'), last_name=info.get('last_name') ) email = info.get('email', '') if email: username = email.split('@', 1)[0] else: username = info.get('id') return { 'username': username, 'email': email, 'fullname': fullname, 'first_name': first_name, 'last_name': last_name, 'phone_number': info.get('phone_number', '') } def user_data(self, access_token, *args, **kwargs): return self.get_json('https://clef.io/api/v1/info', params={'access_token': access_token}) social-auth-core-3.1.0/social_core/backends/google_openidconnect.py0000644000175000017500000000142113336542652025331 0ustar omabomab00000000000000""" Google OpenIdConnect: https://python-social-auth.readthedocs.io/en/latest/backends/google.html """ from .open_id_connect import OpenIdConnectAuth from .google import GoogleOAuth2 class GoogleOpenIdConnect(GoogleOAuth2, OpenIdConnectAuth): name = 'google-openidconnect' OIDC_ENDPOINT = 'https://accounts.google.com' # differs from value in discovery document # http://openid.net/specs/openid-connect-core-1_0.html#rfc.section.15.6.2 ID_TOKEN_ISSUER = 'accounts.google.com' def user_data(self, access_token, *args, **kwargs): """Return user data from Google API""" return self.get_json( 'https://www.googleapis.com/plus/v1/people/me/openIdConnect', params={'access_token': access_token, 'alt': 'json'} ) social-auth-core-3.1.0/social_core/backends/salesforce.py0000644000175000017500000000346013336542652023300 0ustar omabomab00000000000000from six.moves.urllib_parse import urlencode from .oauth import BaseOAuth2 class SalesforceOAuth2(BaseOAuth2): """Salesforce OAuth2 authentication backend""" name = 'salesforce-oauth2' AUTHORIZATION_URL = \ 'https://login.salesforce.com/services/oauth2/authorize' ACCESS_TOKEN_URL = 'https://login.salesforce.com/services/oauth2/token' REVOKE_TOKEN_URL = 'https://login.salesforce.com/services/oauth2/revoke' ACCESS_TOKEN_METHOD = 'POST' REFRESH_TOKEN_METHOD = 'POST' SCOPE_SEPARATOR = ' ' EXTRA_DATA = [ ('id', 'id'), ('instance_url', 'instance_url'), ('issued_at', 'issued_at'), ('signature', 'signature'), ('refresh_token', 'refresh_token'), ] def get_user_details(self, response): """Return user details from a Salesforce account""" return { 'username': response.get('username'), 'email': response.get('email') or '', 'first_name': response.get('first_name'), 'last_name': response.get('last_name'), 'fullname': response.get('display_name') } def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" user_id_url = kwargs.get('response').get('id') url = user_id_url + '?' + urlencode({'access_token': access_token}) try: return self.get_json(url) except ValueError: return None class SalesforceOAuth2Sandbox(SalesforceOAuth2): """Salesforce OAuth2 authentication testing backend""" name = 'salesforce-oauth2-sandbox' AUTHORIZATION_URL = 'https://test.salesforce.com/services/oauth2/authorize' ACCESS_TOKEN_URL = 'https://test.salesforce.com/services/oauth2/token' REVOKE_TOKEN_URL = 'https://test.salesforce.com/services/oauth2/revoke' social-auth-core-3.1.0/social_core/backends/pinterest.py0000644000175000017500000000273413336542652023172 0ustar omabomab00000000000000# -*- coding: utf-8 -*- """ Pinterest OAuth2 backend, docs at: https://developers.pinterest.com/docs/api/authentication/ """ from __future__ import unicode_literals import ssl from .oauth import BaseOAuth2 class PinterestOAuth2(BaseOAuth2): name = 'pinterest' ID_KEY = 'user_id' AUTHORIZATION_URL = 'https://api.pinterest.com/oauth/' ACCESS_TOKEN_URL = 'https://api.pinterest.com/v1/oauth/token' REDIRECT_STATE = False ACCESS_TOKEN_METHOD = 'POST' SSL_PROTOCOL = ssl.PROTOCOL_TLSv1 def user_data(self, access_token, *args, **kwargs): response = self.get_json('https://api.pinterest.com/v1/me/', params={'access_token': access_token}) if 'data' in response: username = response['data']['url'].strip('/').split('/')[-1] response = { 'user_id': response['data']['id'], 'first_name': response['data']['first_name'], 'last_name': response['data']['last_name'], 'username': username, } return response def get_user_details(self, response): fullname, first_name, last_name = self.get_user_names( first_name=response['first_name'], last_name=response['last_name']) return {'username': response.get('username'), 'email': None, 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} social-auth-core-3.1.0/social_core/backends/skyrock.py0000644000175000017500000000225613336542652022641 0ustar omabomab00000000000000""" Skyrock OAuth1 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/skyrock.html """ from .oauth import BaseOAuth1 class SkyrockOAuth(BaseOAuth1): """Skyrock OAuth authentication backend""" name = 'skyrock' ID_KEY = 'id_user' AUTHORIZATION_URL = 'https://api.skyrock.com/v2/oauth/authenticate' REQUEST_TOKEN_URL = 'https://api.skyrock.com/v2/oauth/initiate' ACCESS_TOKEN_URL = 'https://api.skyrock.com/v2/oauth/token' EXTRA_DATA = [('id', 'id')] def get_user_details(self, response): """Return user details from Skyrock account""" fullname, first_name, last_name = self.get_user_names( first_name=response['firstname'], last_name=response['name'] ) return {'username': response['username'], 'email': response['email'], 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token): """Return user data provided""" return self.get_json('https://api.skyrock.com/v2/user/get.json', auth=self.oauth_auth(access_token)) social-auth-core-3.1.0/social_core/backends/aol.py0000644000175000017500000000034113336542652021720 0ustar omabomab00000000000000""" AOL OpenId backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/aol.html """ from .open_id import OpenIdAuth class AOLOpenId(OpenIdAuth): name = 'aol' URL = 'http://openid.aol.com' social-auth-core-3.1.0/social_core/backends/google.py0000644000175000017500000001543013425032722022415 0ustar omabomab00000000000000""" Google OpenId, OAuth2, OAuth1, Google+ Sign-in backends, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/google.html """ from ..utils import handle_http_errors from .open_id import OpenIdAuth from .oauth import BaseOAuth2, BaseOAuth1 from ..exceptions import AuthMissingParameter class BaseGoogleAuth(object): def get_user_id(self, details, response): """Use google email as unique id""" if self.setting('USE_UNIQUE_USER_ID', False): if 'sub' in response: return response['sub'] else: return response['id'] else: return details['email'] def get_user_details(self, response): """Return user details from Google API account""" if 'email' in response: email = response['email'] else: email = '' name, given_name, family_name = ( response.get('name', ''), response.get('given_name', ''), response.get('family_name', ''), ) fullname, first_name, last_name = self.get_user_names( name, given_name, family_name ) return {'username': email.split('@', 1)[0], 'email': email, 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} class BaseGoogleOAuth2API(BaseGoogleAuth): def user_data(self, access_token, *args, **kwargs): """Return user data from Google API""" return self.get_json( 'https://www.googleapis.com/oauth2/v3/userinfo', headers={ 'Authorization': 'Bearer %s' % access_token, }, ) def revoke_token_params(self, token, uid): return {'token': token} def revoke_token_headers(self, token, uid): return {'Content-type': 'application/json'} class GoogleOAuth2(BaseGoogleOAuth2API, BaseOAuth2): """Google OAuth2 authentication backend""" name = 'google-oauth2' REDIRECT_STATE = False AUTHORIZATION_URL = 'https://accounts.google.com/o/oauth2/auth' ACCESS_TOKEN_URL = 'https://accounts.google.com/o/oauth2/token' ACCESS_TOKEN_METHOD = 'POST' REVOKE_TOKEN_URL = 'https://accounts.google.com/o/oauth2/revoke' REVOKE_TOKEN_METHOD = 'GET' # The order of the default scope is important DEFAULT_SCOPE = ['openid', 'email', 'profile'] EXTRA_DATA = [ ('refresh_token', 'refresh_token', True), ('expires_in', 'expires'), ('token_type', 'token_type', True) ] class GooglePlusAuth(BaseGoogleOAuth2API, BaseOAuth2): name = 'google-plus' REDIRECT_STATE = False STATE_PARAMETER = False AUTHORIZATION_URL = 'https://accounts.google.com/o/oauth2/auth' ACCESS_TOKEN_URL = 'https://accounts.google.com/o/oauth2/token' ACCESS_TOKEN_METHOD = 'POST' REVOKE_TOKEN_URL = 'https://accounts.google.com/o/oauth2/revoke' REVOKE_TOKEN_METHOD = 'GET' DEFAULT_SCOPE = [ 'https://www.googleapis.com/auth/plus.login', 'https://www.googleapis.com/auth/plus.me', ] EXTRA_DATA = [ ('id', 'user_id'), ('refresh_token', 'refresh_token', True), ('expires_in', 'expires'), ('access_type', 'access_type', True), ('code', 'code') ] def auth_complete_params(self, state=None): params = super(GooglePlusAuth, self).auth_complete_params(state) if self.data.get('access_token'): # Don't add postmessage if this is plain server-side workflow params['redirect_uri'] = 'postmessage' return params @handle_http_errors def auth_complete(self, *args, **kwargs): if 'access_token' in self.data: # Client-side workflow token = self.data.get('access_token') response = self.get_json( 'https://www.googleapis.com/oauth2/v3/tokeninfo', params={'access_token': token} ) self.process_error(response) return self.do_auth(token, response=response, *args, **kwargs) elif 'code' in self.data: # Server-side workflow response = self.request_access_token( self.ACCESS_TOKEN_URL, data=self.auth_complete_params(), headers=self.auth_headers(), method=self.ACCESS_TOKEN_METHOD ) self.process_error(response) return self.do_auth(response['access_token'], response=response, *args, **kwargs) elif 'id_token' in self.data: # Client-side workflow token = self.data.get('id_token') return self.do_auth(token, *args, **kwargs) else: raise AuthMissingParameter(self, 'access_token, id_token, or code') def user_data(self, access_token, *args, **kwargs): if 'id_token' not in self.data: return super(GooglePlusAuth, self).user_data(access_token, *args, **kwargs) response = self.get_json( 'https://www.googleapis.com/oauth2/v3/tokeninfo', params={'id_token': access_token} ) self.process_error(response) return response class GoogleOAuth(BaseGoogleAuth, BaseOAuth1): """Google OAuth authorization mechanism""" name = 'google-oauth' AUTHORIZATION_URL = 'https://www.google.com/accounts/OAuthAuthorizeToken' REQUEST_TOKEN_URL = 'https://www.google.com/accounts/OAuthGetRequestToken' ACCESS_TOKEN_URL = 'https://www.google.com/accounts/OAuthGetAccessToken' DEFAULT_SCOPE = ['https://www.googleapis.com/auth/userinfo#email'] def user_data(self, access_token, *args, **kwargs): """Return user data from Google API""" return self.get_querystring( 'https://www.googleapis.com/userinfo/email', auth=self.oauth_auth(access_token) ) def get_key_and_secret(self): """Return Google OAuth Consumer Key and Consumer Secret pair, uses anonymous by default, beware that this marks the application as not registered and a security badge is displayed on authorization page. http://code.google.com/apis/accounts/docs/OAuth_ref.html#SigningOAuth """ key_secret = super(GoogleOAuth, self).get_key_and_secret() if key_secret == (None, None): key_secret = ('anonymous', 'anonymous') return key_secret class GoogleOpenId(OpenIdAuth): name = 'google' URL = 'https://www.google.com/accounts/o8/id' def get_user_id(self, details, response): """ Return user unique id provided by service. For google user email is unique enought to flag a single user. Email comes from schema: http://axschema.org/contact/email """ return details['email'] social-auth-core-3.1.0/social_core/backends/pocket.py0000644000175000017500000000324013336542652022433 0ustar omabomab00000000000000""" Pocket OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/pocket.html """ from .base import BaseAuth from ..utils import handle_http_errors class PocketAuth(BaseAuth): name = 'pocket' AUTHORIZATION_URL = 'https://getpocket.com/auth/authorize' ACCESS_TOKEN_URL = 'https://getpocket.com/v3/oauth/authorize' REQUEST_TOKEN_URL = 'https://getpocket.com/v3/oauth/request' ID_KEY = 'username' def get_json(self, url, *args, **kwargs): headers = {'X-Accept': 'application/json'} kwargs.update({'method': 'POST', 'headers': headers}) return super(PocketAuth, self).get_json(url, *args, **kwargs) def get_user_details(self, response): return {'username': response['username']} def extra_data(self, user, uid, response, details=None, *args, **kwargs): return response def auth_url(self): data = { 'consumer_key': self.setting('KEY'), 'redirect_uri': self.redirect_uri, } token = self.get_json(self.REQUEST_TOKEN_URL, data=data)['code'] self.strategy.session_set('pocket_request_token', token) bits = (self.AUTHORIZATION_URL, token, self.redirect_uri) return '%s?request_token=%s&redirect_uri=%s' % bits @handle_http_errors def auth_complete(self, *args, **kwargs): data = { 'consumer_key': self.setting('KEY'), 'code': self.strategy.session_get('pocket_request_token'), } response = self.get_json(self.ACCESS_TOKEN_URL, data=data) kwargs.update({'response': response, 'backend': self}) return self.strategy.authenticate(*args, **kwargs) social-auth-core-3.1.0/social_core/backends/email.py0000644000175000017500000000043113336542652022234 0ustar omabomab00000000000000""" Legacy Email backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/email.html """ from .legacy import LegacyAuth class EmailAuth(LegacyAuth): name = 'email' ID_KEY = 'email' REQUIRES_EMAIL_VALIDATION = True EXTRA_DATA = ['email'] social-auth-core-3.1.0/social_core/backends/auth0.py0000644000175000017500000000332513432551443022166 0ustar omabomab00000000000000""" Auth0 implementation based on: https://auth0.com/docs/quickstart/webapp/django/01-login """ from jose import jwt from .oauth import BaseOAuth2 class Auth0OAuth2(BaseOAuth2): """Auth0 OAuth authentication backend""" name = 'auth0' SCOPE_SEPARATOR = ' ' ACCESS_TOKEN_METHOD = 'POST' EXTRA_DATA = [ ('picture', 'picture') ] def api_path(self, path=''): """Build API path for Auth0 domain""" return 'https://{domain}/{path}'.format(domain=self.setting('DOMAIN'), path=path) def authorization_url(self): return self.api_path('authorize') def access_token_url(self): return self.api_path('oauth/token') def get_user_id(self, details, response): """Return current user id.""" return details['user_id'] def get_user_details(self, response): # Obtain JWT and the keys to validate the signature id_token = response.get('id_token') jwks = self.get_json(self.api_path('.well-known/jwks.json')) issuer = self.api_path() audience = self.setting('KEY') # CLIENT_ID payload = jwt.decode(id_token, jwks, algorithms=['RS256'], audience=audience, issuer=issuer) fullname, first_name, last_name = self.get_user_names(payload['name']) return {'username': payload['nickname'], 'email': payload['email'], 'fullname': fullname, 'first_name': first_name, 'last_name': last_name, 'picture': payload['picture'], 'user_id': payload['sub']} social-auth-core-3.1.0/social_core/backends/tripit.py0000644000175000017500000000336313336542652022467 0ustar omabomab00000000000000""" Tripit OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/tripit.html """ from xml.dom import minidom from .oauth import BaseOAuth1 class TripItOAuth(BaseOAuth1): """TripIt OAuth authentication backend""" name = 'tripit' AUTHORIZATION_URL = 'https://www.tripit.com/oauth/authorize' REQUEST_TOKEN_URL = 'https://api.tripit.com/oauth/request_token' ACCESS_TOKEN_URL = 'https://api.tripit.com/oauth/access_token' EXTRA_DATA = [('screen_name', 'screen_name')] def get_user_details(self, response): """Return user details from TripIt account""" fullname, first_name, last_name = self.get_user_names(response['name']) return {'username': response['screen_name'], 'email': response['email'], 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token, *args, **kwargs): """Return user data provided""" dom = minidom.parseString(self.oauth_request( access_token, 'https://api.tripit.com/v1/get/profile' ).content) return { 'id': dom.getElementsByTagName('Profile')[0].getAttribute('ref'), 'name': dom.getElementsByTagName('public_display_name')[0] .childNodes[0].data, 'screen_name': dom.getElementsByTagName('screen_name')[0] .childNodes[0].data, 'email': dom.getElementsByTagName('is_primary')[0] .parentNode .getElementsByTagName('address')[0] .childNodes[0].data } social-auth-core-3.1.0/social_core/backends/globus.py0000644000175000017500000000173513336542652022450 0ustar omabomab00000000000000""" Globus Auth OpenID Connect backend, docs at: https://docs.globus.org/api/auth http://globus-integration-examples.readthedocs.io """ from social_core.backends.open_id_connect import OpenIdConnectAuth class GlobusOpenIdConnect(OpenIdConnectAuth): name = 'globus' OIDC_ENDPOINT = 'https://auth.globus.org' EXTRA_DATA = [ ('expires_in', 'expires_in', True), ('refresh_token', 'refresh_token', True), ('id_token', 'id_token', True), ('other_tokens', 'other_tokens', True), ] def get_user_details(self, response): username_key = self.setting('USERNAME_KEY', default=self.USERNAME_KEY) name = response.get('name') or '' fullname, first_name, last_name = self.get_user_names(name) return {'username': response.get(username_key), 'email': response.get('email'), 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} social-auth-core-3.1.0/social_core/backends/xing.py0000644000175000017500000000516413336542652022122 0ustar omabomab00000000000000""" XING OAuth1 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/xing.html """ import six from requests_oauthlib import OAuth1 from oauthlib.oauth1 import SIGNATURE_TYPE_AUTH_HEADER from .oauth import BaseOAuth1 from ..exceptions import AuthTokenError class XingOAuth(BaseOAuth1): """Xing OAuth authentication backend""" name = 'xing' AUTHORIZATION_URL = 'https://api.xing.com/v1/authorize' REQUEST_TOKEN_URL = 'https://api.xing.com/v1/request_token' ACCESS_TOKEN_URL = 'https://api.xing.com/v1/access_token' SCOPE_SEPARATOR = '+' EXTRA_DATA = [ ('id', 'id'), ('user_id', 'user_id') ] def get_user_details(self, response): """Return user details from Xing account""" email = response.get('email', '') fullname, first_name, last_name = self.get_user_names( first_name=response['first_name'], last_name=response['last_name'] ) return {'username': first_name + last_name, 'fullname': fullname, 'first_name': first_name, 'last_name': last_name, 'email': email} def clean_oauth_auth(self, access_token): """Override of oauth_auth since Xing doesn't like callback_uri and oauth_verifier on authenticated API calls""" key, secret = self.get_key_and_secret() resource_owner_key = access_token.get('oauth_token') resource_owner_secret = access_token.get('oauth_token_secret') if not resource_owner_key: raise AuthTokenError(self, 'Missing oauth_token') if not resource_owner_secret: raise AuthTokenError(self, 'Missing oauth_token_secret') # decoding='utf-8' produces errors with python-requests on Python3 # since the final URL will be of type bytes decoding = None if six.PY3 else 'utf-8' return OAuth1(key, secret, resource_owner_key=resource_owner_key, resource_owner_secret=resource_owner_secret, signature_type=SIGNATURE_TYPE_AUTH_HEADER, decoding=decoding) def user_data(self, access_token, *args, **kwargs): """Return user data provided""" profile = self.get_json( 'https://api.xing.com/v1/users/me.json', auth=self.clean_oauth_auth(access_token) )['users'][0] return { 'user_id': profile['id'], 'id': profile['id'], 'first_name': profile['first_name'], 'last_name': profile['last_name'], 'email': profile['active_email'] } social-auth-core-3.1.0/social_core/backends/runkeeper.py0000644000175000017500000000342213336542652023150 0ustar omabomab00000000000000""" RunKeeper OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/runkeeper.html """ from .oauth import BaseOAuth2 class RunKeeperOAuth2(BaseOAuth2): """RunKeeper OAuth authentication backend""" name = 'runkeeper' AUTHORIZATION_URL = 'https://runkeeper.com/apps/authorize' ACCESS_TOKEN_URL = 'https://runkeeper.com/apps/token' ACCESS_TOKEN_METHOD = 'POST' EXTRA_DATA = [ ('userID', 'id'), ] def get_user_id(self, details, response): return response['userID'] def get_user_details(self, response): """Parse username from profile link""" username = None profile_url = response.get('profile') if len(profile_url): profile_url_parts = profile_url.split('http://runkeeper.com/user/') if len(profile_url_parts) > 1 and len(profile_url_parts[1]): username = profile_url_parts[1] fullname, first_name, last_name = self.get_user_names( fullname=response.get('name') ) return {'username': username, 'email': response.get('email') or '', 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token, *args, **kwargs): # We need to use the /user endpoint to get the user id, the /profile # endpoint contains name, user name, location, gender user_data = self._user_data(access_token, '/user') profile_data = self._user_data(access_token, '/profile') return dict(user_data, **profile_data) def _user_data(self, access_token, path): url = 'https://api.runkeeper.com{0}'.format(path) return self.get_json(url, params={'access_token': access_token}) social-auth-core-3.1.0/social_core/backends/goclioeu.py0000644000175000017500000000072313336542652022757 0ustar omabomab00000000000000from .goclio import GoClioOAuth2 class GoClioEuOAuth2(GoClioOAuth2): name = 'goclioeu' AUTHORIZATION_URL = 'https://app.goclio.eu/oauth/authorize/' ACCESS_TOKEN_URL = 'https://app.goclio.eu/oauth/token/' def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" return self.get_json( 'https://app.goclio.eu/api/v2/users/who_am_i', params={'access_token': access_token} ) social-auth-core-3.1.0/social_core/backends/facebook.py0000644000175000017500000002143313342002520022701 0ustar omabomab00000000000000""" Facebook OAuth2 and Canvas Application backends, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/facebook.html """ import hmac import time import json import base64 import hashlib from ..utils import parse_qs, constant_time_compare, handle_http_errors from .oauth import BaseOAuth2 from ..exceptions import AuthException, AuthCanceled, AuthUnknownError, \ AuthMissingParameter API_VERSION = 2.9 class FacebookOAuth2(BaseOAuth2): """Facebook OAuth2 authentication backend""" name = 'facebook' REDIRECT_STATE = False RESPONSE_TYPE = None SCOPE_SEPARATOR = ',' AUTHORIZATION_URL = 'https://www.facebook.com/v{version}/dialog/oauth' ACCESS_TOKEN_URL = \ 'https://graph.facebook.com/v{version}/oauth/access_token' REVOKE_TOKEN_URL = \ 'https://graph.facebook.com/v{version}/{uid}/permissions' REVOKE_TOKEN_METHOD = 'DELETE' USER_DATA_URL = 'https://graph.facebook.com/v{version}/me' EXTRA_DATA = [ ('id', 'id'), ('expires', 'expires'), ('granted_scopes', 'granted_scopes'), ('denied_scopes', 'denied_scopes') ] def auth_params(self, state=None): params = super(FacebookOAuth2, self).auth_params(state) params['return_scopes'] = 'true' return params def authorization_url(self): version = self.setting('API_VERSION', API_VERSION) return self.AUTHORIZATION_URL.format(version=version) def access_token_url(self): version = self.setting('API_VERSION', API_VERSION) return self.ACCESS_TOKEN_URL.format(version=version) def get_user_details(self, response): """Return user details from Facebook account""" fullname, first_name, last_name = self.get_user_names( response.get('name', ''), response.get('first_name', ''), response.get('last_name', '') ) return {'username': response.get('username', response.get('name')), 'email': response.get('email', ''), 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" params = self.setting('PROFILE_EXTRA_PARAMS', {}) params['access_token'] = access_token if self.setting('APPSECRET_PROOF', True): _, secret = self.get_key_and_secret() params['appsecret_proof'] = hmac.new( secret.encode('utf8'), msg=access_token.encode('utf8'), digestmod=hashlib.sha256 ).hexdigest() version = self.setting('API_VERSION', API_VERSION) return self.get_json(self.USER_DATA_URL.format(version=version), params=params) def process_error(self, data): super(FacebookOAuth2, self).process_error(data) if data.get('error_code'): raise AuthCanceled(self, data.get('error_message') or data.get('error_code')) @handle_http_errors def auth_complete(self, *args, **kwargs): """Completes login process, must return user instance""" self.process_error(self.data) if not self.data.get('code'): raise AuthMissingParameter(self, 'code') state = self.validate_state() key, secret = self.get_key_and_secret() response = self.request(self.access_token_url(), params={ 'client_id': key, 'redirect_uri': self.get_redirect_uri(state), 'client_secret': secret, 'code': self.data['code'] }) # API v2.3 returns a JSON, according to the documents linked at issue # #592, but it seems that this needs to be enabled(?), otherwise the # usual querystring type response is returned. try: response = response.json() except ValueError: response = parse_qs(response.text) access_token = response['access_token'] return self.do_auth(access_token, response, *args, **kwargs) def process_refresh_token_response(self, response, *args, **kwargs): try: return response.json() except ValueError: return parse_qs(response.content) def refresh_token_params(self, token, *args, **kwargs): client_id, client_secret = self.get_key_and_secret() return { 'fb_exchange_token': token, 'grant_type': 'fb_exchange_token', 'client_id': client_id, 'client_secret': client_secret } def do_auth(self, access_token, response=None, *args, **kwargs): response = response or {} data = self.user_data(access_token) if not isinstance(data, dict): # From time to time Facebook responds back a JSON with just # False as value, the reason is still unknown, but since the # data is needed (it contains the user ID used to identify the # account on further logins), this app cannot allow it to # continue with the auth process. raise AuthUnknownError(self, 'An error ocurred while retrieving ' 'users Facebook data') data['access_token'] = access_token if 'expires_in' in response: data['expires'] = response['expires_in'] if self.data.get('granted_scopes'): data['granted_scopes'] = self.data['granted_scopes'].split(',') if self.data.get('denied_scopes'): data['denied_scopes'] = self.data['denied_scopes'].split(',') kwargs.update({'backend': self, 'response': data}) return self.strategy.authenticate(*args, **kwargs) def revoke_token_url(self, token, uid): version = self.setting('API_VERSION', API_VERSION) return self.REVOKE_TOKEN_URL.format(version=version, uid=uid) def revoke_token_params(self, token, uid): return {'access_token': token} def process_revoke_token_response(self, response): return super(FacebookOAuth2, self).process_revoke_token_response( response ) and response.content == 'true' class FacebookAppOAuth2(FacebookOAuth2): """Facebook Application Authentication support""" name = 'facebook-app' def uses_redirect(self): return False def auth_complete(self, *args, **kwargs): access_token = self.data.get('access_token') response = {} if 'signed_request' in self.data: key, secret = self.get_key_and_secret() response = self.load_signed_request(self.data['signed_request']) if 'user_id' not in response and 'oauth_token' not in response: raise AuthException(self) if response is not None: access_token = response.get('access_token') or \ response.get('oauth_token') or \ self.data.get('access_token') if access_token is None: if self.data.get('error') == 'access_denied': raise AuthCanceled(self) else: raise AuthException(self) return self.do_auth(access_token, response, *args, **kwargs) def auth_html(self): key, secret = self.get_key_and_secret() namespace = self.setting('NAMESPACE', None) scope = self.setting('SCOPE', '') if scope: scope = self.SCOPE_SEPARATOR.join(scope) ctx = { 'FACEBOOK_APP_NAMESPACE': namespace or key, 'FACEBOOK_KEY': key, 'FACEBOOK_EXTENDED_PERMISSIONS': scope, 'FACEBOOK_COMPLETE_URI': self.redirect_uri, } tpl = self.setting('LOCAL_HTML', 'facebook.html') return self.strategy.render_html(tpl=tpl, context=ctx) def load_signed_request(self, signed_request): def base64_url_decode(data): data = data.encode('ascii') data += '='.encode('ascii') * (4 - (len(data) % 4)) return base64.urlsafe_b64decode(data) key, secret = self.get_key_and_secret() try: sig, payload = signed_request.split('.', 1) except ValueError: pass # ignore if can't split on dot else: sig = base64_url_decode(sig) payload_json_bytes = base64_url_decode(payload) data = json.loads(payload_json_bytes.decode('utf-8', 'replace')) expected_sig = hmac.new(secret.encode('ascii'), msg=payload.encode('ascii'), digestmod=hashlib.sha256).digest() # allow the signed_request to function for upto 1 day if constant_time_compare(sig, expected_sig) and \ data['issued_at'] > (time.time() - 86400): return data social-auth-core-3.1.0/social_core/backends/dribbble.py0000644000175000017500000000425413336542652022721 0ustar omabomab00000000000000""" Dribbble OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/dribbble.html http://developer.dribbble.com/v1/oauth/ """ from .oauth import BaseOAuth2 class DribbbleOAuth2(BaseOAuth2): """Dribbble OAuth authentication backend""" name = 'dribbble' AUTHORIZATION_URL = 'https://dribbble.com/oauth/authorize' ACCESS_TOKEN_URL = 'https://dribbble.com/oauth/token' ACCESS_TOKEN_METHOD = 'POST' SCOPE_SEPARATOR = ',' EXTRA_DATA = [ ('id', 'id'), ('name', 'name'), ('html_url', 'html_url'), ('avatar_url', 'avatar_url'), ('bio', 'bio'), ('location', 'location'), ('links', 'links'), ('buckets_count', 'buckets_count'), ('comments_received_count', 'comments_received_count'), ('followers_count', 'followers_count'), ('followings_count', 'followings_count'), ('likes_count', 'likes_count'), ('likes_received_count', 'likes_received_count'), ('projects_count', 'projects_count'), ('rebounds_received_count', 'rebounds_received_count'), ('shots_count', 'shots_count'), ('teams_count', 'teams_count'), ('pro', 'pro'), ('buckets_url', 'buckets_url'), ('followers_url', 'followers_url'), ('following_url', 'following_url'), ('likes_url', 'shots_url'), ('teams_url', 'teams_url'), ('created_at', 'created_at'), ('updated_at', 'updated_at'), ] def get_user_details(self, response): """Return user details from Dribbble account""" fullname, first_name, last_name = self.get_user_names( response.get('name') ) return {'username': response.get('username'), 'email': response.get('email', ''), 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" return self.get_json( 'https://api.dribbble.com/v1/user', headers={ 'Authorization': 'Bearer {0}'.format(access_token) }) social-auth-core-3.1.0/social_core/backends/shimmering.py0000644000175000017500000000225513336542652023315 0ustar omabomab00000000000000""" Shimmering Oauth """ from .oauth import BaseOAuth2 class ShimmeringOAuth2(BaseOAuth2): """Shimmering Verify OAuth2 authentication backend""" name = 'shimmering' ID_KEY = 'id' AUTHORIZATION_URL = 'http://developers.shimmeringverify.com/o/authorize/' ACCESS_TOKEN_URL = 'http://developers.shimmeringverify.com/o/token/' ACCESS_TOKEN_METHOD = 'POST' def get_user_details(self, response): """Return user details from Shimmering""" first_name = response.get('first_name') last_name = response.get('last_name') email = response.get('email') username = response.get('username') fullname = '{} {}'.format(first_name, last_name) return { 'username': username, 'fullname': fullname, 'first_name': first_name, 'last_name': last_name, 'email': email, } def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" headers = {'Authorization': 'Bearer %s' % access_token} return self.get_json( 'http://developers.shimmeringverify.com/user_info/', headers=headers ) social-auth-core-3.1.0/social_core/backends/chatwork.py0000644000175000017500000000421513336542652022773 0ustar omabomab00000000000000""" Chatwork OAuth2 backend """ import base64 from .oauth import BaseOAuth2 class ChatworkOAuth2(BaseOAuth2): """Chatwork OAuth authentication backend""" name = 'chatwork' API_URL = 'https://api.chatwork.com/v2' AUTHORIZATION_URL = 'https://www.chatwork.com/packages/oauth2/login.php' ACCESS_TOKEN_URL = 'https://oauth.chatwork.com/token' ACCESS_TOKEN_METHOD = 'POST' REDIRECT_STATE = True DEFAULT_SCOPE = ['users.profile.me:read'] ID_KEY = 'account_id' EXTRA_DATA = [ ('expires_in', 'expires'), ('refresh_token', 'refresh_token') ] def api_url(self, path): api_url = self.setting('API_URL') or self.API_URL return '{0}{1}'.format(api_url.rstrip('/'), path) def auth_headers(self): return { 'Authorization': b'Basic ' + base64.b64encode( '{0}:{1}'.format(*self.get_key_and_secret()).encode() ) } def auth_complete_params(self, state=None): return { 'grant_type': 'authorization_code', 'code': self.data.get('code', ''), 'redirect_uri': self.get_redirect_uri(state) } def get_user_details(self, response): """Return user details from Chatwork account""" fullname, first_name, last_name = self.get_user_names( response.get('name') ) username = response.get('chatwork_id') or \ response.get('login_mail') or \ response.get('account_id') email = response.get('mail') or \ response.get('login_mail') or \ '' return { 'username': username, 'email': email, 'fullname': fullname, 'first_name': first_name, 'last_name': last_name } def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" headers = {'Authorization': 'Bearer ' + access_token} return self.get_json(self.api_url('/me'), headers=headers) def refresh_token_params(self, token, *args, **kwargs): return {'refresh_token': token, 'grant_type': 'refresh_token'} social-auth-core-3.1.0/social_core/backends/gae.py0000644000175000017500000000237013336542652021705 0ustar omabomab00000000000000""" Google App Engine support using User API """ from __future__ import absolute_import from google.appengine.api import users from .base import BaseAuth from ..exceptions import AuthException class GoogleAppEngineAuth(BaseAuth): """GoogleAppengine authentication backend""" name = 'google-appengine' def get_user_id(self, details, response): """Return current user id.""" user = users.get_current_user() if user: return user.user_id() def get_user_details(self, response): """Return user basic information (id and email only).""" user = users.get_current_user() return {'username': user.user_id(), 'email': user.email(), 'fullname': '', 'first_name': '', 'last_name': ''} def auth_url(self): """Build and return complete URL.""" return users.create_login_url(self.redirect_uri) def auth_complete(self, *args, **kwargs): """Completes login process, must return user instance.""" if not users.get_current_user(): raise AuthException('Authentication error') kwargs.update({'response': '', 'backend': self}) return self.strategy.authenticate(*args, **kwargs) social-auth-core-3.1.0/social_core/backends/withings.py0000644000175000017500000000103613336542652023003 0ustar omabomab00000000000000from .oauth import BaseOAuth1 class WithingsOAuth(BaseOAuth1): name = 'withings' AUTHORIZATION_URL = 'https://developer.health.nokia.com/account/authorize' REQUEST_TOKEN_URL = 'https://developer.health.nokia.com/account/request_token' ACCESS_TOKEN_URL = 'https://developer.health.nokia.com/account/access_token' ID_KEY = 'userid' def get_user_details(self, response): """Return user details from Withings account""" return {'userid': response['access_token']['userid'], 'email': ''} social-auth-core-3.1.0/social_core/backends/gitlab.py0000644000175000017500000000365213336542652022417 0ustar omabomab00000000000000""" GitLab OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/gitlab.html Thanks to [@saily](https://github.com/saily) who published an implementation for GitLab support on his blog post [Weblate with GitLab as OAuth provider](http://widerin.net/blog/weblate-gitlab-oauth-login/). His code was a great reference when working on this implementation. """ from requests import HTTPError from six.moves.urllib.parse import urljoin from .oauth import BaseOAuth2 from ..exceptions import AuthFailed class GitLabOAuth2(BaseOAuth2): """GitLab OAuth authentication backend""" name = 'gitlab' API_URL = 'https://gitlab.com' AUTHORIZATION_URL = 'https://gitlab.com/oauth/authorize' ACCESS_TOKEN_URL = 'https://gitlab.com/oauth/token' ACCESS_TOKEN_METHOD = 'POST' REDIRECT_STATE = False DEFAULT_SCOPE = ['read_user'] EXTRA_DATA = [ ('id', 'id'), ('expires_in', 'expires'), ('refresh_token', 'refresh_token') ] def api_url(self, path): api_url = self.setting('API_URL') or self.API_URL return '{0}{1}'.format(api_url.rstrip('/'), path) def authorization_url(self): return self.api_url('/oauth/authorize') def access_token_url(self): return self.api_url('/oauth/token') def get_user_details(self, response): """Return user details from GitLab account""" fullname, first_name, last_name = self.get_user_names( response.get('name') ) return {'username': response.get('username'), 'email': response.get('email') or '', 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" return self.get_json(self.api_url('/api/v4/user'), params={ 'access_token': access_token }) social-auth-core-3.1.0/social_core/backends/microsoft.py0000644000175000017500000000573313336542652023164 0ustar omabomab00000000000000import time from jwt import DecodeError, ExpiredSignature from ..exceptions import AuthTokenError from .oauth import BaseOAuth2 """ OAuth2 Backend to work with microsoft graph. """ class MicrosoftOAuth2(BaseOAuth2): name = 'microsoft-graph' SCOPE_SEPARATOR = ' ' AUTHORIZATION_URL = \ 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize' ACCESS_TOKEN_URL = \ 'https://login.microsoftonline.com/common/oauth2/v2.0/token' ACCESS_TOKEN_METHOD = 'POST' REDIRECT_STATE = False DEFAULT_SCOPE = ['User.Read'] def auth_complete(self, *args, **kwargs): """Completes login process, must return user instance""" self.process_error(self.data) state = self.validate_state() response = self.request_access_token( self.access_token_url(), data=self.auth_complete_params(state), headers=self.auth_headers(), auth=self.auth_complete_credentials(), method=self.ACCESS_TOKEN_METHOD ) self.process_error(response) return self.do_auth(response['access_token'], response=response, *args, **kwargs) def get_user_id(self, details, response): """Use user account id as unique id""" return response.get('id') def get_user_details(self, response): """Return user details from Microsoft online account""" email = response.get('mail') username = response.get('userPrincipalName') if '@' in username: if not email: email = username username = username.split('@', 1)[0] return {'username': username, 'email': email, 'fullname': response.get('displayName', ''), 'first_name': response.get('givenName', ''), 'last_name': response.get('surname', '')} def user_data(self, access_token, *args, **kwargs): """Return user data by querying Microsoft service""" try: return self.get_json( 'https://graph.microsoft.com/v1.0/me', headers={ 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json', 'Authorization': 'Bearer ' + access_token }, method='GET' ) except (DecodeError, ExpiredSignature) as error: raise AuthTokenError(self, error) def get_auth_token(self, user_id): """Return the access token for the given user, after ensuring that it has not expired, or refreshing it if so.""" user = self.get_user(user_id=user_id) access_token = user.social_user.access_token expires_on = user.social_user.extra_data['expires_on'] if expires_on <= int(time.time()): new_token_response = self.refresh_token(token=access_token) access_token = new_token_response['access_token'] return access_token social-auth-core-3.1.0/social_core/backends/vk.py0000644000175000017500000001576113336542652021601 0ustar omabomab00000000000000# -*- coding: utf-8 -*- """ VK.com OpenAPI, OAuth2 and Iframe application OAuth2 backends, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/vk.html """ import json from time import time from hashlib import md5 from ..utils import parse_qs from .base import BaseAuth from .oauth import BaseOAuth2 from ..exceptions import AuthTokenRevoked, AuthException class VKontakteOpenAPI(BaseAuth): """VK.COM OpenAPI authentication backend""" name = 'vk-openapi' ID_KEY = 'id' def get_user_details(self, response): """Return user details from VK.com request""" nickname = response.get('nickname') or '' fullname, first_name, last_name = self.get_user_names( first_name=response.get('first_name', [''])[0], last_name=response.get('last_name', [''])[0] ) return { 'username': response['id'] if len(nickname) == 0 else nickname, 'email': '', 'fullname': fullname, 'first_name': first_name, 'last_name': last_name } def user_data(self, access_token, *args, **kwargs): return self.data def auth_html(self): """Returns local VK authentication page, not necessary for VK to authenticate. """ ctx = {'VK_APP_ID': self.setting('APP_ID'), 'VK_COMPLETE_URL': self.redirect_uri} local_html = self.setting('LOCAL_HTML', 'vkontakte.html') return self.strategy.render_html(tpl=local_html, context=ctx) def auth_complete(self, *args, **kwargs): """Performs check of authentication in VKontakte, returns User if succeeded""" session_value = self.strategy.session_get( 'vk_app_' + self.setting('APP_ID') ) if 'id' not in self.data or not session_value: raise ValueError('VK.com authentication is not completed') mapping = parse_qs(session_value) check_str = ''.join(item + '=' + mapping[item] for item in ['expire', 'mid', 'secret', 'sid']) key, secret = self.get_key_and_secret() hash = md5((check_str + secret).encode('utf-8')).hexdigest() if hash != mapping['sig'] or int(mapping['expire']) < time(): raise ValueError('VK.com authentication failed: Invalid Hash') kwargs.update({'backend': self, 'response': self.user_data(mapping['mid'])}) return self.strategy.authenticate(*args, **kwargs) def uses_redirect(self): """VK.com does not require visiting server url in order to do authentication, so auth_xxx methods are not needed to be called. Their current implementation is just an example""" return False class VKOAuth2(BaseOAuth2): """VKOAuth2 authentication backend""" name = 'vk-oauth2' ID_KEY = 'id' AUTHORIZATION_URL = 'http://oauth.vk.com/authorize' ACCESS_TOKEN_URL = 'https://oauth.vk.com/access_token' ACCESS_TOKEN_METHOD = 'POST' EXTRA_DATA = [ ('id', 'id'), ('expires_in', 'expires') ] def get_user_details(self, response): """Return user details from VK.com account""" fullname, first_name, last_name = self.get_user_names( first_name=response.get('first_name'), last_name=response.get('last_name') ) return {'username': response.get('screen_name'), 'email': response.get('email', ''), 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" request_data = ['first_name', 'last_name', 'screen_name', 'nickname', 'photo'] + self.setting('EXTRA_DATA', []) fields = ','.join(set(request_data)) data = vk_api(self, 'users.get', { 'access_token': access_token, 'fields': fields, }) if data and data.get('error'): error = data['error'] msg = error.get('error_msg', 'Unknown error') if error.get('error_code') == 5: raise AuthTokenRevoked(self, msg) else: raise AuthException(self, msg) if data: data = data.get('response')[0] data['user_photo'] = data.get('photo') # Backward compatibility return data or {} class VKAppOAuth2(VKOAuth2): """VK.com Application Authentication support""" name = 'vk-app' def auth_complete(self, *args, **kwargs): required_params = ('is_app_user', 'viewer_id', 'access_token', 'api_id') if not all(param in self.data for param in required_params): return None auth_key = self.data.get('auth_key') # Verify signature, if present key, secret = self.get_key_and_secret() if auth_key: check_key = md5('_'.join([key, self.data.get('viewer_id'), secret]).encode('utf-8')).hexdigest() if check_key != auth_key: raise ValueError('VK.com authentication failed: invalid ' 'auth key') user_check = self.setting('USERMODE') user_id = self.data.get('viewer_id') if user_check is not None: user_check = int(user_check) if user_check == 1: is_user = self.data.get('is_app_user') elif user_check == 2: is_user = vk_api( self, 'isAppUser', {'user_id': user_id} ).get('response', 0) if not int(is_user): return None auth_data = { 'auth': self, 'backend': self, 'request': self.strategy.request_data(), 'response': { self.ID_KEY: user_id, } } auth_data['response'].update(json.loads(auth_data['request']['api_result'])['response'][0]) return self.strategy.authenticate(*args, **auth_data) def vk_api(backend, method, data): """ Calls VK.com OpenAPI method, check: https://vk.com/apiclub http://goo.gl/yLcaa """ # We need to perform server-side call if no access_token data['v'] = backend.setting('API_VERSION', '5.53') if 'access_token' not in data: key, secret = backend.get_key_and_secret() if 'api_id' not in data: data['api_id'] = key data['method'] = method data['format'] = 'json' url = 'http://api.vk.com/api.php' param_list = sorted(list(item + '=' + data[item] for item in data)) data['sig'] = md5( (''.join(param_list) + secret).encode('utf-8') ).hexdigest() else: url = 'https://api.vk.com/method/' + method try: return backend.get_json(url, params=data) except (TypeError, KeyError, IOError, ValueError, IndexError): return None social-auth-core-3.1.0/social_core/backends/box.py0000644000175000017500000000422413336542652021741 0ustar omabomab00000000000000""" Box.net OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/box.html """ from .oauth import BaseOAuth2 class BoxOAuth2(BaseOAuth2): """Box.net OAuth authentication backend""" name = 'box' AUTHORIZATION_URL = 'https://www.box.com/api/oauth2/authorize' ACCESS_TOKEN_METHOD = 'POST' ACCESS_TOKEN_URL = 'https://www.box.com/api/oauth2/token' REVOKE_TOKEN_URL = 'https://www.box.com/api/oauth2/revoke' SCOPE_SEPARATOR = ',' EXTRA_DATA = [ ('refresh_token', 'refresh_token', True), ('id', 'id'), ('expires', 'expires'), ] def do_auth(self, access_token, response=None, *args, **kwargs): response = response or {} data = self.user_data(access_token) data['access_token'] = response.get('access_token') data['refresh_token'] = response.get('refresh_token') data['expires'] = response.get('expires_in') kwargs.update({'backend': self, 'response': data}) return self.strategy.authenticate(*args, **kwargs) def get_user_details(self, response): """Return user details Box.net account""" fullname, first_name, last_name = self.get_user_names( response.get('name') ) return {'username': response.get('login'), 'email': response.get('login') or '', 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" params = self.setting('PROFILE_EXTRA_PARAMS', {}) params['access_token'] = access_token return self.get_json('https://api.box.com/2.0/users/me', params=params) def refresh_token(self, token, *args, **kwargs): params = self.refresh_token_params(token, *args, **kwargs) request = self.request(self.REFRESH_TOKEN_URL or self.ACCESS_TOKEN_URL, data=params, headers=self.auth_headers(), method='POST') return self.process_refresh_token_response(request, *args, **kwargs) social-auth-core-3.1.0/social_core/backends/elixir.py0000644000175000017500000000167213365406137022450 0ustar omabomab00000000000000""" Backend for OpenID Connect ELIXIR AAI https://www.elixir-europe.org/services/compute/aai """ from social_core.backends.open_id_connect import OpenIdConnectAuth class ElixirOpenIdConnect(OpenIdConnectAuth): name = 'elixir' OIDC_ENDPOINT = 'https://login.elixir-czech.org/oidc' EXTRA_DATA = [ ('expires_in', 'expires_in', True), ('refresh_token', 'refresh_token', True), ('id_token', 'id_token', True), ('other_tokens', 'other_tokens', True), ] def get_user_details(self, response): username_key = self.setting('USERNAME_KEY', default=self.USERNAME_KEY) name = response.get('name') or '' fullname, first_name, last_name = self.get_user_names(name) return {'username': response.get(username_key), 'email': response.get('email'), 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} social-auth-core-3.1.0/social_core/backends/mineid.py0000644000175000017500000000233213336542652022414 0ustar omabomab00000000000000from .oauth import BaseOAuth2 class MineIDOAuth2(BaseOAuth2): """MineID OAuth2 authentication backend""" name = 'mineid' _AUTHORIZATION_URL = '%(scheme)s://%(host)s/oauth/authorize' _ACCESS_TOKEN_URL = '%(scheme)s://%(host)s/oauth/access_token' ACCESS_TOKEN_METHOD = 'POST' SCOPE_SEPARATOR = ',' EXTRA_DATA = [ ] def get_user_details(self, response): """Return user details""" return {'email': response.get('email'), 'username': response.get('email')} def user_data(self, access_token, *args, **kwargs): return self._user_data(access_token) def _user_data(self, access_token, path=None): url = '%(scheme)s://%(host)s/api/user' % self.get_mineid_url_params() return self.get_json(url, params={'access_token': access_token}) @property def AUTHORIZATION_URL(self): return self._AUTHORIZATION_URL % self.get_mineid_url_params() @property def ACCESS_TOKEN_URL(self): return self._ACCESS_TOKEN_URL % self.get_mineid_url_params() def get_mineid_url_params(self): return { 'host': self.setting('HOST', 'www.mineid.org'), 'scheme': self.setting('SCHEME', 'https'), } social-auth-core-3.1.0/social_core/backends/disqus.py0000644000175000017500000000346113336542652022463 0ustar omabomab00000000000000""" Disqus OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/disqus.html """ from .oauth import BaseOAuth2 class DisqusOAuth2(BaseOAuth2): name = 'disqus' AUTHORIZATION_URL = 'https://disqus.com/api/oauth/2.0/authorize/' ACCESS_TOKEN_URL = 'https://disqus.com/api/oauth/2.0/access_token/' ACCESS_TOKEN_METHOD = 'POST' REDIRECT_STATE = False SCOPE_SEPARATOR = ',' EXTRA_DATA = [ ('avatar', 'avatar'), ('connections', 'connections'), ('user_id', 'user_id'), ('email', 'email'), ('email_hash', 'emailHash'), ('expires', 'expires'), ('location', 'location'), ('meta', 'response'), ('name', 'name'), ('username', 'username'), ] def get_user_id(self, details, response): return response['response']['id'] def get_user_details(self, response): """Return user details from Disqus account""" rr = response.get('response', {}) return { 'username': rr.get('username', ''), 'user_id': response.get('user_id', ''), 'email': rr.get('email', ''), 'name': rr.get('name', ''), } def extra_data(self, user, uid, response, details=None, *args, **kwargs): meta_response = dict(response, **response.get('response', {})) return super(DisqusOAuth2, self).extra_data(user, uid, meta_response, details, *args, **kwargs) def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" key, secret = self.get_key_and_secret() return self.get_json( 'https://disqus.com/api/3.0/users/details.json', params={'access_token': access_token, 'api_secret': secret} ) social-auth-core-3.1.0/social_core/backends/mixcloud.py0000644000175000017500000000170413336542652022775 0ustar omabomab00000000000000""" Mixcloud OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/mixcloud.html """ from .oauth import BaseOAuth2 class MixcloudOAuth2(BaseOAuth2): name = 'mixcloud' ID_KEY = 'username' AUTHORIZATION_URL = 'https://www.mixcloud.com/oauth/authorize' ACCESS_TOKEN_URL = 'https://www.mixcloud.com/oauth/access_token' ACCESS_TOKEN_METHOD = 'POST' def get_user_details(self, response): fullname, first_name, last_name = self.get_user_names(response['name']) return {'username': response['username'], 'email': None, 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token, *args, **kwargs): return self.get_json('https://api.mixcloud.com/me/', params={'access_token': access_token, 'alt': 'json'}) social-auth-core-3.1.0/social_core/backends/flat.py0000644000175000017500000000174413342001632022064 0ustar omabomab00000000000000""" Flat OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/flat.html """ from .oauth import BaseOAuth2 class FlatOAuth2(BaseOAuth2): """Flat OAuth2""" name = 'flat' DEFAULT_SCOPE = ['account.public_profile'] AUTHORIZATION_URL = 'https://flat.io/auth/oauth' ACCESS_TOKEN_URL = 'https://api.flat.io/oauth/access_token' ACCESS_TOKEN_METHOD = 'POST' def get_user_id(self, details, response): return response.get('id') def get_user_details(self, response): """Return user details from Flat account""" return { 'email': response.get('email'), 'username': response.get('username'), 'fullname': response.get('printableName') } def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" return self.get_json('https://api.flat.io/v2/me', headers={ 'Authorization': 'Bearer ' + access_token }) social-auth-core-3.1.0/social_core/backends/universe.py0000644000175000017500000000232313424575563023014 0ustar omabomab00000000000000from .oauth import BaseOAuth2 class UniverseOAuth2(BaseOAuth2): """Universe Ticketing OAuth2 authentication backend""" name = 'universe' AUTHORIZATION_URL = 'https://www.universe.com/oauth/authorize' ACCESS_TOKEN_URL = 'https://www.universe.com/oauth/token' BASE_API_URL = 'https://www.universe.com/api' USER_INFO_URL = BASE_API_URL + '/v2/current_user' ACCESS_TOKEN_METHOD = 'POST' STATE_PARAMETER = True REDIRECT_STATE = True EXTRA_DATA = [ ('id', 'id'), ('slug', 'slug'), ('created_at', 'created_at'), ('updated_at', 'updated_at'), ] def get_user_id(self, details, response): return response['current_user'][self.ID_KEY] def get_user_details(self, response): """Return user details from a Universe account""" # Start with the user data as it was returned user_details = response['current_user'] user_details['username'] = user_details['email'] return user_details def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" return self.get_json(self.USER_INFO_URL, headers={ 'Authorization': 'Bearer {}'.format(access_token) }) social-auth-core-3.1.0/social_core/backends/__init__.py0000644000175000017500000000000013336542652022674 0ustar omabomab00000000000000social-auth-core-3.1.0/social_core/backends/livejournal.py0000644000175000017500000000175313336542652023507 0ustar omabomab00000000000000""" LiveJournal OpenId backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/livejournal.html """ from six.moves.urllib_parse import urlsplit from .open_id import OpenIdAuth from ..exceptions import AuthMissingParameter class LiveJournalOpenId(OpenIdAuth): """LiveJournal OpenID authentication backend""" name = 'livejournal' def get_user_details(self, response): """Generate username from identity url""" values = super(LiveJournalOpenId, self).get_user_details(response) values['username'] = values.get('username') or \ urlsplit(response.identity_url)\ .netloc.split('.', 1)[0] return values def openid_url(self): """Returns LiveJournal authentication URL""" if not self.data.get('openid_lj_user'): raise AuthMissingParameter(self, 'openid_lj_user') return 'http://{0}.livejournal.com'.format(self.data['openid_lj_user']) social-auth-core-3.1.0/social_core/backends/nationbuilder.py0000644000175000017500000000312513336542652024007 0ustar omabomab00000000000000""" NationBuilder OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/nationbuilder.html """ from .oauth import BaseOAuth2 class NationBuilderOAuth2(BaseOAuth2): """NationBuilder OAuth2 authentication backend""" name = 'nationbuilder' AUTHORIZATION_URL = 'https://{slug}.nationbuilder.com/oauth/authorize' ACCESS_TOKEN_URL = 'https://{slug}.nationbuilder.com/oauth/token' ACCESS_TOKEN_METHOD = 'POST' REDIRECT_STATE = False SCOPE_SEPARATOR = ',' EXTRA_DATA = [ ('id', 'id'), ('expires', 'expires') ] def authorization_url(self): return self.AUTHORIZATION_URL.format(slug=self.slug) def access_token_url(self): return self.ACCESS_TOKEN_URL.format(slug=self.slug) @property def slug(self): return self.setting('SLUG') def get_user_details(self, response): """Return user details from Github account""" email = response.get('email') or '' username = email.split('@')[0] if email else '' return {'username': username, 'email': email, 'fullname': response.get('full_name') or '', 'first_name': response.get('first_name') or '', 'last_name': response.get('last_name') or ''} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" url = 'https://{slug}.nationbuilder.com/api/v1/people/me'.format( slug=self.slug ) return self.get_json(url, params={ 'access_token': access_token })['person'] social-auth-core-3.1.0/social_core/backends/classlink.py0000644000175000017500000000270013336542652023131 0ustar omabomab00000000000000from .oauth import BaseOAuth2 class ClasslinkOAuth(BaseOAuth2): """ Classlink OAuth authentication backend. Docs: https://developer.classlink.com/docs/oauth2-workflow """ name = 'classlink' AUTHORIZATION_URL = 'https://launchpad.classlink.com/oauth2/v2/auth' ACCESS_TOKEN_URL = 'https://launchpad.classlink.com/oauth2/v2/token' ACCESS_TOKEN_METHOD = 'POST' DEFAULT_SCOPE = ['profile'] REDIRECT_STATE = False SCOPE_SEPARATOR = ' ' def get_user_id(self, details, response): """Return user unique id provided by service""" return response['UserId'] def get_user_details(self, response): """Return user details from Classlink account""" fullname, first_name, last_name = self.get_user_names( first_name=response.get('FirstName'), last_name=response.get('LastName') ) return { 'username': response.get('Email') or response.get('LoginId'), 'email': response.get('Email'), 'fullname': fullname, 'first_name': first_name, 'last_name': last_name, } def user_data(self, token, *args, **kwargs): """Loads user data from service""" url = 'https://nodeapi.classlink.com/v2/my/info' auth_header = {"Authorization": "Bearer %s" % token} try: return self.get_json(url, headers=auth_header) except ValueError: return None social-auth-core-3.1.0/social_core/backends/jawbone.py0000644000175000017500000000534213342002520022556 0ustar omabomab00000000000000""" Jawbone OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/jawbone.html """ from ..utils import handle_http_errors from .oauth import BaseOAuth2 from ..exceptions import AuthCanceled, AuthUnknownError class JawboneOAuth2(BaseOAuth2): name = 'jawbone' AUTHORIZATION_URL = 'https://jawbone.com/auth/oauth2/auth' ACCESS_TOKEN_URL = 'https://jawbone.com/auth/oauth2/token' SCOPE_SEPARATOR = ' ' REDIRECT_STATE = False def get_user_id(self, details, response): return response['data']['xid'] def get_user_details(self, response): """Return user details from Jawbone account""" data = response['data'] fullname, first_name, last_name = self.get_user_names( first_name=data.get('first', ''), last_name=data.get('last', '') ) return { 'username': first_name + ' ' + last_name, 'fullname': fullname, 'first_name': first_name, 'last_name': last_name, 'dob': data.get('dob', ''), 'gender': data.get('gender', ''), 'height': data.get('height', ''), 'weight': data.get('weight', '') } def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" return self.get_json( 'https://jawbone.com/nudge/api/users/@me', headers={'Authorization': 'Bearer ' + access_token}, ) def process_error(self, data): error = data.get('error') if error: if error == 'access_denied': raise AuthCanceled(self) else: raise AuthUnknownError(self, 'Jawbone error was {0}'.format( error )) return super(JawboneOAuth2, self).process_error(data) def auth_complete_params(self, state=None): client_id, client_secret = self.get_key_and_secret() return { 'grant_type': 'authorization_code', # request auth code 'code': self.data.get('code', ''), # server response code 'client_id': client_id, 'client_secret': client_secret, } @handle_http_errors def auth_complete(self, *args, **kwargs): """Completes login process, must return user instance""" self.process_error(self.data) response = self.request_access_token( self.ACCESS_TOKEN_URL, params=self.auth_complete_params(self.validate_state()), headers=self.auth_headers(), method=self.ACCESS_TOKEN_METHOD ) self.process_error(response) return self.do_auth(response['access_token'], response=response, *args, **kwargs) social-auth-core-3.1.0/social_core/backends/appsfuel.py0000644000175000017500000000274113336542652022772 0ustar omabomab00000000000000""" Appsfueld OAuth2 backend (with sandbox mode support), docs at: https://python-social-auth.readthedocs.io/en/latest/backends/appsfuel.html """ from .oauth import BaseOAuth2 class AppsfuelOAuth2(BaseOAuth2): name = 'appsfuel' ID_KEY = 'user_id' AUTHORIZATION_URL = 'http://app.appsfuel.com/content/permission' ACCESS_TOKEN_URL = 'https://api.appsfuel.com/v1/live/oauth/token' ACCESS_TOKEN_METHOD = 'POST' USER_DETAILS_URL = 'https://api.appsfuel.com/v1/live/user' def get_user_details(self, response): """Return user details from Appsfuel account""" email = response.get('email', '') username = email.split('@')[0] if email else '' fullname, first_name, last_name = self.get_user_names( response.get('display_name', '') ) return { 'username': username, 'fullname': fullname, 'first_name': first_name, 'last_name': last_name, 'email': email } def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" return self.get_json(self.USER_DETAILS_URL, params={ 'access_token': access_token }) class AppsfuelOAuth2Sandbox(AppsfuelOAuth2): name = 'appsfuel-sandbox' AUTHORIZATION_URL = 'https://api.appsfuel.com/v1/sandbox/choose' ACCESS_TOKEN_URL = 'https://api.appsfuel.com/v1/sandbox/oauth/token' USER_DETAILS_URL = 'https://api.appsfuel.com/v1/sandbox/user' social-auth-core-3.1.0/social_core/backends/oauth.py0000644000175000017500000004212613432611340022260 0ustar omabomab00000000000000import six from requests_oauthlib import OAuth1 from oauthlib.oauth1 import SIGNATURE_TYPE_AUTH_HEADER from six.moves.urllib_parse import urlencode, unquote from ..utils import url_add_parameters, parse_qs, handle_http_errors, \ constant_time_compare from ..exceptions import AuthFailed, AuthCanceled, AuthUnknownError, \ AuthMissingParameter, AuthStateMissing, \ AuthStateForbidden, AuthTokenError from .base import BaseAuth class OAuthAuth(BaseAuth): """OAuth authentication backend base class. Also settings will be inspected to get more values names that should be stored on extra_data field. Setting name is created from current backend name (all uppercase) plus _EXTRA_DATA. access_token is always stored. URLs settings: AUTHORIZATION_URL Authorization service url ACCESS_TOKEN_URL Access token URL """ AUTHORIZATION_URL = '' ACCESS_TOKEN_URL = '' ACCESS_TOKEN_METHOD = 'GET' REVOKE_TOKEN_URL = None REVOKE_TOKEN_METHOD = 'POST' ID_KEY = 'id' SCOPE_PARAMETER_NAME = 'scope' DEFAULT_SCOPE = None SCOPE_SEPARATOR = ' ' REDIRECT_STATE = False STATE_PARAMETER = False def extra_data(self, user, uid, response, details=None, *args, **kwargs): """Return access_token and extra defined names to store in extra_data field""" data = super(OAuthAuth, self).extra_data(user, uid, response, details, *args, **kwargs) data['access_token'] = response.get('access_token', '') or \ kwargs.get('access_token') return data def state_token(self): """Generate csrf token to include as state parameter.""" return self.strategy.random_string(32) def get_or_create_state(self): if self.STATE_PARAMETER or self.REDIRECT_STATE: # Store state in session for further request validation. The state # value is passed as state parameter (as specified in OAuth2 spec), # but also added to redirect, that way we can still verify the # request if the provider doesn't implement the state parameter. # Reuse token if any. name = self.name + '_state' state = self.strategy.session_get(name) if state is None: state = self.state_token() self.strategy.session_set(name, state) else: state = None return state def get_session_state(self): return self.strategy.session_get(self.name + '_state') def get_request_state(self): request_state = self.data.get('state') or \ self.data.get('redirect_state') if request_state and isinstance(request_state, list): request_state = request_state[0] return request_state def validate_state(self): """Validate state value. Raises exception on error, returns state value if valid.""" if not self.STATE_PARAMETER and not self.REDIRECT_STATE: return None state = self.get_session_state() request_state = self.get_request_state() if not request_state: raise AuthMissingParameter(self, 'state') elif not state: raise AuthStateMissing(self, 'state') elif not constant_time_compare(request_state, state): raise AuthStateForbidden(self) else: return state def get_redirect_uri(self, state=None): """Build redirect with redirect_state parameter.""" uri = self.redirect_uri if self.REDIRECT_STATE and state: uri = url_add_parameters(uri, {'redirect_state': state}) return uri def get_scope(self): """Return list with needed access scope""" scope = self.setting('SCOPE', []) if not self.setting('IGNORE_DEFAULT_SCOPE', False): scope = scope + (self.DEFAULT_SCOPE or []) return scope def get_scope_argument(self): param = {} scope = self.get_scope() if scope: param[self.SCOPE_PARAMETER_NAME] = self.SCOPE_SEPARATOR.join(scope) return param def user_data(self, access_token, *args, **kwargs): """Loads user data from service. Implement in subclass""" return {} def authorization_url(self): return self.AUTHORIZATION_URL def access_token_url(self): return self.ACCESS_TOKEN_URL def revoke_token_url(self, token, uid): return self.REVOKE_TOKEN_URL def revoke_token_params(self, token, uid): return {} def revoke_token_headers(self, token, uid): return {} def process_revoke_token_response(self, response): return response.status_code == 200 def revoke_token(self, token, uid): if self.REVOKE_TOKEN_URL: url = self.revoke_token_url(token, uid) params = self.revoke_token_params(token, uid) headers = self.revoke_token_headers(token, uid) data = urlencode(params) if self.REVOKE_TOKEN_METHOD != 'GET' \ else None response = self.request(url, params=params, headers=headers, data=data, method=self.REVOKE_TOKEN_METHOD) return self.process_revoke_token_response(response) class BaseOAuth1(OAuthAuth): """Consumer based mechanism OAuth authentication, fill the needed parameters to communicate properly with authentication service. URLs settings: REQUEST_TOKEN_URL Request token URL """ REQUEST_TOKEN_URL = '' REQUEST_TOKEN_METHOD = 'GET' OAUTH_TOKEN_PARAMETER_NAME = 'oauth_token' REDIRECT_URI_PARAMETER_NAME = 'redirect_uri' UNATHORIZED_TOKEN_SUFIX = 'unauthorized_token_name' def auth_url(self): """Return redirect url""" token = self.set_unauthorized_token() return self.oauth_authorization_request(token) def process_error(self, data): if 'oauth_problem' in data: if data['oauth_problem'] == 'user_refused': raise AuthCanceled(self, 'User refused the access') raise AuthUnknownError(self, 'Error was ' + data['oauth_problem']) @handle_http_errors def auth_complete(self, *args, **kwargs): """Return user, might be logged in""" # Multiple unauthorized tokens are supported (see #521) self.process_error(self.data) self.validate_state() token = self.get_unauthorized_token() access_token = self.access_token(token) return self.do_auth(access_token, *args, **kwargs) @handle_http_errors def do_auth(self, access_token, *args, **kwargs): """Finish the auth process once the access_token was retrieved""" if not isinstance(access_token, dict): access_token = parse_qs(access_token) data = self.user_data(access_token) if data is not None and 'access_token' not in data: data['access_token'] = access_token kwargs.update({'response': data, 'backend': self}) return self.strategy.authenticate(*args, **kwargs) def get_unauthorized_token(self): name = self.name + self.UNATHORIZED_TOKEN_SUFIX unauthed_tokens = self.strategy.session_get(name, []) if not unauthed_tokens: raise AuthTokenError(self, 'Missing unauthorized token') data_token = self.data.get(self.OAUTH_TOKEN_PARAMETER_NAME) if data_token is None: raise AuthTokenError(self, 'Missing unauthorized token') token = None for utoken in unauthed_tokens: orig_utoken = utoken if not isinstance(utoken, dict): utoken = parse_qs(utoken) if utoken.get(self.OAUTH_TOKEN_PARAMETER_NAME) == data_token: self.strategy.session_set(name, list(set(unauthed_tokens) - set([orig_utoken]))) token = utoken break else: raise AuthTokenError(self, 'Incorrect tokens') return token def set_unauthorized_token(self): token = self.unauthorized_token() name = self.name + self.UNATHORIZED_TOKEN_SUFIX tokens = self.strategy.session_get(name, []) + [token] self.strategy.session_set(name, tokens) return token def request_token_extra_arguments(self): """Return extra arguments needed on request-token process""" return self.setting('REQUEST_TOKEN_EXTRA_ARGUMENTS', {}) def unauthorized_token(self): """Return request for unauthorized token (first stage)""" params = self.request_token_extra_arguments() params.update(self.get_scope_argument()) key, secret = self.get_key_and_secret() # decoding='utf-8' produces errors with python-requests on Python3 # since the final URL will be of type bytes decoding = None if six.PY3 else 'utf-8' state = self.get_or_create_state() response = self.request( self.REQUEST_TOKEN_URL, params=params, auth=OAuth1(key, secret, callback_uri=self.get_redirect_uri(state), decoding=decoding), method=self.REQUEST_TOKEN_METHOD ) content = response.content if response.encoding or response.apparent_encoding: content = content.decode(response.encoding or response.apparent_encoding) else: content = response.content.decode() return content def oauth_authorization_request(self, token): """Generate OAuth request to authorize token.""" if not isinstance(token, dict): token = parse_qs(token) params = self.auth_extra_arguments() or {} params.update(self.get_scope_argument()) params[self.OAUTH_TOKEN_PARAMETER_NAME] = token.get( self.OAUTH_TOKEN_PARAMETER_NAME ) state = self.get_or_create_state() params[self.REDIRECT_URI_PARAMETER_NAME] = self.get_redirect_uri(state) return '{0}?{1}'.format(self.authorization_url(), urlencode(params)) def oauth_auth(self, token=None, oauth_verifier=None, signature_type=SIGNATURE_TYPE_AUTH_HEADER): key, secret = self.get_key_and_secret() oauth_verifier = oauth_verifier or self.data.get('oauth_verifier') if token: resource_owner_key = token.get('oauth_token') resource_owner_secret = token.get('oauth_token_secret') if not resource_owner_key: raise AuthTokenError(self, 'Missing oauth_token') if not resource_owner_secret: raise AuthTokenError(self, 'Missing oauth_token_secret') else: resource_owner_key = None resource_owner_secret = None # decoding='utf-8' produces errors with python-requests on Python3 # since the final URL will be of type bytes decoding = None if six.PY3 else 'utf-8' state = self.get_or_create_state() return OAuth1(key, secret, resource_owner_key=resource_owner_key, resource_owner_secret=resource_owner_secret, callback_uri=self.get_redirect_uri(state), verifier=oauth_verifier, signature_type=signature_type, decoding=decoding) def oauth_request(self, token, url, params=None, method='GET'): """Generate OAuth request, setups callback url""" return self.request(url, method=method, params=params, auth=self.oauth_auth(token)) def access_token(self, token): """Return request for access token value""" return self.get_querystring(self.access_token_url(), auth=self.oauth_auth(token), method=self.ACCESS_TOKEN_METHOD) class BaseOAuth2(OAuthAuth): """Base class for OAuth2 providers. OAuth2 draft details at: http://tools.ietf.org/html/draft-ietf-oauth-v2-10 """ REFRESH_TOKEN_URL = None REFRESH_TOKEN_METHOD = 'POST' RESPONSE_TYPE = 'code' REDIRECT_STATE = True STATE_PARAMETER = True def auth_params(self, state=None): client_id, client_secret = self.get_key_and_secret() params = { 'client_id': client_id, 'redirect_uri': self.get_redirect_uri(state) } if self.STATE_PARAMETER and state: params['state'] = state if self.RESPONSE_TYPE: params['response_type'] = self.RESPONSE_TYPE return params def auth_url(self): """Return redirect url""" state = self.get_or_create_state() params = self.auth_params(state) params.update(self.get_scope_argument()) params.update(self.auth_extra_arguments()) params = urlencode(params) if not self.REDIRECT_STATE: # redirect_uri matching is strictly enforced, so match the # providers value exactly. params = unquote(params) return '{0}?{1}'.format(self.authorization_url(), params) def auth_complete_params(self, state=None): client_id, client_secret = self.get_key_and_secret() return { 'grant_type': 'authorization_code', # request auth code 'code': self.data.get('code', ''), # server response code 'client_id': client_id, 'client_secret': client_secret, 'redirect_uri': self.get_redirect_uri(state) } def auth_complete_credentials(self): return None def auth_headers(self): return {'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json'} def extra_data(self, user, uid, response, details=None, *args, **kwargs): """Return access_token, token_type, and extra defined names to store in extra_data field""" data = super(BaseOAuth2, self).extra_data(user, uid, response, details=details, *args, **kwargs) data['token_type'] = response.get('token_type') or \ kwargs.get('token_type') return data def request_access_token(self, *args, **kwargs): return self.get_json(*args, **kwargs) def process_error(self, data): if data.get('error'): if 'denied' in data['error'] or 'cancelled' in data['error']: raise AuthCanceled(self, data.get('error_description', '')) raise AuthFailed(self, data.get('error_description') or data['error']) elif 'denied' in data: raise AuthCanceled(self, data['denied']) @handle_http_errors def auth_complete(self, *args, **kwargs): """Completes login process, must return user instance""" self.process_error(self.data) state = self.validate_state() data, params = None, None if self.ACCESS_TOKEN_METHOD == 'GET': params = self.auth_complete_params(state) else: data = self.auth_complete_params(state) response = self.request_access_token( self.access_token_url(), data=data, params=params, headers=self.auth_headers(), auth=self.auth_complete_credentials(), method=self.ACCESS_TOKEN_METHOD ) self.process_error(response) return self.do_auth(response['access_token'], response=response, *args, **kwargs) @handle_http_errors def do_auth(self, access_token, *args, **kwargs): """Finish the auth process once the access_token was retrieved""" data = self.user_data(access_token, *args, **kwargs) response = kwargs.get('response') or {} response.update(data or {}) if 'access_token' not in response: response['access_token'] = access_token kwargs.update({'response': response, 'backend': self}) return self.strategy.authenticate(*args, **kwargs) def refresh_token_params(self, token, *args, **kwargs): client_id, client_secret = self.get_key_and_secret() return { 'refresh_token': token, 'grant_type': 'refresh_token', 'client_id': client_id, 'client_secret': client_secret } def process_refresh_token_response(self, response, *args, **kwargs): return response.json() def refresh_token(self, token, *args, **kwargs): params = self.refresh_token_params(token, *args, **kwargs) url = self.refresh_token_url() method = self.REFRESH_TOKEN_METHOD key = 'params' if method == 'GET' else 'data' request_args = {'headers': self.auth_headers(), 'method': method, key: params} request = self.request(url, **request_args) return self.process_refresh_token_response(request, *args, **kwargs) def refresh_token_url(self): return self.REFRESH_TOKEN_URL or self.access_token_url() social-auth-core-3.1.0/social_core/backends/amazon.py0000644000175000017500000000304413336542652022435 0ustar omabomab00000000000000""" Amazon OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/amazon.html """ import ssl from .oauth import BaseOAuth2 class AmazonOAuth2(BaseOAuth2): name = 'amazon' ID_KEY = 'user_id' AUTHORIZATION_URL = 'https://www.amazon.com/ap/oa' ACCESS_TOKEN_URL = 'https://api.amazon.com/auth/o2/token' DEFAULT_SCOPE = ['profile'] REDIRECT_STATE = False ACCESS_TOKEN_METHOD = 'POST' SSL_PROTOCOL = ssl.PROTOCOL_TLSv1 EXTRA_DATA = [ ('refresh_token', 'refresh_token', True), ('user_id', 'user_id'), ('postal_code', 'postal_code') ] def get_user_details(self, response): """Return user details from amazon account""" name = response.get('name') or '' fullname, first_name, last_name = self.get_user_names(name) return {'username': name, 'email': response.get('email'), 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token, *args, **kwargs): """Grab user profile information from amazon.""" response = self.get_json('https://www.amazon.com/ap/user/profile', params={'access_token': access_token}) if 'Profile' in response: response = { 'user_id': response['Profile']['CustomerId'], 'name': response['Profile']['Name'], 'email': response['Profile']['PrimaryEmail'] } return response social-auth-core-3.1.0/social_core/backends/coding.py0000644000175000017500000000271013336542652022412 0ustar omabomab00000000000000""" Coding OAuth2 backend, docs at: """ from six.moves.urllib.parse import urljoin from .oauth import BaseOAuth2 class CodingOAuth2(BaseOAuth2): """Coding OAuth authentication backend""" name = 'coding' API_URL = 'https://coding.net/api/' AUTHORIZATION_URL = 'https://coding.net/oauth_authorize.html' ACCESS_TOKEN_URL = 'https://coding.net/api/oauth/access_token' ACCESS_TOKEN_METHOD = 'POST' SCOPE_SEPARATOR = ',' DEFAULT_SCOPE = ['user'] REDIRECT_STATE = False def api_url(self): return self.API_URL def get_user_details(self, response): """Return user details from Github account""" fullname, first_name, last_name = self.get_user_names( response.get('name') ) return {'username': response.get('name'), 'email': response.get('email') or '', 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" data = self._user_data(access_token) if data.get('code') != 0: # 获取失败 pass return data.get('data') def _user_data(self, access_token, path=None): url = urljoin( self.api_url(), 'account/current_user{0}'.format(path or '') ) return self.get_json(url, params={'access_token': access_token}) social-auth-core-3.1.0/social_core/backends/bungie.py0000644000175000017500000000631713432621577022430 0ustar omabomab00000000000000""" Bungie OAuth2 backend """ import requests from social_core.backends.oauth import BaseOAuth2 class BungieOAuth2(BaseOAuth2): name = 'bungie' ID_KEY = 'membership_id' AUTHORIZATION_URL = 'https://www.bungie.net/en/oauth/authorize/' ACCESS_TOKEN_URL = 'https://www.bungie.net/platform/app/oauth/token/' REFRESH_TOKEN_URL = 'https://www.bungie.net/platform/app/oauth/token/' ACCESS_TOKEN_METHOD = 'POST' REDIRECT_STATE = False EXTRA_DATA = [ ('refresh_token', 'refresh_token', True), ('access_token', 'access_token', True), ('expires_in', 'expires'), ('membership_id', 'membership_id'), ('refresh_expires_in', 'refresh_expires_in') ] def auth_html(self): """Abstract Method Inclusion""" pass def auth_headers(self): """Adds X-API-KEY and Origin""" return { 'X-API-KEY': self.setting('API_KEY'), 'Content-Type': 'application/x-www-form-urlencoded', 'Origin': self.setting('ORIGIN'), 'Accept': 'application/json' } def make_bungie_request(self, url, access_token, kwargs): """Helper function to get username data keyed off displayName""" headers = self.auth_headers() headers['Authorization'] = 'Bearer ' + access_token return self.get_json(url, headers=headers) def auth_complete(self, *args, **kwargs): """Completes login process, must return user instance""" self.process_error(self.data) state = self.validate_state() response = self.request_access_token( self.access_token_url(), data=self.auth_complete_params(state), headers=self.auth_headers(), auth=self.auth_complete_credentials(), method=self.ACCESS_TOKEN_METHOD ) self.process_error(response) return self.do_auth(response['access_token'], response=response, *args, **kwargs) def do_auth(self, access_token, *args, **kwargs): """Finish the auth process once the access_token was retrieved""" data = self.user_data(access_token, *args, **kwargs) response = kwargs.get('response') or {} response.update(data or {}) if 'access_token' not in response: response['Response']['access_token']['value'] = access_token kwargs.update({'response': response, 'backend': self}) return self.strategy.authenticate(*args, **kwargs) def user_data(self, access_token, *args, **kwargs): """Grab user profile information from Bunige""" membership_id = kwargs['response']['membership_id'] url = 'https://www.bungie.net/Platform/User/GetBungieNetUser/' response = self.make_bungie_request(url, access_token, kwargs) username = response['Response']['user']['displayName'] return {'username': username, 'uid': membership_id} def get_user_details(self, response, *args, **kwargs): """Return user details from Bungie account""" username = response['username'] return { 'first_name': username, 'username': username, 'uid': response['uid'], } social-auth-core-3.1.0/social_core/backends/flickr.py0000644000175000017500000000273313336542652022426 0ustar omabomab00000000000000""" Flickr OAuth1 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/flickr.html """ from .oauth import BaseOAuth1 class FlickrOAuth(BaseOAuth1): """Flickr OAuth authentication backend""" name = 'flickr' AUTHORIZATION_URL = 'https://www.flickr.com/services/oauth/authorize' REQUEST_TOKEN_URL = 'https://www.flickr.com/services/oauth/request_token' ACCESS_TOKEN_URL = 'https://www.flickr.com/services/oauth/access_token' EXTRA_DATA = [ ('id', 'id'), ('username', 'username'), ('expires', 'expires') ] def get_user_details(self, response): """Return user details from Flickr account""" fullname, first_name, last_name = self.get_user_names( response.get('fullname') ) return {'username': response.get('username') or response.get('id'), 'email': '', 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" return { 'id': access_token['user_nsid'], 'username': access_token['username'], 'fullname': access_token.get('fullname', ''), } def auth_extra_arguments(self): params = super(FlickrOAuth, self).auth_extra_arguments() or {} if 'perms' not in params: params['perms'] = 'read' return params social-auth-core-3.1.0/social_core/backends/mailchimp.py0000644000175000017500000000212013336542652023105 0ustar omabomab00000000000000from .oauth import BaseOAuth2 class MailChimpOAuth2(BaseOAuth2): """MailChimp OAuth2 authentication backend""" name = 'mailchimp' AUTHORIZATION_URL = 'https://login.mailchimp.com/oauth2/authorize' ACCESS_TOKEN_URL = 'https://login.mailchimp.com/oauth2/token' METADATA_URL = 'https://login.mailchimp.com/oauth2/metadata' ACCESS_TOKEN_METHOD = 'POST' STATE_PARAMETER = False REDIRECT_STATE = False ID_KEY = 'user_id' EXTRA_DATA = [ ('accountname', 'accountname'), ('api_endpoint', 'api_endpoint'), ('role', 'role'), ('login', 'login') ] def get_user_details(self, response): """Return user details from a Mailchimp metadata response""" return { 'username': response['login']['login_name'], 'email': response['login']['email'] } def user_data(self, access_token, *args, **kwargs): """Loads user data and datacenter information from service""" return self.get_json(self.METADATA_URL, headers={ 'Authorization': 'OAuth ' + access_token }) social-auth-core-3.1.0/social_core/backends/twilio.py0000644000175000017500000000257013342002520022440 0ustar omabomab00000000000000""" Twilio auth backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/twilio.html """ from re import sub from six.moves.urllib_parse import urlencode from .base import BaseAuth class TwilioAuth(BaseAuth): name = 'twilio' ID_KEY = 'AccountSid' def get_user_details(self, response): """Return twilio details, Twilio only provides AccountSID as parameters.""" # /complete/twilio/?AccountSid=ACc65ea16c9ebd4d4684edf814995b27e return {'username': response['AccountSid'], 'email': '', 'fullname': '', 'first_name': '', 'last_name': ''} def auth_url(self): """Return authorization redirect url.""" key, secret = self.get_key_and_secret() callback = self.strategy.absolute_uri(self.redirect_uri) callback = sub(r'^https', 'http', callback) query = urlencode({'cb': callback}) return 'https://www.twilio.com/authorize/{0}?{1}'.format(key, query) def auth_complete(self, *args, **kwargs): """Completes login process, must return user instance""" account_sid = self.data.get('AccountSid') if not account_sid: raise ValueError('No AccountSid returned') kwargs.update({'response': self.data, 'backend': self}) return self.strategy.authenticate(*args, **kwargs) social-auth-core-3.1.0/social_core/backends/readability.py0000644000175000017500000000251213336542652023440 0ustar omabomab00000000000000""" Readability OAuth1 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/readability.html """ from .oauth import BaseOAuth1 READABILITY_API = 'https://www.readability.com/api/rest/v1' class ReadabilityOAuth(BaseOAuth1): """Readability OAuth authentication backend""" name = 'readability' ID_KEY = 'username' AUTHORIZATION_URL = '{0}/oauth/authorize/'.format(READABILITY_API) REQUEST_TOKEN_URL = '{0}/oauth/request_token/'.format(READABILITY_API) ACCESS_TOKEN_URL = '{0}/oauth/access_token/'.format(READABILITY_API) EXTRA_DATA = [('date_joined', 'date_joined'), ('kindle_email_address', 'kindle_email_address'), ('avatar_url', 'avatar_url'), ('email_into_address', 'email_into_address')] def get_user_details(self, response): fullname, first_name, last_name = self.get_user_names( first_name=response['first_name'], last_name=response['last_name'] ) return {'username': response['username'], 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token): return self.get_json(READABILITY_API + '/users/_current', auth=self.oauth_auth(access_token)) social-auth-core-3.1.0/social_core/backends/taobao.py0000644000175000017500000000155513336542652022422 0ustar omabomab00000000000000from .oauth import BaseOAuth2 class TAOBAOAuth(BaseOAuth2): """Taobao OAuth authentication mechanism""" name = 'taobao' ID_KEY = 'taobao_user_id' ACCESS_TOKEN_METHOD = 'POST' AUTHORIZATION_URL = 'https://oauth.taobao.com/authorize' ACCESS_TOKEN_URL = 'https://oauth.taobao.com/token' def user_data(self, access_token, *args, **kwargs): """Return user data provided""" try: return self.get_json('https://eco.taobao.com/router/rest', params={ 'method': 'taobao.user.get', 'fomate': 'json', 'v': '2.0', 'access_token': access_token }) except ValueError: return None def get_user_details(self, response): """Return user details from Taobao account""" return {'username': response.get('taobao_user_nick')} social-auth-core-3.1.0/social_core/backends/line.py0000644000175000017500000000675513417122006022076 0ustar omabomab00000000000000""" LINE Login OAuth2 backend, docs at: https://developers.line.me/en/docs/line-login/ """ import requests import json from .oauth import BaseOAuth2 from ..exceptions import AuthFailed from ..utils import handle_http_errors class LineOAuth2(BaseOAuth2): name = 'line' AUTHORIZATION_URL = 'https://access.line.me/oauth2/v2.1/authorize' ACCESS_TOKEN_URL = 'https://api.line.me/oauth2/v2.1/token' BASE_API_URL = 'https://api.line.me' USER_INFO_URL = BASE_API_URL + '/v2/profile' ACCESS_TOKEN_METHOD = 'POST' STATE_PARAMETER = True DEFAULT_SCOPE = ['profile'] REDIRECT_STATE = True ID_KEY = 'userId' EXTRA_DATA = [ ('userId', 'id'), ('picture_url', 'picture_url'), ('status_message', 'status_message'), ('expires_in', 'expire'), ('refresh_token', 'refresh_token') ] def auth_params(self, state=None): client_id, client_secret = self.get_key_and_secret() return { 'response_type': self.RESPONSE_TYPE, 'client_id': client_id, 'redirect_uri': self.get_redirect_uri(), 'state': self.get_or_create_state(), 'scope': self.get_scope() } def process_error(self, data): error_code = data.get('errorCode') or \ data.get('statusCode') or \ data.get('error') error_message = data.get('errorMessage') or \ data.get('error_description') if error_code is not None or error_message is not None: raise AuthFailed(self, error_message or error_code) @handle_http_errors def auth_complete(self, *args, **kwargs): """Completes login process, must return user instance""" self.process_error(self.data) try: response = self.request_access_token( self.access_token_url(), method=self.ACCESS_TOKEN_METHOD, headers=self.auth_headers(), data=self.auth_complete_params() ) self.process_error(response) return self.do_auth(response['access_token'], response=response, *args, **kwargs) except requests.HTTPError as err: self.process_error(json.loads(err.response.content)) def get_user_details(self, response): fullname, first_name, last_name = self.get_user_names( response.get('displayName') ) username = response.get('userId') picture_url = response.get('pictureUrl') status_message = response.get('statusMessage') return { 'username': username, 'fullname': fullname, 'first_name': first_name, 'last_name': last_name, 'picture_url': picture_url, 'status_message': status_message } def get_user_id(self, details, response): """ Return a unique ID for the current user, by default from server response. """ return response.get(self.ID_KEY) def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" try: response = self.get_json( self.USER_INFO_URL, headers={ "Authorization": "Bearer {}".format(access_token) } ) self.process_error(response) return response except requests.HTTPError as err: self.process_error(err.response.json()) social-auth-core-3.1.0/social_core/backends/legacy.py0000644000175000017500000000273213342002520022375 0ustar omabomab00000000000000from .base import BaseAuth from ..exceptions import AuthMissingParameter class LegacyAuth(BaseAuth): def get_user_id(self, details, response): return details.get(self.ID_KEY) or \ response.get(self.ID_KEY) def auth_url(self): return self.setting('FORM_URL') def auth_html(self): return self.strategy.render_html(tpl=self.setting('FORM_HTML')) def uses_redirect(self): return self.setting('FORM_URL') and not \ self.setting('FORM_HTML') def auth_complete(self, *args, **kwargs): """Completes login process, must return user instance""" if self.ID_KEY not in self.data: raise AuthMissingParameter(self, self.ID_KEY) kwargs.update({'response': self.data, 'backend': self}) return self.strategy.authenticate(*args, **kwargs) def get_user_details(self, response): """Return user details""" email = response.get('email', '') username = response.get('username', '') fullname, first_name, last_name = self.get_user_names( response.get('fullname', ''), response.get('first_name', ''), response.get('last_name', '') ) if email and not username: username = email.split('@', 1)[0] return { 'username': username, 'email': email, 'fullname': fullname, 'first_name': first_name, 'last_name': last_name } social-auth-core-3.1.0/social_core/backends/podio.py0000644000175000017500000000234113336542652022261 0ustar omabomab00000000000000""" Podio OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/podio.html """ from .oauth import BaseOAuth2 class PodioOAuth2(BaseOAuth2): """Podio OAuth authentication backend""" name = 'podio' AUTHORIZATION_URL = 'https://podio.com/oauth/authorize' ACCESS_TOKEN_URL = 'https://podio.com/oauth/token' ACCESS_TOKEN_METHOD = 'POST' EXTRA_DATA = [ ('access_token', 'access_token'), ('token_type', 'token_type'), ('expires_in', 'expires'), ('refresh_token', 'refresh_token'), ] def get_user_id(self, details, response): return response['ref']['id'] def get_user_details(self, response): fullname, first_name, last_name = self.get_user_names( response['profile']['name'] ) return { 'username': 'user_%d' % response['user']['user_id'], 'email': response['user']['mail'], 'fullname': fullname, 'first_name': first_name, 'last_name': last_name, } def user_data(self, access_token, *args, **kwargs): return self.get_json('https://api.podio.com/user/status', headers={'Authorization': 'OAuth2 ' + access_token}) social-auth-core-3.1.0/social_core/backends/slack.py0000644000175000017500000000406513336542652022251 0ustar omabomab00000000000000""" Slack OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/slack.html https://api.slack.com/docs/oauth """ from __future__ import unicode_literals from .oauth import BaseOAuth2 class SlackOAuth2(BaseOAuth2): """Slack OAuth authentication backend""" name = 'slack' AUTHORIZATION_URL = 'https://slack.com/oauth/authorize' ACCESS_TOKEN_URL = 'https://slack.com/api/oauth.access' ACCESS_TOKEN_METHOD = 'POST' DEFAULT_SCOPE = ['identity.basic', 'identity.email'] SCOPE_SEPARATOR = ',' REDIRECT_STATE = False EXTRA_DATA = [ ('id', 'id'), ('name', 'name'), ('real_name', 'real_name') ] def auth_extra_arguments(self): params = super(SlackOAuth2, self).auth_extra_arguments() or {} if self.setting('TEAM'): params['team'] = self.setting('TEAM') return params def get_user_details(self, response): """Return user details from Slack account""" # Build the username with the team $username@$team_url # Necessary to get unique names for all of slack user = response['user'] team = response.get('team') name = user['name'] email = user.get('email') username = email and email.split('@', 1)[0] or name fullname, first_name, last_name = self.get_user_names(name) if self.setting('USERNAME_WITH_TEAM', True) and team and \ 'name' in team: username = '{0}@{1}'.format(username, response['team']['name']) return { 'username': username, 'email': email, 'fullname': fullname, 'first_name': first_name, 'last_name': last_name } def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" response = self.get_json('https://slack.com/api/users.identity', params={'token': access_token}) if not response.get('id', None): response['id'] = response['user']['id'] return response social-auth-core-3.1.0/social_core/backends/kakao.py0000644000175000017500000000332413336542652022237 0ustar omabomab00000000000000""" Kakao OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/kakao.html """ from .oauth import BaseOAuth2 class KakaoOAuth2(BaseOAuth2): """Kakao OAuth authentication backend""" name = 'kakao' AUTHORIZATION_URL = 'https://kauth.kakao.com/oauth/authorize' ACCESS_TOKEN_URL = 'https://kauth.kakao.com/oauth/token' ACCESS_TOKEN_METHOD = 'POST' REDIRECT_STATE = False EXTRA_DATA = [ ('properties', 'properties'), ] def get_user_id(self, details, response): return response['id'] def get_user_details(self, response): """Return user details from Kakao account""" kaccount_email = response.get('kaccount_email', '') properties = response.get('properties', '') nickname = properties.get('nickname') if properties else '' return { 'username': nickname, 'email': kaccount_email, 'fullname': nickname, 'first_name': nickname[1:] if nickname else '', 'last_name': nickname[0] if nickname else '', } def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" return self.get_json( 'https://kapi.kakao.com/v1/user/me', headers={ 'Authorization': 'Bearer {0}'.format(access_token), 'Content_Type': 'application/x-www-form-urlencoded;charset=utf-8', }, params={'access_token': access_token} ) def auth_complete_params(self, state=None): return { 'grant_type': 'authorization_code', 'code': self.data.get('code', ''), 'client_id': self.get_key_and_secret()[0], } social-auth-core-3.1.0/social_core/backends/deezer.py0000644000175000017500000000317313336542652022431 0ustar omabomab00000000000000""" Deezer backend, docs at: https://developers.deezer.com/api/oauth https://developers.deezer.com/api/permissions """ from six.moves.urllib.parse import parse_qsl from .oauth import BaseOAuth2 class DeezerOAuth2(BaseOAuth2): """Deezer OAuth2 authentication backend""" name = 'deezer' ID_KEY = 'name' AUTHORIZATION_URL = 'https://connect.deezer.com/oauth/auth.php' ACCESS_TOKEN_URL = 'https://connect.deezer.com/oauth/access_token.php' ACCESS_TOKEN_METHOD = 'POST' SCOPE_SEPARATOR = ',' REDIRECT_STATE = False def auth_complete_params(self, state=None): client_id, client_secret = self.get_key_and_secret() return { 'app_id': client_id, 'secret': client_secret, 'code': self.data.get('code') } def request_access_token(self, *args, **kwargs): response = self.request(*args, **kwargs) return dict(parse_qsl(response.text)) def get_user_details(self, response): """Return user details from Deezer account""" fullname, first_name, last_name = self.get_user_names( first_name=response.get('firstname'), last_name=response.get('lastname') ) return {'username': response.get('name'), 'email': response.get('email'), 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" return self.get_json('http://api.deezer.com/user/me', params={ 'access_token': access_token }) social-auth-core-3.1.0/social_core/backends/thisismyjam.py0000644000175000017500000000230613336542652023511 0ustar omabomab00000000000000""" ThisIsMyJam OAuth1 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/thisismyjam.html """ from .oauth import BaseOAuth1 class ThisIsMyJamOAuth1(BaseOAuth1): """ThisIsMyJam OAuth1 authentication backend""" name = 'thisismyjam' REQUEST_TOKEN_URL = 'http://www.thisismyjam.com/oauth/request_token' AUTHORIZATION_URL = 'http://www.thisismyjam.com/oauth/authorize' ACCESS_TOKEN_URL = 'http://www.thisismyjam.com/oauth/access_token' REDIRECT_URI_PARAMETER_NAME = 'oauth_callback' def get_user_details(self, response): """Return user details from ThisIsMyJam account""" info = response.get('person') fullname, first_name, last_name = self.get_user_names( info.get('fullname') ) return { 'username': info.get('name'), 'email': '', 'fullname': fullname, 'first_name': first_name, 'last_name': last_name } def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" return self.get_json('http://api.thisismyjam.com/1/verify.json', auth=self.oauth_auth(access_token)) social-auth-core-3.1.0/social_core/backends/twitch.py0000644000175000017500000000162113336542652022451 0ustar omabomab00000000000000""" Twitch OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/twitch.html """ from .oauth import BaseOAuth2 class TwitchOAuth2(BaseOAuth2): """Twitch OAuth authentication backend""" name = 'twitch' ID_KEY = '_id' AUTHORIZATION_URL = 'https://api.twitch.tv/kraken/oauth2/authorize' ACCESS_TOKEN_URL = 'https://api.twitch.tv/kraken/oauth2/token' ACCESS_TOKEN_METHOD = 'POST' DEFAULT_SCOPE = ['user_read'] REDIRECT_STATE = False def get_user_details(self, response): return { 'username': response.get('name'), 'email': response.get('email'), 'first_name': '', 'last_name': '' } def user_data(self, access_token, *args, **kwargs): return self.get_json( 'https://api.twitch.tv/kraken/user/', params={'oauth_token': access_token} ) social-auth-core-3.1.0/social_core/backends/phabricator.py0000644000175000017500000000301213336542652023441 0ustar omabomab00000000000000""" Phabricator OAuth2 backend, docs at: https://secure.phabricator.com/book/phabcontrib/article/using_oauthserver/ """ from .oauth import BaseOAuth2 class PhabricatorOAuth2(BaseOAuth2): """Phabricator OAuth authentication backend""" name = 'phabricator' API_URL = 'https://secure.phabricator.com' AUTHORIZATION_URL = 'https://secure.phabricator.com/oauthserver/auth/' ACCESS_TOKEN_URL = 'https://secure.phabricator.com/oauthserver/token/' ACCESS_TOKEN_METHOD = 'POST' REDIRECT_STATE = False def api_url(self, path): api_url = self.setting('API_URL') or self.API_URL return '{0}{1}'.format(api_url.rstrip('/'), path) def authorization_url(self): return self.api_url('/oauthserver/auth/') def access_token_url(self): return self.api_url('/oauthserver/token/') def get_user_details(self, response): """Return user details from Phabricator""" fullname, first_name, last_name = self.get_user_names( response.get('realName') ) return { 'id': response.get('phid'), 'username': response.get('userName'), 'email': response.get('primaryEmail', ''), 'fullname': fullname, 'first_name': first_name, 'last_name': last_name, } def user_data(self, access_token, *args, **kwargs): """Loads user data from API""" return self.get_json(self.api_url('/api/user.whoami'), params={ 'access_token': access_token, }) social-auth-core-3.1.0/social_core/backends/coursera.py0000644000175000017500000000266413336542652023002 0ustar omabomab00000000000000""" Coursera OAuth2 backend, docs at: https://tech.coursera.org/app-platform/oauth2/ """ from .oauth import BaseOAuth2 class CourseraOAuth2(BaseOAuth2): """Coursera OAuth2 authentication backend""" name = 'coursera' ID_KEY = 'username' AUTHORIZATION_URL = 'https://accounts.coursera.org/oauth2/v1/auth' ACCESS_TOKEN_URL = 'https://accounts.coursera.org/oauth2/v1/token' ACCESS_TOKEN_METHOD = 'POST' REDIRECT_STATE = False SCOPE_SEPARATOR = ',' DEFAULT_SCOPE = ['view_profile'] def _get_username_from_response(self, response): elements = response.get('elements', []) for element in elements: if 'id' in element: return element.get('id') return None def get_user_details(self, response): """Return user details from Coursera account""" return {'username': self._get_username_from_response(response)} def get_user_id(self, details, response): """Return a username prepared in get_user_details as uid""" return details.get(self.ID_KEY) def user_data(self, access_token, *args, **kwargs): """Load user data from the service""" return self.get_json( 'https://api.coursera.org/api/externalBasicProfiles.v1?q=me', headers=self.get_auth_header(access_token) ) def get_auth_header(self, access_token): return {'Authorization': 'Bearer {0}'.format(access_token)} social-auth-core-3.1.0/social_core/backends/battlenet.py0000644000175000017500000000341313336542652023132 0ustar omabomab00000000000000from .oauth import BaseOAuth2 # This provides a backend for python-social-auth. This should not be confused # with officially battle.net offerings. This piece of code is not officially # affiliated with Blizzard Entertainment, copyrights to their respective # owners. See http://us.battle.net/en/forum/topic/13979588015 for more details. class BattleNetOAuth2(BaseOAuth2): """ battle.net Oauth2 backend""" name = 'battlenet-oauth2' ID_KEY = 'accountId' REDIRECT_STATE = False AUTHORIZATION_URL = 'https://eu.battle.net/oauth/authorize' ACCESS_TOKEN_URL = 'https://eu.battle.net/oauth/token' ACCESS_TOKEN_METHOD = 'POST' REVOKE_TOKEN_METHOD = 'GET' DEFAULT_SCOPE = ['wow.profile'] EXTRA_DATA = [ ('refresh_token', 'refresh_token', True), ('expires_in', 'expires'), ('token_type', 'token_type', True) ] def get_characters(self, access_token): """ Fetches the character list from the battle.net API. Returns list of characters or empty list if the request fails. """ params = {'access_token': access_token} if self.setting('API_LOCALE'): params['locale'] = self.setting('API_LOCALE') response = self.get_json( 'https://eu.api.battle.net/wow/user/characters', params=params ) return response.get('characters') or [] def get_user_details(self, response): """ Return user details from Battle.net account """ return {'battletag': response.get('battletag')} def user_data(self, access_token, *args, **kwargs): """ Loads user data from service """ return self.get_json( 'https://eu.api.battle.net/account/user', params={'access_token': access_token} ) social-auth-core-3.1.0/social_core/backends/qq.py0000644000175000017500000000421113365410437021563 0ustar omabomab00000000000000""" Created on May 13, 2014 @author: Yong Zhang (zyfyfe@gmail.com) """ import json from ..utils import parse_qs from .oauth import BaseOAuth2 class QQOAuth2(BaseOAuth2): name = 'qq' ID_KEY = 'openid' AUTHORIZE_URL = 'https://graph.qq.com/oauth2.0/authorize' ACCESS_TOKEN_URL = 'https://graph.qq.com/oauth2.0/token' AUTHORIZATION_URL = 'https://graph.qq.com/oauth2.0/authorize' OPENID_URL = 'https://graph.qq.com/oauth2.0/me' REDIRECT_STATE = False EXTRA_DATA = [ ('nickname', 'username'), ('figureurl_qq_1', 'profile_image_url'), ('gender', 'gender') ] def get_user_details(self, response): """ Return user detail from QQ account sometimes nickname will duplicate with another qq account, to avoid this issue it's possible to use openid as username. """ if self.setting('USE_OPENID_AS_USERNAME', False): username = response.get('openid', '') else: username = response.get('nickname', '') fullname, first_name, last_name = self.get_user_names( first_name=response.get('nickname', '') ) return { 'username': username, 'fullname': fullname, 'first_name': first_name, 'last_name': last_name } def get_openid(self, access_token): response = self.request(self.OPENID_URL, params={ 'access_token': access_token }) content = response.content.decode() data = json.loads(content[10:-3]) return data['openid'] def user_data(self, access_token, *args, **kwargs): openid = self.get_openid(access_token) response = self.get_json( 'https://graph.qq.com/user/get_user_info', params={ 'access_token': access_token, 'oauth_consumer_key': self.setting('KEY'), 'openid': openid } ) response['openid'] = openid return response def request_access_token(self, url, data, *args, **kwargs): response = self.request(url, *args, **kwargs) return parse_qs(response.content) social-auth-core-3.1.0/social_core/backends/persona.py0000644000175000017500000000345513342002520022603 0ustar omabomab00000000000000""" Mozilla Persona authentication backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/persona.html """ from ..utils import handle_http_errors from .base import BaseAuth from ..exceptions import AuthFailed, AuthMissingParameter class PersonaAuth(BaseAuth): """BrowserID authentication backend""" name = 'persona' def get_user_id(self, details, response): """Use BrowserID email as ID""" return details['email'] def get_user_details(self, response): """Return user details, BrowserID only provides Email.""" # {'status': 'okay', # 'audience': 'localhost:8000', # 'expires': 1328983575529, # 'email': 'name@server.com', # 'issuer': 'browserid.org'} email = response['email'] return {'username': email.split('@', 1)[0], 'email': email, 'fullname': '', 'first_name': '', 'last_name': ''} def extra_data(self, user, uid, response, details=None, *args, **kwargs): """Return users extra data""" return {'audience': response['audience'], 'issuer': response['issuer']} @handle_http_errors def auth_complete(self, *args, **kwargs): """Completes login process, must return user instance""" if 'assertion' not in self.data: raise AuthMissingParameter(self, 'assertion') response = self.get_json('https://browserid.org/verify', data={ 'assertion': self.data['assertion'], 'audience': self.strategy.request_host() }, method='POST') if response.get('status') == 'failure': raise AuthFailed(self) kwargs.update({'response': response, 'backend': self}) return self.strategy.authenticate(*args, **kwargs) social-auth-core-3.1.0/social_core/backends/github.py0000644000175000017500000000763613336542652022445 0ustar omabomab00000000000000""" Github OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/github.html """ from requests import HTTPError from six.moves.urllib.parse import urljoin from .oauth import BaseOAuth2 from ..exceptions import AuthFailed class GithubOAuth2(BaseOAuth2): """Github OAuth authentication backend""" name = 'github' API_URL = 'https://api.github.com/' AUTHORIZATION_URL = 'https://github.com/login/oauth/authorize' ACCESS_TOKEN_URL = 'https://github.com/login/oauth/access_token' ACCESS_TOKEN_METHOD = 'POST' SCOPE_SEPARATOR = ',' REDIRECT_STATE = False STATE_PARAMETER = True EXTRA_DATA = [ ('id', 'id'), ('expires', 'expires'), ('login', 'login') ] def api_url(self): return self.API_URL def get_user_details(self, response): """Return user details from Github account""" fullname, first_name, last_name = self.get_user_names( response.get('name') ) return {'username': response.get('login'), 'email': response.get('email') or '', 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" data = self._user_data(access_token) if not data.get('email'): try: emails = self._user_data(access_token, '/emails') except (HTTPError, ValueError, TypeError): emails = [] if emails: email = emails[0] primary_emails = [ e for e in emails if not isinstance(e, dict) or e.get('primary') ] if primary_emails: email = primary_emails[0] if isinstance(email, dict): email = email.get('email', '') data['email'] = email return data def _user_data(self, access_token, path=None): url = urljoin(self.api_url(), 'user{0}'.format(path or '')) return self.get_json(url, params={'access_token': access_token}) class GithubMemberOAuth2(GithubOAuth2): no_member_string = '' def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" user_data = super(GithubMemberOAuth2, self).user_data( access_token, *args, **kwargs ) try: self.request(self.member_url(user_data), params={ 'access_token': access_token }) except HTTPError as err: # if the user is a member of the organization, response code # will be 204, see http://bit.ly/ZS6vFl if err.response.status_code != 204: raise AuthFailed(self, 'User doesn\'t belong to the organization') return user_data def member_url(self, user_data): raise NotImplementedError('Implement in subclass') class GithubOrganizationOAuth2(GithubMemberOAuth2): """Github OAuth2 authentication backend for organizations""" name = 'github-org' no_member_string = 'User doesn\'t belong to the organization' def member_url(self, user_data): return urljoin( self.api_url(), 'orgs/{org}/members/{username}'.format( org=self.setting('NAME'), username=user_data.get('login') ) ) class GithubTeamOAuth2(GithubMemberOAuth2): """Github OAuth2 authentication backend for teams""" name = 'github-team' no_member_string = 'User doesn\'t belong to the team' def member_url(self, user_data): return urljoin( self.api_url(), 'teams/{team_id}/members/{username}'.format( team_id=self.setting('ID'), username=user_data.get('login') ) ) social-auth-core-3.1.0/social_core/backends/ngpvan.py0000644000175000017500000000406113336542652022441 0ustar omabomab00000000000000""" NGP VAN's `ActionID` Provider http://developers.ngpvan.com/action-id """ from openid.extensions import ax from .open_id import OpenIdAuth class ActionIDOpenID(OpenIdAuth): """ NGP VAN's ActionID OpenID 1.1 authentication backend """ name = 'actionid-openid' URL = 'https://accounts.ngpvan.com/Home/Xrds' USERNAME_KEY = 'email' def get_ax_attributes(self): """ Return the AX attributes that ActionID responds with, as well as the user data result that it must map to. """ return [ ('http://openid.net/schema/contact/internet/email', 'email'), ('http://openid.net/schema/contact/phone/business', 'phone'), ('http://openid.net/schema/namePerson/first', 'first_name'), ('http://openid.net/schema/namePerson/last', 'last_name'), ('http://openid.net/schema/namePerson', 'fullname'), ] def setup_request(self, params=None): """ Setup the OpenID request Because ActionID does not advertise the availiability of AX attributes nor use standard attribute aliases, we need to setup the attributes manually instead of rely on the parent OpenIdAuth.setup_request() """ request = self.openid_request(params) fetch_request = ax.FetchRequest() fetch_request.add(ax.AttrInfo( 'http://openid.net/schema/contact/internet/email', alias='ngpvanemail', required=True )) fetch_request.add(ax.AttrInfo( 'http://openid.net/schema/contact/phone/business', alias='ngpvanphone', required=False )) fetch_request.add(ax.AttrInfo( 'http://openid.net/schema/namePerson/first', alias='ngpvanfirstname', required=False )) fetch_request.add(ax.AttrInfo( 'http://openid.net/schema/namePerson/last', alias='ngpvanlastname', required=False )) request.addExtension(fetch_request) return request social-auth-core-3.1.0/social_core/backends/justgiving.py0000644000175000017500000000412413342002520023317 0ustar omabomab00000000000000from requests.auth import HTTPBasicAuth from ..utils import handle_http_errors from .oauth import BaseOAuth2 class JustGivingOAuth2(BaseOAuth2): """Just Giving OAuth authentication backend""" name = 'justgiving' ID_KEY = 'userId' AUTHORIZATION_URL = 'https://identity.justgiving.com/connect/authorize' ACCESS_TOKEN_URL = 'https://identity.justgiving.com/connect/token' ACCESS_TOKEN_METHOD = 'POST' USER_DATA_URL = 'https://api.justgiving.com/v1/account' DEFAULT_SCOPE = ['openid', 'account', 'profile', 'email', 'fundraise'] def get_user_details(self, response): """Return user details from Just Giving account""" fullname, first_name, last_name = self.get_user_names( '', response.get('firstName'), response.get('lastName')) return { 'username': response.get('email'), 'email': response.get('email'), 'fullname': fullname, 'first_name': first_name, 'last_name': last_name } def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" key, secret = self.get_key_and_secret() return self.get_json(self.USER_DATA_URL, headers={ 'Authorization': 'Bearer {0}'.format(access_token), 'Content-Type': 'application/json', 'x-application-key': secret, 'x-api-key': key }) @handle_http_errors def auth_complete(self, *args, **kwargs): """Completes login process, must return user instance""" state = self.validate_state() self.process_error(self.data) key, secret = self.get_key_and_secret() response = self.request_access_token( self.access_token_url(), data=self.auth_complete_params(state), headers=self.auth_headers(), auth=HTTPBasicAuth(key, secret), method=self.ACCESS_TOKEN_METHOD ) self.process_error(response) return self.do_auth(response['access_token'], response=response, *args, **kwargs) social-auth-core-3.1.0/social_core/backends/openshift.py0000644000175000017500000000215413336542652023150 0ustar omabomab00000000000000""" Openshift OAuth2 backend """ import requests from six.moves.urllib.parse import urljoin from ..utils import append_slash from .oauth import BaseOAuth2 class OpenshiftOAuth2(BaseOAuth2): name = 'openshift' ACCESS_TOKEN_METHOD = 'POST' def access_token_url(self): return urljoin(append_slash(self.setting('URL')), 'oauth/token') def authorization_url(self): return urljoin(append_slash(self.setting('URL')), 'oauth/authorize') def get_user_id(self, details, response): return response['metadata']['uid'] def get_user_details(self, response): """Return user details from openshift account""" username = response['metadata']['name'] email = response['metadata']['name'] return {'username': username, 'email': email} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" headers = {'Authorization': 'Bearer ' + access_token} return requests.get( urljoin(append_slash(self.setting('URL')), 'oapi/v1/users/~'), headers=headers ).json() social-auth-core-3.1.0/social_core/backends/goclio.py0000644000175000017500000000232013336542652022420 0ustar omabomab00000000000000from .oauth import BaseOAuth2 class GoClioOAuth2(BaseOAuth2): name = 'goclio' AUTHORIZATION_URL = 'https://app.goclio.com/oauth/authorize/' ACCESS_TOKEN_METHOD = 'POST' ACCESS_TOKEN_URL = 'https://app.goclio.com/oauth/token/' REDIRECT_STATE = False STATE_PARAMETER = False def get_user_details(self, response): """Return user details from GoClio account""" user = response.get('user', {}) username = user.get('id', None) email = user.get('email', None) first_name, last_name = (user.get('first_name', None), user.get('last_name', None)) fullname = '%s %s' % (first_name, last_name) return {'username': username, 'fullname': fullname, 'first_name': first_name, 'last_name': last_name, 'email': email} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" return self.get_json( 'https://app.goclio.com/api/v2/users/who_am_i', params={'access_token': access_token} ) def get_user_id(self, details, response): return response.get('user', {}).get('id') social-auth-core-3.1.0/social_core/backends/pushbullet.py0000644000175000017500000000147613336542652023346 0ustar omabomab00000000000000import base64 from .oauth import BaseOAuth2 class PushbulletOAuth2(BaseOAuth2): """pushbullet OAuth authentication backend""" name = 'pushbullet' EXTRA_DATA = [('id', 'id')] ID_KEY = 'username' AUTHORIZATION_URL = 'https://www.pushbullet.com/authorize' REQUEST_TOKEN_URL = 'https://api.pushbullet.com/oauth2/token' ACCESS_TOKEN_URL = 'https://api.pushbullet.com/oauth2/token' ACCESS_TOKEN_METHOD = 'POST' STATE_PARAMETER = False def get_user_details(self, response): return {'username': response.get('access_token')} def get_user_id(self, details, response): auth = 'Basic {0}'.format(base64.b64encode(details['username'])) return self.get_json('https://api.pushbullet.com/v2/users/me', headers={'Authorization': auth})['iden'] social-auth-core-3.1.0/social_core/backends/discord.py0000644000175000017500000000205013336542652022573 0ustar omabomab00000000000000""" Discord Auth OAuth2 backend, docs at: https://discordapp.com/developers/docs/topics/oauth2 """ from .oauth import BaseOAuth2 class DiscordOAuth2(BaseOAuth2): name = 'discord' AUTHORIZATION_URL = 'https://discordapp.com/api/oauth2/authorize' ACCESS_TOKEN_URL = 'https://discordapp.com/api/oauth2/token' ACCESS_TOKEN_METHOD = 'POST' REVOKE_TOKEN_URL = 'https://discordapp.com/api/oauth2/token/revoke' REVOKE_TOKEN_METHOD = 'GET' DEFAULT_SCOPE = ['identify'] SCOPE_SEPARATOR = '+' REDIRECT_STATE = False EXTRA_DATA = [ ('expires_in', 'expires'), ('refresh_token', 'refresh_token') ] def get_user_details(self, response): return {'username': response.get('username'), 'email': response.get('email') or ''} def user_data(self, access_token, *args, **kwargs): url = 'https://discordapp.com/api/users/@me' auth_header = {"Authorization": "Bearer %s" % access_token} return self.get_json(url, headers=auth_header) social-auth-core-3.1.0/social_core/backends/atlassian.py0000644000175000017500000000310313365406137023122 0ustar omabomab00000000000000from social_core.backends.oauth import BaseOAuth2 class AtlassianOAuth2(BaseOAuth2): name = 'atlassian' AUTHORIZATION_URL = 'https://accounts.atlassian.com/authorize' ACCESS_TOKEN_METHOD = 'POST' ACCESS_TOKEN_URL = 'https://api.atlassian.com/oauth/token' DEFAULT_SCOPE = ['read:jira-user', 'offline_access'] ID_KEY = 'accountId' EXTRA_DATA = [ ('resources', 'resources'), ('refresh_token', 'refresh_token'), ('expires_in', 'expires_in'), ] def auth_params(self, state=None): params = super(AtlassianOAuth2, self).auth_params(state) params.update({'audience': 'api.atlassian.com', 'prompt': 'consent'}) return params def get_user_details(self, response): fullname, first_name, last_name = self.get_user_names(response['displayName']) return {'username': response['name'], 'email': response['emailAddress'], 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token, *args, **kwargs): resources = self.get_json('https://api.atlassian.com/oauth/token/accessible-resources', headers={'Authorization': 'Bearer {}'.format(access_token)}) user_info = self.get_json('https://api.atlassian.com/ex/jira/{}/rest/api/2/myself'.format(resources[0]['id']), headers={'Authorization': 'Bearer {}'.format(access_token)}) user_info['resources'] = resources return user_info social-auth-core-3.1.0/social_core/backends/zotero.py0000644000175000017500000000166013336542652022474 0ustar omabomab00000000000000""" Zotero OAuth1 backends, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/zotero.html """ from .oauth import BaseOAuth1 class ZoteroOAuth(BaseOAuth1): """Zotero OAuth authorization mechanism""" name = 'zotero' AUTHORIZATION_URL = 'https://www.zotero.org/oauth/authorize' REQUEST_TOKEN_URL = 'https://www.zotero.org/oauth/request' ACCESS_TOKEN_URL = 'https://www.zotero.org/oauth/access' def get_user_id(self, details, response): """ Return user unique id provided by service. For Ubuntu One the nickname should be original. """ return details['userID'] def get_user_details(self, response): """Return user details from Zotero API account""" access_token = response.get('access_token', {}) return { 'username': access_token.get('username', ''), 'userID': access_token.get('userID', '') } social-auth-core-3.1.0/social_core/backends/douban.py0000644000175000017500000000406713336542652022426 0ustar omabomab00000000000000""" Douban OAuth1 and OAuth2 backends, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/douban.html """ from .oauth import BaseOAuth2, BaseOAuth1 class DoubanOAuth(BaseOAuth1): """Douban OAuth authentication backend""" name = 'douban' EXTRA_DATA = [('id', 'id')] AUTHORIZATION_URL = 'http://www.douban.com/service/auth/authorize' REQUEST_TOKEN_URL = 'http://www.douban.com/service/auth/request_token' ACCESS_TOKEN_URL = 'http://www.douban.com/service/auth/access_token' def get_user_id(self, details, response): return response['db:uid']['$t'] def get_user_details(self, response): """Return user details from Douban""" return {'username': response["db:uid"]["$t"], 'email': ''} def user_data(self, access_token, *args, **kwargs): """Return user data provided""" return self.get_json('http://api.douban.com/people/%40me?&alt=json', auth=self.oauth_auth(access_token)) class DoubanOAuth2(BaseOAuth2): """Douban OAuth authentication backend""" name = 'douban-oauth2' AUTHORIZATION_URL = 'https://www.douban.com/service/auth2/auth' ACCESS_TOKEN_URL = 'https://www.douban.com/service/auth2/token' ACCESS_TOKEN_METHOD = 'POST' REDIRECT_STATE = False EXTRA_DATA = [ ('id', 'id'), ('uid', 'username'), ('refresh_token', 'refresh_token'), ] def get_user_details(self, response): """Return user details from Douban""" fullname, first_name, last_name = self.get_user_names( response.get('name', '') ) return {'username': response.get('uid', ''), 'fullname': fullname, 'first_name': first_name, 'last_name': last_name, 'email': ''} def user_data(self, access_token, *args, **kwargs): """Return user data provided""" return self.get_json( 'https://api.douban.com/v2/user/~me', headers={'Authorization': 'Bearer {0}'.format(access_token)} ) social-auth-core-3.1.0/social_core/backends/monzo.py0000644000175000017500000000157313336542652022317 0ustar omabomab00000000000000from .oauth import BaseOAuth2 class MonzoOAuth2(BaseOAuth2): """ Monzo OAuth2 authentication backend. """ name = 'monzo' AUTHORIZATION_URL = 'https://auth.getmondo.co.uk/' ACCESS_TOKEN_URL = 'https://api.monzo.com/oauth2/token' ACCESS_TOKEN_METHOD = 'POST' REDIRECT_STATE = False def get_user_details(self, response): fullname, first_name, last_name = self.get_user_names( response['accounts'][0]['description'], ) return { 'username': str(response.get('user_id')), 'fullname': fullname, 'first_name': first_name, 'last_name': last_name, } def user_data(self, access_token, *args, **kwargs): return self.get_json( 'https://api.monzo.com/accounts', headers={'Authorization': 'Bearer {0}'.format(access_token)}, ) social-auth-core-3.1.0/social_core/backends/reddit.py0000644000175000017500000000346613336542652022433 0ustar omabomab00000000000000""" Reddit OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/reddit.html """ import base64 from .oauth import BaseOAuth2 class RedditOAuth2(BaseOAuth2): """Reddit OAuth2 authentication backend""" name = 'reddit' AUTHORIZATION_URL = 'https://ssl.reddit.com/api/v1/authorize' ACCESS_TOKEN_URL = 'https://ssl.reddit.com/api/v1/access_token' ACCESS_TOKEN_METHOD = 'POST' REFRESH_TOKEN_METHOD = 'POST' REDIRECT_STATE = False SCOPE_SEPARATOR = ',' DEFAULT_SCOPE = ['identity'] SEND_USER_AGENT = True EXTRA_DATA = [ ('id', 'id'), ('name', 'username'), ('link_karma', 'link_karma'), ('comment_karma', 'comment_karma'), ('refresh_token', 'refresh_token'), ('expires_in', 'expires') ] def get_user_details(self, response): """Return user details from Reddit account""" return {'username': response.get('name'), 'email': '', 'fullname': '', 'first_name': '', 'last_name': ''} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" return self.get_json( 'https://oauth.reddit.com/api/v1/me.json', headers={'Authorization': 'bearer ' + access_token} ) def auth_headers(self): return { 'Authorization': b'Basic ' + base64.urlsafe_b64encode( '{0}:{1}'.format(*self.get_key_and_secret()).encode() ) } def refresh_token_params(self, token, redirect_uri=None, *args, **kwargs): params = super(RedditOAuth2, self).refresh_token_params(token) params['redirect_uri'] = self.redirect_uri or redirect_uri return params def auth_complete_credentials(self): return self.get_key_and_secret() social-auth-core-3.1.0/social_core/backends/open_id_connect.py0000644000175000017500000001624513432611347024300 0ustar omabomab00000000000000import json import datetime from calendar import timegm import six from jose import jwk, jwt from jose.jwt import JWTError, JWTClaimsError, ExpiredSignatureError from jose.utils import base64url_decode from social_core.backends.oauth import BaseOAuth2 from social_core.utils import cache from social_core.exceptions import AuthTokenError class OpenIdConnectAssociation(object): """ Use Association model to save the nonce by force.""" def __init__(self, handle, secret='', issued=0, lifetime=0, assoc_type=''): self.handle = handle # as nonce self.secret = secret.encode() # not use self.issued = issued # not use self.lifetime = lifetime # not use self.assoc_type = assoc_type # as state class OpenIdConnectAuth(BaseOAuth2): """ Base class for Open ID Connect backends. Currently only the code response type is supported. """ # Override OIDC_ENDPOINT in your subclass to enable autoconfig of OIDC OIDC_ENDPOINT = None ID_TOKEN_MAX_AGE = 600 DEFAULT_SCOPE = ['openid', 'profile', 'email'] EXTRA_DATA = ['id_token', 'refresh_token', ('sub', 'id')] REDIRECT_STATE = False ACCESS_TOKEN_METHOD = 'POST' REVOKE_TOKEN_METHOD = 'GET' ID_KEY = 'sub' USERNAME_KEY = 'preferred_username' ID_TOKEN_ISSUER = '' ACCESS_TOKEN_URL = '' AUTHORIZATION_URL = '' REVOKE_TOKEN_URL = '' USERINFO_URL = '' JWKS_URI = '' JWT_DECODE_OPTIONS = dict() def __init__(self, *args, **kwargs): self.id_token = None super(OpenIdConnectAuth, self).__init__(*args, **kwargs) def authorization_url(self): return self.AUTHORIZATION_URL or \ self.oidc_config().get('authorization_endpoint') def access_token_url(self): return self.ACCESS_TOKEN_URL or \ self.oidc_config().get('token_endpoint') def revoke_token_url(self, token, uid): return self.REVOKE_TOKEN_URL or \ self.oidc_config().get('revocation_endpoint') def id_token_issuer(self): return self.ID_TOKEN_ISSUER or \ self.oidc_config().get('issuer') def userinfo_url(self): return self.USERINFO_URL or \ self.oidc_config().get('userinfo_endpoint') def jwks_uri(self): return self.JWKS_URI or \ self.oidc_config().get('jwks_uri') @cache(ttl=86400) def oidc_config(self): return self.get_json(self.OIDC_ENDPOINT + '/.well-known/openid-configuration') @cache(ttl=86400) def get_jwks_keys(self): keys = self.get_remote_jwks_keys() # Add client secret as oct key so it can be used for HMAC signatures # client_id, client_secret = self.get_key_and_secret() # keys.append({'key': client_secret, 'kty': 'oct'}) return keys def get_remote_jwks_keys(self): response = self.request(self.jwks_uri()) return json.loads(response.text)['keys'] def auth_params(self, state=None): """Return extra arguments needed on auth process.""" params = super(OpenIdConnectAuth, self).auth_params(state) params['nonce'] = self.get_and_store_nonce( self.authorization_url(), state ) return params def get_and_store_nonce(self, url, state): # Create a nonce nonce = self.strategy.random_string(64) # Store the nonce association = OpenIdConnectAssociation(nonce, assoc_type=state) self.strategy.storage.association.store(url, association) return nonce def get_nonce(self, nonce): try: return self.strategy.storage.association.get( server_url=self.authorization_url(), handle=nonce )[0] except IndexError: pass def remove_nonce(self, nonce_id): self.strategy.storage.association.remove([nonce_id]) def validate_claims(self, id_token): utc_timestamp = timegm(datetime.datetime.utcnow().utctimetuple()) if 'nbf' in id_token and utc_timestamp < id_token['nbf']: raise AuthTokenError(self, 'Incorrect id_token: nbf') # Verify the token was issued in the last 10 minutes iat_leeway = self.setting('ID_TOKEN_MAX_AGE', self.ID_TOKEN_MAX_AGE) if utc_timestamp > id_token['iat'] + iat_leeway: raise AuthTokenError(self, 'Incorrect id_token: iat') # Validate the nonce to ensure the request was not modified nonce = id_token.get('nonce') if not nonce: raise AuthTokenError(self, 'Incorrect id_token: nonce') nonce_obj = self.get_nonce(nonce) if nonce_obj: self.remove_nonce(nonce_obj.id) else: raise AuthTokenError(self, 'Incorrect id_token: nonce') def find_valid_key(self, id_token): for key in self.get_jwks_keys(): rsakey = jwk.construct(key) message, encoded_sig = id_token.rsplit('.', 1) decoded_sig = base64url_decode(encoded_sig.encode('utf-8')) if rsakey.verify(message.encode('utf-8'), decoded_sig): return key def validate_and_return_id_token(self, id_token, access_token): """ Validates the id_token according to the steps at http://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation. """ client_id, client_secret = self.get_key_and_secret() key = self.find_valid_key(id_token) if not key: raise AuthTokenError(self, 'Signature verification failed') alg = key['alg'] rsakey = jwk.construct(key) try: claims = jwt.decode( id_token, rsakey.to_pem().decode('utf-8'), algorithms=[alg], audience=client_id, issuer=self.id_token_issuer(), access_token=access_token, options=self.JWT_DECODE_OPTIONS, ) except ExpiredSignatureError: raise AuthTokenError(self, 'Signature has expired') except JWTClaimsError as error: raise AuthTokenError(self, str(error)) except JWTError: raise AuthTokenError(self, 'Invalid signature') self.validate_claims(claims) return claims def request_access_token(self, *args, **kwargs): """ Retrieve the access token. Also, validate the id_token and store it (temporarily). """ response = self.get_json(*args, **kwargs) self.id_token = self.validate_and_return_id_token( response['id_token'], response['access_token'] ) return response def user_data(self, access_token, *args, **kwargs): return self.get_json(self.userinfo_url(), headers={ 'Authorization': 'Bearer {0}'.format(access_token) }) def get_user_details(self, response): username_key = self.setting('USERNAME_KEY', default=self.USERNAME_KEY) return { 'username': response.get(username_key), 'email': response.get('email'), 'fullname': response.get('name'), 'first_name': response.get('given_name'), 'last_name': response.get('family_name'), } social-auth-core-3.1.0/social_core/backends/quizlet.py0000644000175000017500000000125413336542652022646 0ustar omabomab00000000000000""" Quizlet OAuth2 Sign-in backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/quizlet.html """ from .oauth import BaseOAuth2 class QuizletOAuth2(BaseOAuth2): """Quizlet OAuth2""" name = 'quizlet' ID_KEY = 'user_id' API_URL = 'https://api.quizlet.com/2.0/' AUTHORIZATION_URL = 'https://quizlet.com/authorize' ACCESS_TOKEN_URL = 'https://api.quizlet.com/oauth/token' ACCESS_TOKEN_METHOD = 'POST' SCOPE_SEPARATOR = ' ' DEFAULT_SCOPE = ['read'] def get_user_details(self, response): """Return user details from Quizlet account""" return { 'username': response.get('user_id') } social-auth-core-3.1.0/social_core/backends/eveonline.py0000644000175000017500000000263113336542652023135 0ustar omabomab00000000000000""" EVE Online Single Sign-On (SSO) OAuth2 backend Documentation at https://eveonline-third-party-documentation.readthedocs.io/en/latest/sso/index.html """ from .oauth import BaseOAuth2 class EVEOnlineOAuth2(BaseOAuth2): """EVE Online OAuth authentication backend""" name = 'eveonline' BASE_URL = 'https://login.eveonline.com/oauth' AUTHORIZATION_URL = BASE_URL + '/authorize' ACCESS_TOKEN_URL = BASE_URL + '/token' ID_KEY = 'CharacterID' ACCESS_TOKEN_METHOD = 'POST' EXTRA_DATA = [ ('CharacterID', 'id'), ('expires_in', 'expires'), ('CharacterOwnerHash', 'owner_hash', True), ('refresh_token', 'refresh_token', True), ] def get_user_details(self, response): """Return user details from EVE Online account""" user_data = self.user_data(response['access_token']) fullname, first_name, last_name = self.get_user_names( user_data['CharacterName'] ) return { 'email': '', 'username': fullname, 'fullname': fullname, 'first_name': first_name, 'last_name': last_name } def user_data(self, access_token, *args, **kwargs): """Get Character data from EVE server""" return self.get_json( 'https://login.eveonline.com/oauth/verify', headers={'Authorization': 'Bearer {0}'.format(access_token)} ) social-auth-core-3.1.0/social_core/backends/telegram.py0000644000175000017500000000427213336542652022754 0ustar omabomab00000000000000import hmac import time import hashlib from .base import BaseAuth from ..exceptions import AuthFailed, AuthMissingParameter from ..utils import handle_http_errors class TelegramAuth(BaseAuth): name = 'telegram' ID_KEY = 'id' def verify_data(self, response): bot_token = self.setting('BOT_TOKEN') if bot_token is None: raise AuthMissingParameter('telegram', 'SOCIAL_AUTH_TELEGRAM_BOT_TOKEN') received_hash_string = response.get('hash') auth_date = response.get('auth_date') if received_hash_string is None or auth_date is None: raise AuthMissingParameter('telegram', 'hash or auth_date') data_check_string = ['{}={}'.format(k, v) for k, v in response.items() if k != 'hash'] data_check_string = '\n'.join(sorted(data_check_string)) secret_key = hashlib.sha256(bot_token.encode()).digest() built_hash = hmac.new(secret_key, msg=data_check_string.encode(), digestmod=hashlib.sha256).hexdigest() current_timestamp = int(time.time()) auth_timestamp = int(auth_date) if current_timestamp - auth_timestamp > 86400: raise AuthFailed('telegram', 'Auth date is outdated') if built_hash != received_hash_string: raise AuthFailed('telegram', 'Invalid hash supplied') def extra_data(self, user, uid, response, details=None, *args, **kwargs): return response def get_user_details(self, response): first_name = response.get('first_name', '') last_name = response.get('last_name', '') fullname = '{} {}'.format(first_name, last_name).strip() return { 'username': response.get('username') or response[self.ID_KEY], 'first_name': first_name, 'last_name': last_name, 'fullname': fullname } @handle_http_errors def auth_complete(self, *args, **kwargs): response = self.data self.verify_data(response) kwargs.update({'response': self.data, 'backend': self}) return self.strategy.authenticate(*args, **kwargs) social-auth-core-3.1.0/social_core/backends/orbi.py0000644000175000017500000000236413336542652022107 0ustar omabomab00000000000000""" Orbi OAuth2 backend """ from .oauth import BaseOAuth2 class OrbiOAuth2(BaseOAuth2): """Orbi OAuth2 authentication backend""" name = 'orbi' AUTHORIZATION_URL = 'https://login.orbi.kr/oauth/authorize' ACCESS_TOKEN_URL = 'https://login.orbi.kr/oauth/token' ACCESS_TOKEN_METHOD = 'POST' EXTRA_DATA = [ ('imin', 'imin'), ('nick', 'nick'), ('photo', 'photo'), ('sex', 'sex'), ('birth', 'birth'), ] def get_user_id(self, details, response): return response.get('id') def get_user_details(self, response): fullname, first_name, last_name = self.get_user_names( response.get('name', ''), response.get('first_name', ''), response.get('last_name', '') ) return { 'username': response.get('username', response.get('name')), 'email': response.get('email', ''), 'fullname': fullname, 'first_name': first_name, 'last_name': last_name, } def user_data(self, access_token, *args, **kwargs): """Load user data from orbi""" return self.get_json('https://login.orbi.kr/oauth/user/get', params={ 'access_token': access_token }) social-auth-core-3.1.0/social_core/backends/live.py0000644000175000017500000000304013336542652022103 0ustar omabomab00000000000000""" Live OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/live.html """ from .oauth import BaseOAuth2 class LiveOAuth2(BaseOAuth2): name = 'live' AUTHORIZATION_URL = 'https://login.live.com/oauth20_authorize.srf' ACCESS_TOKEN_URL = 'https://login.live.com/oauth20_token.srf' ACCESS_TOKEN_METHOD = 'POST' SCOPE_SEPARATOR = ',' DEFAULT_SCOPE = ['wl.basic', 'wl.emails'] EXTRA_DATA = [ ('id', 'id'), ('access_token', 'access_token'), ('authentication_token', 'authentication_token'), ('refresh_token', 'refresh_token'), ('expires_in', 'expires'), ('email', 'email'), ('first_name', 'first_name'), ('last_name', 'last_name'), ('token_type', 'token_type'), ] REDIRECT_STATE = False def get_user_details(self, response): """Return user details from Live Connect account""" fullname, first_name, last_name = self.get_user_names( first_name=response.get('first_name'), last_name=response.get('last_name') ) return {'username': response.get('name'), 'email': response.get('emails', {}).get('account', ''), 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" return self.get_json('https://apis.live.net/v5.0/me', params={ 'access_token': access_token }) social-auth-core-3.1.0/social_core/backends/upwork.py0000644000175000017500000000257113336542652022503 0ustar omabomab00000000000000""" Upwork OAuth1 backend """ from .oauth import BaseOAuth1 class UpworkOAuth(BaseOAuth1): """Upwork OAuth authentication backend""" name = 'upwork' ID_KEY = 'id' AUTHORIZATION_URL = 'https://www.upwork.com/services/api/auth' REQUEST_TOKEN_URL = \ 'https://www.upwork.com/api/auth/v1/oauth/token/request' REQUEST_TOKEN_METHOD = 'POST' ACCESS_TOKEN_URL = 'https://www.upwork.com/api/auth/v1/oauth/token/access' ACCESS_TOKEN_METHOD = 'POST' REDIRECT_URI_PARAMETER_NAME = 'oauth_callback' def get_user_details(self, response): """Return user details from Upwork account""" info = response.get('info', {}) auth_user = response.get('auth_user', {}) first_name = auth_user.get('first_name') last_name = auth_user.get('last_name') fullname = '{} {}'.format(first_name, last_name) profile_url = info.get('profile_url', '') username = profile_url.rsplit('/')[-1].replace('~', '') return { 'username': username, 'fullname': fullname, 'first_name': first_name, 'last_name': last_name } def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" return self.get_json( 'https://www.upwork.com/api/auth/v1/info.json', auth=self.oauth_auth(access_token) ) social-auth-core-3.1.0/social_core/backends/exacttarget.py0000644000175000017500000000765113336542652023473 0ustar omabomab00000000000000""" ExactTarget OAuth support. Support Authentication from IMH using JWT token and pre-shared key. Requires package pyjwt """ from datetime import timedelta, datetime import jwt from ..exceptions import AuthFailed, AuthCanceled from .oauth import BaseOAuth2 class ExactTargetOAuth2(BaseOAuth2): name = 'exacttarget' def get_user_details(self, response): """Use the email address of the user, suffixed by _et""" user = response.get('token', {})\ .get('request', {})\ .get('user', {}) if 'email' in user: user['username'] = user['email'] return user def get_user_id(self, details, response): """ Create a user ID from the ET user ID. Uses details rather than the default response, as only the token is available in response. details is much richer: { 'expiresIn': 1200, 'username': 'example@example.com', 'refreshToken': '1234567890abcdef', 'internalOauthToken': 'jwttoken.......', 'oauthToken': 'yetanothertoken', 'id': 123456, 'culture': 'en-US', 'timezone': { 'shortName': 'CST', 'offset': -6.0, 'dst': False, 'longName': '(GMT-06:00) Central Time (No Daylight Saving)' }, 'email': 'example@example.com' } """ return '{0}'.format(details.get('id')) def uses_redirect(self): return False def auth_url(self): return None def process_error(self, data): if data.get('error'): error = self.data.get('error_description') or self.data['error'] raise AuthFailed(self, error) def do_auth(self, token, *args, **kwargs): dummy, secret = self.get_key_and_secret() try: # Decode the token, using the Application Signature from settings decoded = jwt.decode(token, secret, algorithms=['HS256']) except jwt.DecodeError: # Wrong signature, fail authentication raise AuthCanceled(self) kwargs.update({'response': {'token': decoded}, 'backend': self}) return self.strategy.authenticate(*args, **kwargs) def auth_complete(self, *args, **kwargs): """Completes login process, must return user instance""" token = self.data.get('jwt', {}) if not token: raise AuthFailed(self, 'Authentication Failed') return self.do_auth(token, *args, **kwargs) def extra_data(self, user, uid, response, details=None, *args, **kwargs): """Load extra details from the JWT token""" data = { 'id': details.get('id'), 'email': details.get('email'), # OAuth token, for use with legacy SOAP API calls: # http://bit.ly/13pRHfo 'internalOauthToken': details.get('internalOauthToken'), # Token for use with the Application ClientID for the FUEL API 'oauthToken': details.get('oauthToken'), # If the token has expired, use the FUEL API to get a new token see # http://bit.ly/10v1K5l and http://bit.ly/11IbI6F - set legacy=1 'refreshToken': details.get('refreshToken'), } # The expiresIn value determines how long the tokens are valid for. # Take a bit off, then convert to an int timestamp expiresSeconds = details.get('expiresIn', 0) - 30 expires = datetime.utcnow() + timedelta(seconds=expiresSeconds) data['expires'] = (expires - datetime(1970, 1, 1)).total_seconds() if response.get('token'): token = response['token'] org = token.get('request', {}).get('organization') if org: data['stack'] = org.get('stackKey') data['enterpriseId'] = org.get('enterpriseId') return data social-auth-core-3.1.0/social_core/backends/foursquare.py0000644000175000017500000000254313336542652023347 0ustar omabomab00000000000000""" Foursquare OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/foursquare.html """ from .oauth import BaseOAuth2 class FoursquareOAuth2(BaseOAuth2): name = 'foursquare' AUTHORIZATION_URL = 'https://foursquare.com/oauth2/authenticate' ACCESS_TOKEN_URL = 'https://foursquare.com/oauth2/access_token' ACCESS_TOKEN_METHOD = 'POST' API_VERSION = '20140128' def get_user_id(self, details, response): return response['response']['user']['id'] def get_user_details(self, response): """Return user details from Foursquare account""" info = response['response']['user'] email = info['contact']['email'] fullname, first_name, last_name = self.get_user_names( first_name=info.get('firstName', ''), last_name=info.get('lastName', '') ) return {'username': first_name + ' ' + last_name, 'fullname': fullname, 'first_name': first_name, 'last_name': last_name, 'email': email} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" return self.get_json('https://api.foursquare.com/v2/users/self', params={'oauth_token': access_token, 'v': self.API_VERSION}) social-auth-core-3.1.0/social_core/backends/azuread_b2c.py0000644000175000017500000001575113365602645023342 0ustar omabomab00000000000000""" Copyright (c) 2018 Noderabbit Inc., d.b.a. Appsembler All rights reserved. MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. See https://nicksnettravels.builttoroam.com/post/2017/01/24/Verifying-Azure-Active-Directory-JWT-Tokens.aspx for verifying JWT tokens. """ import json import six from cryptography.hazmat.primitives import serialization from jwt import DecodeError, ExpiredSignature, decode as jwt_decode from jwt.utils import base64url_decode try: from jwt.algorithms import RSAAlgorithm except ImportError as e: raise Exception( # Python 3.3 is not supported because of compatibility in # Cryptography package in Python3.3 You are welcome to patch # and open a pull request. 'Cryptography library is required for this backend ' \ '(AzureADB2COAuth2) to work. Note that this backend is only ' \ 'supported on Python 2 and Python 3.4+.' ) from ..exceptions import AuthException, AuthTokenError from .azuread import AzureADOAuth2 class AzureADB2COAuth2(AzureADOAuth2): name = 'azuread-b2c-oauth2' BASE_URL = 'https://login.microsoftonline.com/{tenant_id}' AUTHORIZATION_URL = '{base_url}/oauth2/v2.0/authorize' OPENID_CONFIGURATION_URL = '{base_url}/v2.0/.well-known/openid-configuration?p={policy}' ACCESS_TOKEN_URL = '{base_url}/oauth2/v2.0/token?p={policy}' JWKS_URL = '{base_url}/discovery/v2.0/keys?p={policy}' DEFAULT_SCOPE = ['openid', 'email'] EXTRA_DATA = [ ('access_token', 'access_token'), ('id_token', 'id_token'), ('refresh_token', 'refresh_token'), ('id_token_expires_in', 'expires'), ('exp', 'expires_on'), ('not_before', 'not_before'), ('given_name', 'first_name'), ('family_name', 'last_name'), ('tfp', 'policy'), ('token_type', 'token_type') ] @property def tenant_id(self): return self.setting('TENANT_ID', 'common') @property def policy(self): policy = self.setting('POLICY') if not policy or not policy.lower().startswith('b2c_'): raise AuthException('SOCIAL_AUTH_AZUREAD_B2C_OAUTH2_POLICY is ' 'required and should start with `b2c_`') return policy @property def base_url(self): return self.BASE_URL.format(tenant_id=self.tenant_id) def openid_configuration_url(self): return self.OPENID_CONFIGURATION_URL.format(base_url=self.base_url, policy=self.policy) def authorization_url(self): # Policy is required, but added later by `auth_extra_arguments()` return self.AUTHORIZATION_URL.format(base_url=self.base_url) def access_token_url(self): return self.ACCESS_TOKEN_URL.format(base_url=self.base_url, policy=self.policy) def jwks_url(self): return self.JWKS_URL.format(base_url=self.base_url, policy=self.policy) def request_access_token(self, *args, **kwargs): """ This is probably a hack, but otherwise AzureADOAuth2 expects `access_token`. However, B2C backends provides `id_token`. """ response = super(AzureADB2COAuth2, self).request_access_token( *args, **kwargs ) if 'access_token' not in response: response['access_token'] = response['id_token'] return response def auth_extra_arguments(self): """ Return extra arguments needed on auth process. The defaults can be overridden by GET parameters. """ extra_arguments = super(AzureADB2COAuth2, self).auth_extra_arguments() extra_arguments['p'] = self.policy or self.data.get('p') return extra_arguments def jwt_key_to_pem(self, key_json_dict): """ Builds a PEM formatted key string from a JWT public key dict. """ pub_key = RSAAlgorithm.from_jwk(json.dumps(key_json_dict)) return pub_key.public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo ) def get_user_id(self, details, response): """Use subject (sub) claim as unique id.""" return response.get('sub') def get_user_details(self, response): """ Email address is returned on a different attribute for AzureAD B2C backends. """ details = super(AzureADB2COAuth2, self).get_user_details(response) if not details['email'] and response.get('emails'): details['email'] = response['emails'] if isinstance(details.get('email'), (list, tuple)): details['email'] = details['email'][0] return details def get_public_key(self, kid): """ Retrieve JWT keys from the URL. """ resp = self.request(self.jwks_url(), method='GET') resp.raise_for_status() # find the proper key for the kid for key in resp.json()['keys']: if key['kid'] == kid: return self.jwt_key_to_pem(key) raise DecodeError('Cannot find kid={}'.format(kid)) def user_data(self, access_token, *args, **kwargs): response = kwargs.get('response') id_token = response.get('id_token') if six.PY2: # str() to fix a bug in Python's base64 # https://stackoverflow.com/a/2230623/161278 id_token = str(id_token) jwt_header_json = base64url_decode(id_token.split('.')[0]) jwt_header = json.loads(jwt_header_json.decode('ascii')) # `kid` is short for key id key = self.get_public_key(jwt_header['kid']) try: return jwt_decode( id_token, key=key, algorithms=jwt_header['alg'], audience=self.setting('KEY'), leeway=self.setting('JWT_LEEWAY', default=0), ) except (DecodeError, ExpiredSignature) as error: raise AuthTokenError(self, error) social-auth-core-3.1.0/social_core/backends/angel.py0000644000175000017500000000205413336542652022236 0ustar omabomab00000000000000""" Angel OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/angel.html """ from .oauth import BaseOAuth2 class AngelOAuth2(BaseOAuth2): name = 'angel' AUTHORIZATION_URL = 'https://angel.co/api/oauth/authorize/' ACCESS_TOKEN_METHOD = 'POST' ACCESS_TOKEN_URL = 'https://angel.co/api/oauth/token/' REDIRECT_STATE = False def get_user_details(self, response): """Return user details from Angel account""" username = response['angellist_url'].split('/')[-1] email = response.get('email', '') fullname, first_name, last_name = self.get_user_names(response['name']) return {'username': username, 'fullname': fullname, 'first_name': first_name, 'last_name': last_name, 'email': email} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" return self.get_json('https://api.angel.co/1/me/', params={ 'access_token': access_token }) social-auth-core-3.1.0/social_core/backends/strava.py0000644000175000017500000000347513336542652022460 0ustar omabomab00000000000000""" Strava OAuth2 backend, docs at: https://python-social-auth.readthedocs.io/en/latest/backends/strava.html """ from .oauth import BaseOAuth2 class StravaOAuth(BaseOAuth2): name = 'strava' AUTHORIZATION_URL = 'https://www.strava.com/oauth/authorize' ACCESS_TOKEN_URL = 'https://www.strava.com/oauth/token' ACCESS_TOKEN_METHOD = 'POST' # Strava doesn't check for parameters in redirect_uri and directly appends # the auth parameters to it, ending with an URL like: # http://example.com/complete/strava?redirect_state=xxx?code=xxx&state=xxx # Check issue #259 for details. REDIRECT_STATE = False REVOKE_TOKEN_URL = 'https://www.strava.com/oauth/deauthorize' def get_user_id(self, details, response): return response['athlete']['id'] def get_user_details(self, response): """Return user details from Strava account""" # because there is no usernames on strava username = response['athlete']['id'] email = response['athlete'].get('email', '') fullname, first_name, last_name = self.get_user_names( first_name=response['athlete'].get('firstname', ''), last_name=response['athlete'].get('lastname', ''), ) return {'username': str(username), 'fullname': fullname, 'first_name': first_name, 'last_name': last_name, 'email': email} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" return self.get_json('https://www.strava.com/api/v3/athlete', params={'access_token': access_token}) def revoke_token_params(self, token, uid): params = super(StravaOAuth, self).revoke_token_params(token, uid) params['access_token'] = token return params social-auth-core-3.1.0/social_core/tests/0000755000175000017500000000000013433256445020165 5ustar omabomab00000000000000social-auth-core-3.1.0/social_core/tests/pipeline.py0000644000175000017500000000225613336542652022351 0ustar omabomab00000000000000from ..pipeline.partial import partial @partial def ask_for_password(strategy, *args, **kwargs): if strategy.session_get('password'): return {'password': strategy.session_get('password')} else: return strategy.redirect(strategy.build_absolute_uri('/password')) @partial def ask_for_slug(strategy, *args, **kwargs): if strategy.session_get('slug'): return {'slug': strategy.session_get('slug')} else: return strategy.redirect(strategy.build_absolute_uri('/slug')) def set_password(strategy, user, *args, **kwargs): user.set_password(kwargs['password']) def set_slug(strategy, user, *args, **kwargs): user.slug = kwargs['slug'] def remove_user(strategy, user, *args, **kwargs): return {'user': None} @partial def set_user_from_kwargs(strategy, *args, **kwargs): if strategy.session_get('attribute'): kwargs['user'].id else: return strategy.redirect(strategy.build_absolute_uri('/attribute')) @partial def set_user_from_args(strategy, user, *args, **kwargs): if strategy.session_get('attribute'): user.id else: return strategy.redirect(strategy.build_absolute_uri('/attribute')) social-auth-core-3.1.0/social_core/tests/requirements-base.txt0000644000175000017500000000007213336542652024360 0ustar omabomab00000000000000httpretty==0.6.5 coverage>=3.6 nose>=1.2.1 rednose>=0.4.1 social-auth-core-3.1.0/social_core/tests/requirements.txt0000644000175000017500000000003413336542652023446 0ustar omabomab00000000000000-r requirements-python2.txt social-auth-core-3.1.0/social_core/tests/models.py0000644000175000017500000001360013336542652022022 0ustar omabomab00000000000000import base64 from ..storage import UserMixin, NonceMixin, AssociationMixin, \ CodeMixin, PartialMixin, BaseStorage class BaseModel(object): @classmethod def next_id(cls): cls.NEXT_ID += 1 return cls.NEXT_ID - 1 @classmethod def get(cls, key): return cls.cache.get(key) @classmethod def reset_cache(cls): cls.cache = {} class User(BaseModel): NEXT_ID = 1 cache = {} _is_active = True def __init__(self, username, email=None, **extra_user_fields): self.id = User.next_id() self.username = username self.email = email self.password = None self.slug = None self.social = [] self.extra_data = {} self.extra_user_fields = extra_user_fields self.save() def is_active(self): return self._is_active @classmethod def set_active(cls, is_active=True): cls._is_active = is_active def set_password(self, password): self.password = password def save(self): User.cache[self.username] = self class TestUserSocialAuth(UserMixin, BaseModel): NEXT_ID = 1 cache = {} cache_by_uid = {} def __init__(self, user, provider, uid, extra_data=None): self.id = TestUserSocialAuth.next_id() self.user = user self.provider = provider self.uid = uid self.extra_data = extra_data or {} self.user.social.append(self) TestUserSocialAuth.cache_by_uid[uid] = self def save(self): pass @classmethod def reset_cache(cls): cls.cache = {} cls.cache_by_uid = {} @classmethod def changed(cls, user): pass @classmethod def get_username(cls, user): return user.username @classmethod def user_model(cls): return User @classmethod def username_max_length(cls): return 1024 @classmethod def allowed_to_disconnect(cls, user, backend_name, association_id=None): return user.password or len(user.social) > 1 @classmethod def disconnect(cls, entry): cls.cache.pop(entry.id, None) entry.user.social = [s for s in entry.user.social if entry != s] @classmethod def user_exists(cls, username): return User.cache.get(username) is not None @classmethod def create_user(cls, username, email=None, **extra_user_fields): return User(username=username, email=email, **extra_user_fields) @classmethod def get_user(cls, pk): for username, user in User.cache.items(): if user.id == pk: return user @classmethod def get_social_auth(cls, provider, uid): social_user = cls.cache_by_uid.get(uid) if social_user and social_user.provider == provider: return social_user @classmethod def get_social_auth_for_user(cls, user, provider=None, id=None): return [usa for usa in user.social if provider in (None, usa.provider) and id in (None, usa.id)] @classmethod def create_social_auth(cls, user, uid, provider): return cls(user=user, provider=provider, uid=uid) @classmethod def get_users_by_email(cls, email): return [user for user in User.cache.values() if user.email == email] class TestNonce(NonceMixin, BaseModel): NEXT_ID = 1 cache = {} def __init__(self, server_url, timestamp, salt): self.id = TestNonce.next_id() self.server_url = server_url self.timestamp = timestamp self.salt = salt @classmethod def use(cls, server_url, timestamp, salt): nonce = TestNonce(server_url, timestamp, salt) TestNonce.cache[server_url] = nonce return nonce class TestAssociation(AssociationMixin, BaseModel): NEXT_ID = 1 cache = {} def __init__(self, server_url, handle): self.id = TestAssociation.next_id() self.server_url = server_url self.handle = handle def save(self): TestAssociation.cache[(self.server_url, self.handle)] = self @classmethod def store(cls, server_url, association): assoc = TestAssociation.cache.get((server_url, association.handle)) if assoc is None: assoc = TestAssociation(server_url=server_url, handle=association.handle) assoc.secret = base64.encodestring(association.secret) assoc.issued = association.issued assoc.lifetime = association.lifetime assoc.assoc_type = association.assoc_type assoc.save() @classmethod def get(cls, server_url=None, handle=None): result = [] for assoc in TestAssociation.cache.values(): if server_url and assoc.server_url != server_url: continue if handle and assoc.handle != handle: continue result.append(assoc) return result @classmethod def remove(cls, ids_to_delete): assoc = filter(lambda a: a.id in ids_to_delete, TestAssociation.cache.values()) for a in list(assoc): TestAssociation.cache.pop((a.server_url, a.handle), None) class TestCode(CodeMixin, BaseModel): NEXT_ID = 1 cache = {} @classmethod def get_code(cls, code): for c in cls.cache.values(): if c.code == code: return c class TestPartial(PartialMixin, BaseModel): NEXT_ID = 1 cache = {} def save(self): TestPartial.cache[self.token] = self @classmethod def load(cls, token): return cls.cache.get(token) @classmethod def destroy(cls, token): cls.cache.pop(token) class TestStorage(BaseStorage): user = TestUserSocialAuth nonce = TestNonce association = TestAssociation code = TestCode partial = TestPartial @classmethod def is_integrity_error(cls, exception): pass social-auth-core-3.1.0/social_core/tests/test_partial.py0000644000175000017500000001062013336542652023231 0ustar omabomab00000000000000import unittest2 as unittest try: from mock import Mock, patch except ImportError: from unittest.mock import Mock, patch from ..utils import PARTIAL_TOKEN_SESSION_NAME from ..pipeline.partial import partial, partial_step class PartialDecoratorTestCase(unittest.TestCase): def setUp(self): super(PartialDecoratorTestCase, self).setUp() self.mock_current_partial_token = Mock() self.mock_current_partial = Mock(token=self.mock_current_partial_token) self.mock_strategy = Mock() self.mock_backend = Mock() self.mock_pipeline_index = Mock() self.mock_partial_store = Mock() self.mock_strategy.storage.partial.store = self.mock_partial_store self.mock_sesstion_set = Mock() self.mock_strategy.session_set = self.mock_sesstion_set def test_save_to_session(self): # GIVEN expected_response = Mock() @partial_step(save_to_session=True) def decorated_func(*args, **kwargs): return expected_response # WHEN with patch('social_core.pipeline.partial.partial_prepare', return_value=self.mock_current_partial): response = decorated_func(self.mock_strategy, self.mock_backend, self.mock_pipeline_index) # THEN self.assertEqual(expected_response, response) self.assertEqual(1, self.mock_partial_store.call_count) self.assertEqual((self.mock_current_partial,), self.mock_partial_store.call_args[0]) self.assertEqual(1, self.mock_sesstion_set.call_count) self.assertEqual((PARTIAL_TOKEN_SESSION_NAME, self.mock_current_partial_token), self.mock_sesstion_set.call_args[0]) def test_not_to_save_to_session(self): # GIVEN expected_response = Mock() @partial_step(save_to_session=False) def decorated_func(*args, **kwargs): return expected_response # WHEN with patch('social_core.pipeline.partial.partial_prepare', return_value=self.mock_current_partial): response = decorated_func(self.mock_strategy, self.mock_backend, self.mock_pipeline_index) # THEN self.assertEqual(expected_response, response) self.assertEqual(1, self.mock_partial_store.call_count) self.assertEqual((self.mock_current_partial,), self.mock_partial_store.call_args[0]) self.assertEqual(0, self.mock_sesstion_set.call_count) def test_save_to_session_by_backward_compatible_decorator(self): # GIVEN expected_response = Mock() @partial def decorated_func(*args, **kwargs): return expected_response # WHEN with patch('social_core.pipeline.partial.partial_prepare', return_value=self.mock_current_partial): response = decorated_func(self.mock_strategy, self.mock_backend, self.mock_pipeline_index) # THEN self.assertEqual(expected_response, response) self.assertEqual(1, self.mock_partial_store.call_count) self.assertEqual((self.mock_current_partial,), self.mock_partial_store.call_args[0]) self.assertEqual(1, self.mock_sesstion_set.call_count) self.assertEqual((PARTIAL_TOKEN_SESSION_NAME, self.mock_current_partial_token), self.mock_sesstion_set.call_args[0]) def test_not_to_save_to_session_when_the_response_is_a_dict(self): # GIVEN expected_response = {'test_key': 'test_value'} @partial_step(save_to_session=True) def decorated_func(*args, **kwargs): return expected_response # WHEN response = decorated_func(self.mock_strategy, self.mock_backend, self.mock_pipeline_index) # THEN self.assertEqual(expected_response, response) self.assertEqual(0, self.mock_partial_store.call_count) self.assertEqual(0, self.mock_sesstion_set.call_count) social-auth-core-3.1.0/social_core/tests/test_storage.py0000644000175000017500000001436513336542652023253 0ustar omabomab00000000000000import six import random import unittest2 as unittest from ..strategy import BaseStrategy from ..storage import UserMixin, NonceMixin, AssociationMixin, \ CodeMixin, BaseStorage from .models import User NOT_IMPLEMENTED_MSG = 'Implement in subclass' class BrokenUser(UserMixin): pass class BrokenAssociation(AssociationMixin): pass class BrokenNonce(NonceMixin): pass class BrokenCode(CodeMixin): pass class BrokenStrategy(BaseStrategy): pass class BrokenStrategyWithSettings(BrokenStrategy): def get_setting(self, name): raise AttributeError() class BrokenStorage(BaseStorage): pass class BrokenUserTests(unittest.TestCase): def setUp(self): self.user = BrokenUser def tearDown(self): self.user = None def test_get_username(self): with self.assertRaisesRegexp(NotImplementedError, NOT_IMPLEMENTED_MSG): self.user.get_username(User('foobar')) def test_user_model(self): with self.assertRaisesRegexp(NotImplementedError, NOT_IMPLEMENTED_MSG): self.user.user_model() def test_username_max_length(self): with self.assertRaisesRegexp(NotImplementedError, NOT_IMPLEMENTED_MSG): self.user.username_max_length() def test_get_user(self): with self.assertRaisesRegexp(NotImplementedError, NOT_IMPLEMENTED_MSG): self.user.get_user(1) def test_get_social_auth(self): with self.assertRaisesRegexp(NotImplementedError, NOT_IMPLEMENTED_MSG): self.user.get_social_auth('foo', 1) def test_get_social_auth_for_user(self): with self.assertRaisesRegexp(NotImplementedError, NOT_IMPLEMENTED_MSG): self.user.get_social_auth_for_user(User('foobar')) def test_create_social_auth(self): with self.assertRaisesRegexp(NotImplementedError, NOT_IMPLEMENTED_MSG): self.user.create_social_auth(User('foobar'), 1, 'foo') def test_disconnect(self): with self.assertRaisesRegexp(NotImplementedError, NOT_IMPLEMENTED_MSG): self.user.disconnect(BrokenUser()) class BrokenAssociationTests(unittest.TestCase): def setUp(self): self.association = BrokenAssociation def tearDown(self): self.association = None def test_store(self): with self.assertRaisesRegexp(NotImplementedError, NOT_IMPLEMENTED_MSG): self.association.store('http://foobar.com', BrokenAssociation()) def test_get(self): with self.assertRaisesRegexp(NotImplementedError, NOT_IMPLEMENTED_MSG): self.association.get() def test_remove(self): with self.assertRaisesRegexp(NotImplementedError, NOT_IMPLEMENTED_MSG): self.association.remove([1, 2, 3]) class BrokenNonceTests(unittest.TestCase): def setUp(self): self.nonce = BrokenNonce def tearDown(self): self.nonce = None def test_use(self): with self.assertRaisesRegexp(NotImplementedError, NOT_IMPLEMENTED_MSG): self.nonce.use('http://foobar.com', 1364951922, 'foobar123') class BrokenCodeTest(unittest.TestCase): def setUp(self): self.code = BrokenCode def tearDown(self): self.code = None def test_get_code(self): with self.assertRaisesRegexp(NotImplementedError, NOT_IMPLEMENTED_MSG): self.code.get_code('foobar') class BrokenStrategyTests(unittest.TestCase): def setUp(self): self.strategy = BrokenStrategy(storage=BrokenStorage) def tearDown(self): self.strategy = None def test_redirect(self): with self.assertRaisesRegexp(NotImplementedError, NOT_IMPLEMENTED_MSG): self.strategy.redirect('http://foobar.com') def test_get_setting(self): with self.assertRaisesRegexp(NotImplementedError, NOT_IMPLEMENTED_MSG): self.strategy.get_setting('foobar') def test_html(self): with self.assertRaisesRegexp(NotImplementedError, NOT_IMPLEMENTED_MSG): self.strategy.html('

foobar

') def test_request_data(self): with self.assertRaisesRegexp(NotImplementedError, NOT_IMPLEMENTED_MSG): self.strategy.request_data() def test_request_host(self): with self.assertRaisesRegexp(NotImplementedError, NOT_IMPLEMENTED_MSG): self.strategy.request_host() def test_session_get(self): with self.assertRaisesRegexp(NotImplementedError, NOT_IMPLEMENTED_MSG): self.strategy.session_get('foobar') def test_session_set(self): with self.assertRaisesRegexp(NotImplementedError, NOT_IMPLEMENTED_MSG): self.strategy.session_set('foobar', 123) def test_session_pop(self): with self.assertRaisesRegexp(NotImplementedError, NOT_IMPLEMENTED_MSG): self.strategy.session_pop('foobar') def test_build_absolute_uri(self): with self.assertRaisesRegexp(NotImplementedError, NOT_IMPLEMENTED_MSG): self.strategy.build_absolute_uri('/foobar') def test_render_html_with_tpl(self): with self.assertRaisesRegexp(NotImplementedError, NOT_IMPLEMENTED_MSG): self.strategy.render_html('foobar.html', context={}) def test_render_html_with_html(self): with self.assertRaisesRegexp(NotImplementedError, NOT_IMPLEMENTED_MSG): self.strategy.render_html(html='

foobar

', context={}) def test_render_html_with_none(self): with self.assertRaisesRegexp(ValueError, 'Missing template or html parameters'): self.strategy.render_html() def test_is_integrity_error(self): with self.assertRaisesRegexp(NotImplementedError, NOT_IMPLEMENTED_MSG): self.strategy.storage.is_integrity_error(None) def test_random_string(self): self.assertTrue(isinstance(self.strategy.random_string(), six.string_types)) def test_random_string_without_systemrandom(self): def SystemRandom(): raise NotImplementedError() orig_random = getattr(random, 'SystemRandom', None) random.SystemRandom = SystemRandom strategy = BrokenStrategyWithSettings(storage=BrokenStorage) self.assertTrue(isinstance(strategy.random_string(), six.string_types)) random.SystemRandom = orig_random social-auth-core-3.1.0/social_core/tests/requirements-python3.txt0000644000175000017500000000004313336542652025050 0ustar omabomab00000000000000-r requirements-base.txt unittest2 social-auth-core-3.1.0/social_core/tests/strategy.py0000644000175000017500000000733113336542652022405 0ustar omabomab00000000000000from ..strategy import BaseStrategy, BaseTemplateStrategy TEST_URI = 'http://myapp.com' TEST_HOST = 'myapp.com' class Redirect(object): def __init__(self, url): self.url = url class TestTemplateStrategy(BaseTemplateStrategy): def render_template(self, tpl, context): return tpl def render_string(self, html, context): return html class TestStrategy(BaseStrategy): DEFAULT_TEMPLATE_STRATEGY = TestTemplateStrategy def __init__(self, storage, tpl=None): self._request_data = {} self._settings = {} self._session = {} super(TestStrategy, self).__init__(storage, tpl) def redirect(self, url): return Redirect(url) def get_setting(self, name): """Return value for given setting name""" return self._settings[name] def html(self, content): """Return HTTP response with given content""" return content def render_html(self, tpl=None, html=None, context=None): """Render given template or raw html with given context""" return tpl or html def request_data(self, merge=True): """Return current request data (POST or GET)""" return self._request_data def request_host(self): """Return current host value""" return TEST_HOST def request_is_secure(self): """ Is the request using HTTPS? """ return False def request_path(self): """ path of the current request """ return '' def request_port(self): """ Port in use for this request """ return 80 def request_get(self): """ Request GET data """ return self._request_data.copy() def request_post(self): """ Request POST data """ return self._request_data.copy() def session_get(self, name, default=None): """Return session value for given key""" return self._session.get(name, default) def session_set(self, name, value): """Set session value for given key""" self._session[name] = value def session_pop(self, name): """Pop session value for given key""" return self._session.pop(name, None) def build_absolute_uri(self, path=None): """Build absolute URI with given (optional) path""" path = path or '' if path.startswith('http://') or path.startswith('https://'): return path return TEST_URI + path def set_settings(self, values): self._settings.update(values) def set_request_data(self, values, backend): self._request_data.update(values) backend.data = self._request_data def remove_from_request_data(self, name): self._request_data.pop(name, None) def authenticate(self, *args, **kwargs): user = super(TestStrategy, self).authenticate(*args, **kwargs) if isinstance(user, self.storage.user.user_model()): self.session_set('username', user.username) return user def get_pipeline(self, backend=None): return self.setting( 'PIPELINE', ( 'social_core.pipeline.social_auth.social_details', 'social_core.pipeline.social_auth.social_uid', 'social_core.pipeline.social_auth.auth_allowed', 'social_core.pipeline.social_auth.social_user', 'social_core.pipeline.user.get_username', 'social_core.pipeline.social_auth.associate_by_email', 'social_core.pipeline.user.create_user', 'social_core.pipeline.social_auth.associate_user', 'social_core.pipeline.social_auth.load_extra_data', 'social_core.pipeline.user.user_details' ), backend ) social-auth-core-3.1.0/social_core/tests/backends/0000755000175000017500000000000013433256445021737 5ustar omabomab00000000000000social-auth-core-3.1.0/social_core/tests/backends/test_azuread_b2c.py0000644000175000017500000001614713432621010025521 0ustar omabomab00000000000000""" Copyright (c) 2017 Noderabbit Inc., d.b.a. Appsembler All rights reserved. MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ import json import jwt from time import time from httpretty import HTTPretty from jwt.algorithms import RSAAlgorithm from .oauth import OAuth2Test # Dummy private and private keys: RSA_PUBLIC_JWT_KEY = { # https://github.com/jpadilla/pyjwt/blob/06f461a/tests/keys/jwk_rsa_pub.json 'kty': 'RSA', 'kid': 'bilbo.baggins@hobbiton.example', 'use': 'sig', 'n': 'n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw', 'e': 'AQAB' } RSA_PRIVATE_JWT_KEY = { # https://github.com/jpadilla/pyjwt/blob/06f461a/tests/keys/jwk_rsa_key.json 'kty': 'RSA', 'kid': 'bilbo.baggins@hobbiton.example', 'use': 'sig', 'n': 'n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw', 'e': 'AQAB', 'd': 'bWUC9B-EFRIo8kpGfh0ZuyGPvMNKvYWNtB_ikiH9k20eT-O1q_I78eiZkpXxXQ0UTEs2LsNRS-8uJbvQ-A1irkwMSMkK1J3XTGgdrhCku9gRldY7sNA_AKZGh-Q661_42rINLRCe8W-nZ34ui_qOfkLnK9QWDDqpaIsA-bMwWWSDFu2MUBYwkHTMEzLYGqOe04noqeq1hExBTHBOBdkMXiuFhUq1BU6l-DqEiWxqg82sXt2h-LMnT3046AOYJoRioz75tSUQfGCshWTBnP5uDjd18kKhyv07lhfSJdrPdM5Plyl21hsFf4L_mHCuoFau7gdsPfHPxxjVOcOpBrQzwQ', 'p': '3Slxg_DwTXJcb6095RoXygQCAZ5RnAvZlno1yhHtnUex_fp7AZ_9nRaO7HX_-SFfGQeutao2TDjDAWU4Vupk8rw9JR0AzZ0N2fvuIAmr_WCsmGpeNqQnev1T7IyEsnh8UMt-n5CafhkikzhEsrmndH6LxOrvRJlsPp6Zv8bUq0k', 'q': 'uKE2dh-cTf6ERF4k4e_jy78GfPYUIaUyoSSJuBzp3Cubk3OCqs6grT8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7anV5UzhM1iZ7z4yMkuUwFWoBvyY898EXvRD-hdqRxHlSqAZ192zB3pVFJ0s7pFc', 'dp': 'B8PVvXkvJrj2L-GYQ7v3y9r6Kw5g9SahXBwsWUzp19TVlgI-YV85q1NIb1rxQtD-IsXXR3-TanevuRPRt5OBOdiMGQp8pbt26gljYfKU_E9xn-RULHz0-ed9E9gXLKD4VGngpz-PfQ_q29pk5xWHoJp009Qf1HvChixRX59ehik', 'dq': 'CLDmDGduhylc9o7r84rEUVn7pzQ6PF83Y-iBZx5NT-TpnOZKF1pErAMVeKzFEl41DlHHqqBLSM0W1sOFbwTxYWZDm6sI6og5iTbwQGIC3gnJKbi_7k_vJgGHwHxgPaX2PnvP-zyEkDERuf-ry4c_Z11Cq9AqC2yeL6kdKT1cYF8', 'qi': '3PiqvXQN0zwMeE-sBvZgi289XP9XCQF3VWqPzMKnIgQp7_Tugo6-NZBKCQsMf3HaEGBjTVJs_jcK8-TRXvaKe-7ZMaQj8VfBdYkssbu0NKDDhjJ-GtiseaDVWt7dcH0cfwxgFUHpQh7FoCrjFJ6h6ZEpMF6xmujs4qMpPz8aaI4' } class AzureADOAuth2Test(OAuth2Test): AUTH_KEY = 'abcdef12-1234-9876-0000-abcdef098765' EXPIRES_IN = 3600 AUTH_TIME = int(time()) EXPIRES_ON = AUTH_TIME + EXPIRES_IN backend_path = 'social_core.backends.azuread_b2c.AzureADB2COAuth2' expected_username = 'FooBar' refresh_token_body = json.dumps({ 'access_token': 'foobar-new-token', 'token_type': 'bearer', 'expires_in': EXPIRES_IN, 'refresh_token': 'foobar-new-refresh-token', 'scope': 'identity' }) access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer', 'id_token': jwt.encode( key=RSAAlgorithm.from_jwk(json.dumps(RSA_PRIVATE_JWT_KEY)), headers={ 'kid': RSA_PRIVATE_JWT_KEY['kid'], }, algorithm='RS256', payload={ 'aud': AUTH_KEY, 'auth_time': AUTH_TIME, 'country': 'Axphain', 'emails': [ 'foobar@example.com' ], 'exp': EXPIRES_ON, 'family_name': 'Bar', 'given_name': 'Foo', 'iat': AUTH_TIME, 'iss': 'https://login.microsoftonline.com/9a9a9a9a-1111-5555-0000-bc24adfdae00/v2.0/', 'name': 'FooBar', 'nbf': AUTH_TIME, 'oid': '11223344-5566-7788-9999-aabbccddeeff', 'postalCode': '00000', 'sub': '11223344-5566-7788-9999-aabbccddeeff', 'tfp': 'B2C_1_SignIn', 'ver': '1.0', }).decode('ascii'), 'expires_in': EXPIRES_IN, 'expires_on': EXPIRES_ON, 'not_before': AUTH_TIME, }) def extra_settings(self): settings = super(AzureADOAuth2Test, self).extra_settings() settings.update({ 'SOCIAL_AUTH_' + self.name + '_POLICY': 'b2c_1_signin', 'SOCIAL_AUTH_' + self.name + '_KEY': self.AUTH_KEY, 'SOCIAL_AUTH_' + self.name + '_TENANT_ID': 'footenant.onmicrosoft.com', }) return settings def setUp(self): super(AzureADOAuth2Test, self).setUp() keys_url = 'https://login.microsoftonline.com/footenant.onmicrosoft.com/discovery/v2.0/keys?p=b2c_1_signin' keys_body = json.dumps({ 'keys': [{ # Dummy public key that pairs with `access_token_body` key: # https://github.com/jpadilla/pyjwt/blob/06f461a/tests/keys/jwk_rsa_pub.json 'kty': 'RSA', 'kid': 'bilbo.baggins@hobbiton.example', 'use': 'sig', 'n': 'n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-X' 'V2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_Ns' 'YOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHY' 'pPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCu' 'EHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_' 'mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw', 'e': 'AQAB', }], }) HTTPretty.register_uri(HTTPretty.GET, keys_url, status=200, body=keys_body) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() def test_refresh_token(self): user, social = self.do_refresh_token() self.assertEqual(social.extra_data['access_token'], 'foobar-new-token') social-auth-core-3.1.0/social_core/tests/backends/test_thisismyjam.py0000644000175000017500000000153413336542652025714 0ustar omabomab00000000000000import json from six.moves.urllib_parse import urlencode from .oauth import OAuth1Test class ThisIsMyJameOAuth1Test(OAuth1Test): backend_path = 'social_core.backends.thisismyjam.ThisIsMyJamOAuth1' user_data_url = 'http://api.thisismyjam.com/1/verify.json' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) request_token_body = urlencode({ 'oauth_token_secret': 'foobar-secret', 'oauth_token': 'foobar', 'oauth_callback_confirmed': 'true' }) user_data_body = json.dumps({ 'id': 10101010, 'person': { 'name': 'foobar', 'fullname': 'Foo Bar' } }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_steam.py0000644000175000017500000001243613336542652024467 0ustar omabomab00000000000000import json import datetime from httpretty import HTTPretty from six.moves.urllib_parse import urlencode from ...exceptions import AuthFailed from .open_id import OpenIdTest INFO_URL = 'http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?' JANRAIN_NONCE = datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%SZ') class SteamOpenIdTest(OpenIdTest): backend_path = 'social_core.backends.steam.SteamOpenId' expected_username = 'foobar' discovery_body = ''.join([ '', '', '', '', 'http://specs.openid.net/auth/2.0/server', 'https://steamcommunity.com/openid/login', '', '', '' ]) user_discovery_body = ''.join([ '', '', '', '', 'http://specs.openid.net/auth/2.0/signon ', 'https://steamcommunity.com/openid/login', '', '', '' ]) server_response = urlencode({ 'janrain_nonce': JANRAIN_NONCE, 'openid.ns': 'http://specs.openid.net/auth/2.0', 'openid.mode': 'id_res', 'openid.op_endpoint': 'https://steamcommunity.com/openid/login', 'openid.claimed_id': 'https://steamcommunity.com/openid/id/123', 'openid.identity': 'https://steamcommunity.com/openid/id/123', 'openid.return_to': 'http://myapp.com/complete/steam/?' 'janrain_nonce=' + JANRAIN_NONCE, 'openid.response_nonce': JANRAIN_NONCE + 'oD4UZ3w9chOAiQXk0AqDipqFYRA=', 'openid.assoc_handle': '1234567890', 'openid.signed': 'signed,op_endpoint,claimed_id,identity,return_to,' 'response_nonce,assoc_handle', 'openid.sig': '1az53vj9SVdiBwhk8%2BFQ68R2plo=', }) player_details = json.dumps({ 'response': { 'players': [{ 'steamid': '123', 'primaryclanid': '1234', 'timecreated': 1360768416, 'personaname': 'foobar', 'personastate': 0, 'communityvisibilitystate': 3, 'profileurl': 'http://steamcommunity.com/profiles/123/', 'avatar': 'http://media.steampowered.com/steamcommunity/' 'public/images/avatars/fe/fef49e7fa7e1997310d7' '05b2a6158ff8dc1cdfeb.jpg', 'avatarfull': 'http://media.steampowered.com/steamcommunity/' 'public/images/avatars/fe/fef49e7fa7e1997310d7' '05b2a6158ff8dc1cdfeb_full.jpg', 'avatarmedium': 'http://media.steampowered.com/steamcommunity/' 'public/images/avatars/fe/fef49e7fa7e1997310d7' '05b2a6158ff8dc1cdfeb_medium.jpg', 'lastlogoff': 1360790014 }] } }) def _login_setup(self, user_url=None): self.strategy.set_settings({ 'SOCIAL_AUTH_STEAM_API_KEY': '123abc' }) HTTPretty.register_uri(HTTPretty.POST, 'https://steamcommunity.com/openid/login', status=200, body=self.server_response) HTTPretty.register_uri( HTTPretty.GET, user_url or 'https://steamcommunity.com/openid/id/123', status=200, body=self.user_discovery_body ) HTTPretty.register_uri(HTTPretty.GET, INFO_URL, status=200, body=self.player_details) def test_login(self): self._login_setup() self.do_login() def test_partial_pipeline(self): self._login_setup() self.do_partial_pipeline() class SteamOpenIdMissingSteamIdTest(SteamOpenIdTest): server_response = urlencode({ 'janrain_nonce': JANRAIN_NONCE, 'openid.ns': 'http://specs.openid.net/auth/2.0', 'openid.mode': 'id_res', 'openid.op_endpoint': 'https://steamcommunity.com/openid/login', 'openid.claimed_id': 'https://steamcommunity.com/openid/BROKEN', 'openid.identity': 'https://steamcommunity.com/openid/BROKEN', 'openid.return_to': 'http://myapp.com/complete/steam/?' 'janrain_nonce=' + JANRAIN_NONCE, 'openid.response_nonce': JANRAIN_NONCE + 'oD4UZ3w9chOAiQXk0AqDipqFYRA=', 'openid.assoc_handle': '1234567890', 'openid.signed': 'signed,op_endpoint,claimed_id,identity,return_to,' 'response_nonce,assoc_handle', 'openid.sig': '1az53vj9SVdiBwhk8%2BFQ68R2plo=', }) def test_login(self): self._login_setup(user_url='https://steamcommunity.com/openid/BROKEN') with self.assertRaises(AuthFailed): self.do_login() def test_partial_pipeline(self): self._login_setup(user_url='https://steamcommunity.com/openid/BROKEN') with self.assertRaises(AuthFailed): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_xing.py0000644000175000017500000001420513336542652024317 0ustar omabomab00000000000000import json from six.moves.urllib_parse import urlencode from .oauth import OAuth1Test class XingOAuth1Test(OAuth1Test): backend_path = 'social_core.backends.xing.XingOAuth' user_data_url = 'https://api.xing.com/v1/users/me.json' expected_username = 'FooBar' access_token_body = urlencode({ 'access_token': 'foobar', 'token_type': 'bearer', 'user_id': '123456_abcdef', 'oauth_token_secret': 'foobar-secret', 'oauth_token': 'foobar', }) request_token_body = urlencode({ 'oauth_token_secret': 'foobar-secret', 'oauth_token': 'foobar', 'oauth_callback_confirmed': 'true' }) user_data_body = json.dumps({ 'users': [{ 'id': '123456_abcdef', 'first_name': 'Foo', 'last_name': 'Bar', 'display_name': 'Foo Bar', 'page_name': 'Foo_Bar', 'permalink': 'https://www.xing.com/profile/Foo_Bar', 'gender': 'm', 'birth_date': { 'day': 12, 'month': 8, 'year': 1963 }, 'active_email': 'foo@bar.com', 'time_zone': { 'name': 'Europe/Copenhagen', 'utc_offset': 2.0 }, 'premium_services': ['SEARCH', 'PRIVATEMESSAGES'], 'badges': ['PREMIUM', 'MODERATOR'], 'wants': 'Nothing', 'haves': 'Skills', 'interests': 'Foo Foo', 'organisation_member': 'ACM, GI', 'languages': { 'de': 'NATIVE', 'en': 'FLUENT', 'fr': None, 'zh': 'BASIC' }, 'private_address': { 'city': 'Foo', 'country': 'DE', 'zip_code': '20357', 'street': 'Bar', 'phone': '12|34|1234560', 'fax': '||', 'province': 'Foo', 'email': 'foo@bar.com', 'mobile_phone': '12|3456|1234567' }, 'business_address': { 'city': 'Foo', 'country': 'DE', 'zip_code': '20357', 'street': 'Bar', 'phone': '12|34|1234569', 'fax': '12|34|1234561', 'province': 'Foo', 'email': 'foo@bar.com', 'mobile_phone': '12|345|12345678' }, 'web_profiles': { 'qype': ['http://qype.de/users/foo'], 'google_plus': ['http://plus.google.com/foo'], 'blog': ['http://blog.example.org'], 'homepage': ['http://example.org', 'http://other-example.org'] }, 'instant_messaging_accounts': { 'skype': 'foobar', 'googletalk': 'foobar' }, 'professional_experience': { 'primary_company': { 'name': 'XING AG', 'title': 'Softwareentwickler', 'company_size': '201-500', 'tag': None, 'url': 'http://www.xing.com', 'career_level': 'PROFESSIONAL_EXPERIENCED', 'begin_date': '2010-01', 'description': None, 'end_date': None, 'industry': 'AEROSPACE' }, 'non_primary_companies': [{ 'name': 'Ninja Ltd.', 'title': 'DevOps', 'company_size': None, 'tag': 'NINJA', 'url': 'http://www.ninja-ltd.co.uk', 'career_level': None, 'begin_date': '2009-04', 'description': None, 'end_date': '2010-07', 'industry': 'ALTERNATIVE_MEDICINE' }, { 'name': None, 'title': 'Wiss. Mitarbeiter', 'company_size': None, 'tag': 'OFFIS', 'url': 'http://www.uni.de', 'career_level': None, 'begin_date': '2007', 'description': None, 'end_date': '2008', 'industry': 'APPAREL_AND_FASHION' }, { 'name': None, 'title': 'TEST NINJA', 'company_size': '201-500', 'tag': 'TESTCOMPANY', 'url': None, 'career_level': 'ENTRY_LEVEL', 'begin_date': '1998-12', 'description': None, 'end_date': '1999-05', 'industry': 'ARTS_AND_CRAFTS' }], 'awards': [{ 'name': 'Awesome Dude Of The Year', 'date_awarded': 2007, 'url': None }] }, 'educational_background': { 'schools': [{ 'name': 'Foo University', 'degree': 'MSc CE/CS', 'notes': None, 'subject': None, 'begin_date': '1998-08', 'end_date': '2005-02' }], 'qualifications': ['TOEFLS', 'PADI AOWD'] }, 'photo_urls': { 'large': 'http://www.xing.com/img/users/e/3/d/' 'f94ef165a.123456,1.140x185.jpg', 'mini_thumb': 'http://www.xing.com/img/users/e/3/d/' 'f94ef165a.123456,1.18x24.jpg', 'thumb': 'http://www.xing.com/img/users/e/3/d/' 'f94ef165a.123456,1.30x40.jpg', 'medium_thumb': 'http://www.xing.com/img/users/e/3/d/' 'f94ef165a.123456,1.57x75.jpg', 'maxi_thumb': 'http://www.xing.com/img/users/e/3/d/' 'f94ef165a.123456,1.70x93.jpg' } }] }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_upwork.py0000644000175000017500000000277013336542652024705 0ustar omabomab00000000000000import json from six.moves.urllib_parse import urlencode from .oauth import OAuth1Test class UpworkOAuth1Test(OAuth1Test): backend_path = 'social_core.backends.upwork.UpworkOAuth' user_data_url = 'https://www.upwork.com/api/auth/v1/info.json' expected_username = '10101010' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) request_token_body = urlencode({ 'oauth_token_secret': 'foobar-secret', 'oauth_token': 'foobar', 'oauth_callback_confirmed': 'true' }) user_data_body = json.dumps({ 'info': { 'portrait_32_img': '', 'capacity': { 'buyer': 'no', 'affiliate_manager': 'no', 'provider': 'yes' }, 'company_url': '', 'has_agency': '1', 'portrait_50_img': '', 'portrait_100_img': '', 'location': { 'city': 'New York', 'state': '', 'country': 'USA' }, 'ref': '9755314', 'profile_url': 'https://www.upwork.com/users/~10101010' }, 'auth_user': { 'timezone': 'USA/New York', 'first_name': 'Foo', 'last_name': 'Bar', 'timezone_offset': '10000' }, 'server_time': '1111111111' }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/open_id.py0000644000175000017500000000641313336542652023732 0ustar omabomab00000000000000# -*- coding: utf-8 -*- import sys import requests from six.moves.html_parser import HTMLParser from openid import oidutil from httpretty import HTTPretty sys.path.insert(0, '..') from .base import BaseBackendTest from ..strategy import TestStrategy from ..models import TestStorage, User, TestUserSocialAuth, \ TestNonce, TestAssociation from ...utils import parse_qs, module_member from ...backends.utils import load_backends # Patch to remove the too-verbose output until a new version is released oidutil.log = lambda *args, **kwargs: None class FormHTMLParser(HTMLParser): form = {} inputs = {} def handle_starttag(self, tag, attrs): attrs = dict(attrs) if tag == 'form': self.form.update(attrs) elif tag == 'input' and 'name' in attrs: self.inputs[attrs['name']] = attrs['value'] class OpenIdTest(BaseBackendTest): backend_path = None backend = None access_token_body = None user_data_body = None user_data_url = '' expected_username = '' settings = None partial_login_settings = None raw_complete_url = '/complete/{0}/' def setUp(self): HTTPretty.enable() Backend = module_member(self.backend_path) self.strategy = TestStrategy(TestStorage) self.complete_url = self.raw_complete_url.format(Backend.name) self.backend = Backend(self.strategy, redirect_uri=self.complete_url) self.strategy.set_settings({ 'SOCIAL_AUTH_AUTHENTICATION_BACKENDS': ( self.backend_path, 'social_core.tests.backends.test_broken.BrokenBackendAuth' ) }) # Force backends loading to trash PSA cache load_backends( self.strategy.get_setting('SOCIAL_AUTH_AUTHENTICATION_BACKENDS'), force_load=True ) def tearDown(self): self.strategy = None User.reset_cache() TestUserSocialAuth.reset_cache() TestNonce.reset_cache() TestAssociation.reset_cache() HTTPretty.disable() def get_form_data(self, html): parser = FormHTMLParser() parser.feed(html) return parser.form, parser.inputs def openid_url(self): return self.backend.openid_url() def post_start(self): pass def do_start(self): HTTPretty.register_uri(HTTPretty.GET, self.openid_url(), status=200, body=self.discovery_body, content_type='application/xrds+xml') start = self.backend.start() self.post_start() form, inputs = self.get_form_data(start) HTTPretty.register_uri(HTTPretty.POST, form.get('action'), status=200, body=self.server_response) response = requests.post(form.get('action'), data=inputs) self.strategy.set_request_data(parse_qs(response.content), self.backend) HTTPretty.register_uri(HTTPretty.POST, form.get('action'), status=200, body='is_valid:true\n') return self.backend.complete() social-auth-core-3.1.0/social_core/tests/backends/test_mixcloud.py0000644000175000017500000000456213336542652025203 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class MixcloudOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.mixcloud.MixcloudOAuth2' user_data_url = 'https://api.mixcloud.com/me/' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps({ 'username': 'foobar', 'cloudcast_count': 0, 'following_count': 0, 'url': 'http://www.mixcloud.com/foobar/', 'pictures': { 'medium': 'http://images-mix.netdna-ssl.com/w/100/h/100/q/85/' 'images/graphics/33_Profile/default_user_600x600-v4.png', '320wx320h': 'http://images-mix.netdna-ssl.com/w/320/h/320/q/85/' 'images/graphics/33_Profile/' 'default_user_600x600-v4.png', 'extra_large': 'http://images-mix.netdna-ssl.com/w/600/h/600/q/85/' 'images/graphics/33_Profile/' 'default_user_600x600-v4.png', 'large': 'http://images-mix.netdna-ssl.com/w/300/h/300/q/85/' 'images/graphics/33_Profile/default_user_600x600-v4.png', '640wx640h': 'http://images-mix.netdna-ssl.com/w/640/h/640/q/85/' 'images/graphics/33_Profile/' 'default_user_600x600-v4.png', 'medium_mobile': 'http://images-mix.netdna-ssl.com/w/80/h/80/q/75/' 'images/graphics/33_Profile/' 'default_user_600x600-v4.png', 'small': 'http://images-mix.netdna-ssl.com/w/25/h/25/q/85/images/' 'graphics/33_Profile/default_user_600x600-v4.png', 'thumbnail': 'http://images-mix.netdna-ssl.com/w/50/h/50/q/85/' 'images/graphics/33_Profile/' 'default_user_600x600-v4.png' }, 'is_current_user': True, 'listen_count': 0, 'updated_time': '2013-03-17T06:26:31Z', 'following': False, 'follower': False, 'key': '/foobar/', 'created_time': '2013-03-17T06:26:31Z', 'follower_count': 0, 'favorite_count': 0, 'name': 'foobar' }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_disqus.py0000644000175000017500000000414613336542652024665 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class DisqusOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.disqus.DisqusOAuth2' user_data_url = 'https://disqus.com/api/3.0/users/details.json' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps({ 'code': 0, 'response': { 'username': 'foobar', 'numFollowers': 0, 'isFollowing': False, 'numFollowing': 0, 'name': 'Foo Bar', 'numPosts': 0, 'url': '', 'isAnonymous': False, 'rep': 1.231755, 'about': '', 'isFollowedBy': False, 'connections': {}, 'emailHash': '5280f14cedf530b544aecc31fcfe0240', 'reputation': 1.231755, 'avatar': { 'small': { 'permalink': 'https://disqus.com/api/users/avatars/' 'foobar.jpg', 'cache': 'https://securecdn.disqus.com/uploads/' 'users/453/4556/avatar32.jpg?1285535379' }, 'isCustom': False, 'permalink': 'https://disqus.com/api/users/avatars/foobar.jpg', 'cache': 'https://securecdn.disqus.com/uploads/users/453/' '4556/avatar92.jpg?1285535379', 'large': { 'permalink': 'https://disqus.com/api/users/avatars/' 'foobar.jpg', 'cache': 'https://securecdn.disqus.com/uploads/users/' '453/4556/avatar92.jpg?1285535379' } }, 'profileUrl': 'http://disqus.com/foobar/', 'numLikesReceived': 0, 'isPrimary': True, 'joinedAt': '2010-09-26T21:09:39', 'id': '1010101', 'location': '' } }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_eventbrite.py0000644000175000017500000000204613336542652025521 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class EventbriteAuth2Test(OAuth2Test): backend_path = 'social_core.backends.eventbrite.EventbriteOAuth2' user_data_url = 'https://www.eventbriteapi.com/v3/users/me' expected_username = 'sean+awesome@eventbrite.com' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps({ 'first_name': 'sean', 'last_name': 'rose', 'name': 'sean rose', 'access_token': 'YQEN5H2W2OTKQZWZMYER', 'emails': [ { 'verified': True, 'email': 'sean+awesome2@eventbrite.com', 'primary': False, }, { 'verified': True, 'email': 'sean+awesome@eventbrite.com', 'primary': True, }, ], 'token_type': 'bearer', 'image_id': None, 'is_public': False, 'id': '128559602587', }) def test_login(self): self.do_login() social-auth-core-3.1.0/social_core/tests/backends/test_azuread.py0000644000175000017500000000613413336542652025007 0ustar omabomab00000000000000""" Copyright (c) 2015 Microsoft Open Technologies, Inc. All rights reserved. MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ import json from .oauth import OAuth2Test class AzureADOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.azuread.AzureADOAuth2' user_data_url = 'https://graph.windows.net/me' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer', 'id_token': 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL' '3N0cy53aW5kb3dzLm5ldC83Mjc0MDZhYy03MDY4LTQ4ZmEtOTJiOS1jMmQ' '2NzIxMWJjNTAvIiwiaWF0IjpudWxsLCJleHAiOm51bGwsImF1ZCI6IjAyO' 'WNjMDEwLWJiNzQtNGQyYi1hMDQwLWY5Y2VkM2ZkMmM3NiIsInN1YiI6In' 'FVOHhrczltSHFuVjZRMzR6aDdTQVpvY2loOUV6cnJJOW1wVlhPSWJWQTg' 'iLCJ2ZXIiOiIxLjAiLCJ0aWQiOiI3Mjc0MDZhYy03MDY4LTQ4ZmEtOTJi' 'OS1jMmQ2NzIxMWJjNTAiLCJvaWQiOiI3ZjhlMTk2OS04YjgxLTQzOGMtO' 'GQ0ZS1hZDZmNTYyYjI4YmIiLCJ1cG4iOiJmb29iYXJAdGVzdC5vbm1pY3' 'Jvc29mdC5jb20iLCJnaXZlbl9uYW1lIjoiZm9vIiwiZmFtaWx5X25hbWU' 'iOiJiYXIiLCJuYW1lIjoiZm9vIGJhciIsInVuaXF1ZV9uYW1lIjoiZm9v' 'YmFyQHRlc3Qub25taWNyb3NvZnQuY29tIiwicHdkX2V4cCI6IjQ3MzMwO' 'TY4IiwicHdkX3VybCI6Imh0dHBzOi8vcG9ydGFsLm1pY3Jvc29mdG9ubG' 'luZS5jb20vQ2hhbmdlUGFzc3dvcmQuYXNweCJ9.3V50dHXTZOHj9UWtkn' '2g7BjX5JxNe8skYlK4PdhiLz4', 'expires_in': 3600, 'expires_on': 1423650396, 'not_before': 1423646496 }) refresh_token_body = json.dumps({ 'access_token': 'foobar-new-token', 'token_type': 'bearer', 'expires_in': 3600, 'refresh_token': 'foobar-new-refresh-token', 'scope': 'identity' }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() def test_refresh_token(self): user, social = self.do_refresh_token() self.assertEqual(social.extra_data['access_token'], 'foobar-new-token') social-auth-core-3.1.0/social_core/tests/backends/test_itembase.py0000644000175000017500000000252413336542652025144 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class ItembaseOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.itembase.ItembaseOAuth2' user_data_url = 'https://users.itembase.com/v1/me' expected_username = 'foobar' access_token_body = json.dumps({ "access_token": "foobar-token", "expires_in": 2592000, "token_type": "bearer", "scope": "user.minimal", "refresh_token": "foobar-refresh-token" }) user_data_body = json.dumps({ "uuid": "a4b91ee7-ec1a-49b9-afce-371dc8797749", "username": "foobar", "email": "foobar@itembase.biz", "first_name": "Foo", "middle_name": None, "last_name": "Bar", "name_format": "first middle last", "locale": "en", "preferred_currency": "EUR" }) refresh_token_body = json.dumps({ "access_token": "foobar-new-token", "expires_in": 2592000, "token_type": "bearer", "scope": "user.minimal", "refresh_token": "foobar-new-refresh-token" }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() class ItembaseOAuth2SandboxTest(OAuth2Test): backend_path = 'social_core.backends.itembase.ItembaseOAuth2Sandbox' user_data_url = 'http://sandbox.users.itembase.io/v1/me' social-auth-core-3.1.0/social_core/tests/backends/test_edmodo.py0000644000175000017500000000245313336542652024623 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class EdmodoOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.edmodo.EdmodoOAuth2' user_data_url = 'https://api.edmodo.com/users/me' expected_username = 'foobar12345' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps({ 'username': 'foobar12345', 'coppa_verified': False, 'first_name': 'Foo', 'last_name': 'Bar', 'premium': False, 'verified_institution_member': False, 'url': 'https://api.edmodo.com/users/12345', 'type': 'teacher', 'time_zone': None, 'end_level': None, 'start_level': None, 'locale': 'en', 'subjects': None, 'utc_offset': None, 'email': 'foo.bar@example.com', 'gender': None, 'about': None, 'user_title': None, 'id': 12345, 'avatars': { 'small': 'https://api.edmodo.com/users/12345/avatar?type=small&u=5a15xug93m53mi4ey3ck4fvkq', 'large': 'https://api.edmodo.com/users/12345/avatar?type=large&u=5a15xug93m53mi4ey3ck4fvkq' } }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_github.py0000644000175000017500000001445213336542652024640 0ustar omabomab00000000000000import json from httpretty import HTTPretty from ...exceptions import AuthFailed from .oauth import OAuth2Test class GithubOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.github.GithubOAuth2' user_data_url = 'https://api.github.com/user' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps({ 'login': 'foobar', 'id': 1, 'avatar_url': 'https://github.com/images/error/foobar_happy.gif', 'gravatar_id': 'somehexcode', 'url': 'https://api.github.com/users/foobar', 'name': 'monalisa foobar', 'company': 'GitHub', 'blog': 'https://github.com/blog', 'location': 'San Francisco', 'email': 'foo@bar.com', 'hireable': False, 'bio': 'There once was...', 'public_repos': 2, 'public_gists': 1, 'followers': 20, 'following': 0, 'html_url': 'https://github.com/foobar', 'created_at': '2008-01-14T04:33:35Z', 'type': 'User', 'total_private_repos': 100, 'owned_private_repos': 100, 'private_gists': 81, 'disk_usage': 10000, 'collaborators': 8, 'plan': { 'name': 'Medium', 'space': 400, 'collaborators': 10, 'private_repos': 20 } }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() class GithubOAuth2NoEmailTest(GithubOAuth2Test): user_data_body = json.dumps({ 'login': 'foobar', 'id': 1, 'avatar_url': 'https://github.com/images/error/foobar_happy.gif', 'gravatar_id': 'somehexcode', 'url': 'https://api.github.com/users/foobar', 'name': 'monalisa foobar', 'company': 'GitHub', 'blog': 'https://github.com/blog', 'location': 'San Francisco', 'email': '', 'hireable': False, 'bio': 'There once was...', 'public_repos': 2, 'public_gists': 1, 'followers': 20, 'following': 0, 'html_url': 'https://github.com/foobar', 'created_at': '2008-01-14T04:33:35Z', 'type': 'User', 'total_private_repos': 100, 'owned_private_repos': 100, 'private_gists': 81, 'disk_usage': 10000, 'collaborators': 8, 'plan': { 'name': 'Medium', 'space': 400, 'collaborators': 10, 'private_repos': 20 } }) def test_login(self): url = 'https://api.github.com/user/emails' HTTPretty.register_uri(HTTPretty.GET, url, status=200, body=json.dumps(['foo@bar.com']), content_type='application/json') self.do_login() def test_login_next_format(self): url = 'https://api.github.com/user/emails' HTTPretty.register_uri(HTTPretty.GET, url, status=200, body=json.dumps([{'email': 'foo@bar.com'}]), content_type='application/json') self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() class GithubOrganizationOAuth2Test(GithubOAuth2Test): backend_path = 'social_core.backends.github.GithubOrganizationOAuth2' def auth_handlers(self, start_url): url = 'https://api.github.com/orgs/foobar/members/foobar' HTTPretty.register_uri(HTTPretty.GET, url, status=204, body='') return super(GithubOrganizationOAuth2Test, self).auth_handlers( start_url ) def test_login(self): self.strategy.set_settings({'SOCIAL_AUTH_GITHUB_ORG_NAME': 'foobar'}) self.do_login() def test_partial_pipeline(self): self.strategy.set_settings({'SOCIAL_AUTH_GITHUB_ORG_NAME': 'foobar'}) self.do_partial_pipeline() class GithubOrganizationOAuth2FailTest(GithubOAuth2Test): backend_path = 'social_core.backends.github.GithubOrganizationOAuth2' def auth_handlers(self, start_url): url = 'https://api.github.com/orgs/foobar/members/foobar' HTTPretty.register_uri(HTTPretty.GET, url, status=404, body='{"message": "Not Found"}', content_type='application/json') return super(GithubOrganizationOAuth2FailTest, self).auth_handlers( start_url ) def test_login(self): self.strategy.set_settings({'SOCIAL_AUTH_GITHUB_ORG_NAME': 'foobar'}) with self.assertRaises(AuthFailed): self.do_login() def test_partial_pipeline(self): self.strategy.set_settings({'SOCIAL_AUTH_GITHUB_ORG_NAME': 'foobar'}) with self.assertRaises(AuthFailed): self.do_partial_pipeline() class GithubTeamOAuth2Test(GithubOAuth2Test): backend_path = 'social_core.backends.github.GithubTeamOAuth2' def auth_handlers(self, start_url): url = 'https://api.github.com/teams/123/members/foobar' HTTPretty.register_uri(HTTPretty.GET, url, status=204, body='') return super(GithubTeamOAuth2Test, self).auth_handlers( start_url ) def test_login(self): self.strategy.set_settings({'SOCIAL_AUTH_GITHUB_TEAM_ID': '123'}) self.do_login() def test_partial_pipeline(self): self.strategy.set_settings({'SOCIAL_AUTH_GITHUB_TEAM_ID': '123'}) self.do_partial_pipeline() class GithubTeamOAuth2FailTest(GithubOAuth2Test): backend_path = 'social_core.backends.github.GithubTeamOAuth2' def auth_handlers(self, start_url): url = 'https://api.github.com/teams/123/members/foobar' HTTPretty.register_uri(HTTPretty.GET, url, status=404, body='{"message": "Not Found"}', content_type='application/json') return super(GithubTeamOAuth2FailTest, self).auth_handlers( start_url ) def test_login(self): self.strategy.set_settings({'SOCIAL_AUTH_GITHUB_TEAM_ID': '123'}) with self.assertRaises(AuthFailed): self.do_login() def test_partial_pipeline(self): self.strategy.set_settings({'SOCIAL_AUTH_GITHUB_TEAM_ID': '123'}) with self.assertRaises(AuthFailed): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_auth0.py0000644000175000017500000000463513432611360024367 0ustar omabomab00000000000000 import json from jose import jwt from httpretty import HTTPretty from .oauth import OAuth2Test JWK_KEY = { 'kty': 'RSA', 'd': 'ZmswNokEvBcxW_Kvcy8mWUQOQCBdGbnM0xR7nhvGHC-Q24z3XAQWlMWbsmGc_R1o' \ '_F3zK7DBlc3BokdRaO1KJirNmnHCw5TlnBlJrXiWpFBtVglUg98-4sRRO0VWnGXK' \ 'JPOkBQ6b_DYRO3b0o8CSpWowpiV6HB71cjXTqKPZf-aXU9WjCCAtxVjfIxgQFu5I' \ '-G1Qah8mZeY8HK_y99L4f0siZcbUoaIcfeWBhxi14ODyuSAHt0sNEkhiIVBZE7QZ' \ 'm-SEP1ryT9VAaljbwHHPmg7NC26vtLZhvaBGbTTJnEH0ZubbN2PMzsfeNyoCIHy4' \ '4QDSpQDCHfgcGOlHY_t5gQ', 'e': 'AQAB', 'use': 'sig', 'kid': 'foobar', 'alg': 'RS256', 'n': 'pUfcJ8WFrVue98Ygzb6KEQXHBzi8HavCu8VENB2As943--bHPcQ-nScXnrRFAUg8' \ 'H5ZltuOcHWvsGw_AQifSLmOCSWJAPkdNb0w0QzY7Re8NrPjCsP58Tytp5LicF0Ao' \ 'Ag28UK3JioY9hXHGvdZsWR1Rp3I-Z3nRBP6HyO18pEgcZ91c9aAzsqu80An9X4DA' \ 'b1lExtZorvcd5yTBzZgr-MUeytVRni2lDNEpa6OFuopHXmg27Hn3oWAaQlbymd4g' \ 'ifc01oahcwl3ze2tMK6gJxa_TdCf1y99Yq6oilmVvZJ8kwWWnbPE-oDmOVPVnEyT' \ 'vYVCvN4rBT1DQ-x0F1mo2Q' } JWK_PUBLIC_KEY = {key: value for key, value in JWK_KEY.items() if key != 'd'} DOMAIN = 'foobar.auth0.com' class Auth0OAuth2Test(OAuth2Test): backend_path = 'social_core.backends.auth0.Auth0OAuth2' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer', 'expires_in': 86400, 'id_token': jwt.encode({ 'nickname': 'foobar', 'email': 'foobar@auth0.com', 'name': 'John Doe', 'picture': 'http://example.com/image.png', 'sub': '123456', 'iss': 'https://{}/'.format(DOMAIN), }, JWK_KEY, algorithm='RS256') }) expected_username = 'foobar' jwks_url = 'https://foobar.auth0.com/.well-known/jwks.json' def extra_settings(self): settings = super(Auth0OAuth2Test, self).extra_settings() settings['SOCIAL_AUTH_' + self.name + '_DOMAIN'] = DOMAIN return settings def auth_handlers(self, start_url): HTTPretty.register_uri(HTTPretty.GET, self.jwks_url, body=json.dumps({'keys': [JWK_PUBLIC_KEY]}), content_type='application/json') return super(Auth0OAuth2Test, self).auth_handlers(start_url) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_udata.py0000644000175000017500000000143113336542652024445 0ustar omabomab00000000000000import json from six.moves.urllib_parse import urlencode from .oauth import OAuth2Test class DatagouvfrOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.udata.DatagouvfrOAuth2' user_data_url = 'https://www.data.gouv.fr/api/1/me/' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer', 'first_name': 'foobar', 'email': 'foobar@example.com' }) request_token_body = urlencode({ 'oauth_token_secret': 'foobar-secret', 'oauth_token': 'foobar', 'oauth_callback_confirmed': 'true' }) user_data_body = json.dumps({}) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_yammer.py0000644000175000017500000000762613336542652024655 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class YammerOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.yammer.YammerOAuth2' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': { 'user_id': 1010101010, 'view_groups': True, 'modify_messages': True, 'network_id': 101010, 'created_at': '2013/03/17 16:39:56 +0000', 'view_members': True, 'authorized_at': '2013/03/17 16:39:56 +0000', 'view_subscriptions': True, 'view_messages': True, 'modify_subscriptions': True, 'token': 'foobar', 'expires_at': None, 'network_permalink': 'foobar.com', 'view_tags': True, 'network_name': 'foobar.com' }, 'user': { 'last_name': 'Bar', 'web_url': 'https://www.yammer.com/foobar/users/foobar', 'expertise': None, 'full_name': 'Foo Bar', 'timezone': 'Pacific Time (US & Canada)', 'mugshot_url': 'https://mug0.assets-yammer.com/mugshot/images/' '48x48/no_photo.png', 'guid': None, 'network_name': 'foobar', 'id': 1010101010, 'previous_companies': [], 'first_name': 'Foo', 'stats': { 'following': 0, 'followers': 0, 'updates': 1 }, 'hire_date': None, 'state': 'active', 'location': None, 'department': 'Software Development', 'type': 'user', 'show_ask_for_photo': True, 'job_title': 'Software Developer', 'interests': None, 'kids_names': None, 'activated_at': '2013/03/17 16:27:50 +0000', 'verified_admin': 'false', 'can_broadcast': 'false', 'schools': [], 'admin': 'false', 'network_domains': ['foobar.com'], 'name': 'foobar', 'external_urls': [], 'url': 'https://www.yammer.com/api/v1/users/1010101010', 'settings': { 'xdr_proxy': 'https://xdrproxy.yammer.com' }, 'summary': None, 'network_id': 101010, 'contact': { 'phone_numbers': [], 'im': { 'username': '', 'provider': '' }, 'email_addresses': [{ 'type': 'primary', 'address': 'foo@bar.com' }], 'has_fake_email': False }, 'birth_date': '', 'mugshot_url_template': 'https://mug0.assets-yammer.com/mugshot/' 'images/{width}x{height}/no_photo.png', 'significant_other': None }, 'network': { 'show_upgrade_banner': False, 'header_text_color': '#FFFFFF', 'is_org_chart_enabled': True, 'name': 'foobar.com', 'is_group_enabled': True, 'header_background_color': '#396B9A', 'created_at': '2012/12/26 16:52:35 +0000', 'profile_fields_config': { 'enable_work_phone': True, 'enable_mobile_phone': True, 'enable_job_title': True }, 'permalink': 'foobar.com', 'paid': False, 'id': 101010, 'is_chat_enabled': True, 'web_url': 'https://www.yammer.com/foobar.com', 'moderated': False, 'community': False, 'type': 'network', 'navigation_background_color': '#38699F', 'navigation_text_color': '#FFFFFF' } }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_linkedin.py0000644000175000017500000000224513336542652025150 0ustar omabomab00000000000000import json from six.moves.urllib_parse import urlencode from .oauth import OAuth1Test, OAuth2Test class BaseLinkedinTest(object): user_data_url = 'https://api.linkedin.com/v1/people/~:' \ '(first-name,id,last-name)' expected_username = 'FooBar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps({ 'lastName': 'Bar', 'id': '1010101010', 'firstName': 'Foo' }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() class LinkedinOAuth1Test(BaseLinkedinTest, OAuth1Test): backend_path = 'social_core.backends.linkedin.LinkedinOAuth' request_token_body = urlencode({ 'oauth_token_secret': 'foobar-secret', 'oauth_token': 'foobar', 'oauth_callback_confirmed': 'true' }) class LinkedinOAuth2Test(BaseLinkedinTest, OAuth2Test): backend_path = 'social_core.backends.linkedin.LinkedinOAuth2' class LinkedinMobileOAuth2Test(BaseLinkedinTest, OAuth2Test): backend_path = 'social_core.backends.linkedin.LinkedinMobileOAuth2' social-auth-core-3.1.0/social_core/tests/backends/test_saml.py0000644000175000017500000001151113336542652024303 0ustar omabomab00000000000000import re import json import sys import unittest2 import requests import os from os import path try: from unittest.mock import patch except ImportError: from mock import patch from httpretty import HTTPretty from six.moves.urllib_parse import urlparse, urlunparse, urlencode, parse_qs try: from onelogin.saml2.utils import OneLogin_Saml2_Utils except ImportError: # Only available for python 2.7 at the moment, so don't worry if this fails pass from .base import BaseBackendTest from ...exceptions import AuthMissingParameter DATA_DIR = path.join(path.dirname(__file__), 'data') @unittest2.skipIf('TRAVIS' in os.environ, 'Travis-ci segfaults probably due to a bad ' 'dependencies build') @unittest2.skipIf('__pypy__' in sys.builtin_module_names, 'dm.xmlsec not compatible with pypy') class SAMLTest(BaseBackendTest): backend_path = 'social_core.backends.saml.SAMLAuth' expected_username = 'myself' def extra_settings(self): name = path.join(DATA_DIR, 'saml_config.json') with open(name, 'r') as config_file: config_str = config_file.read() return json.loads(config_str) def setUp(self): """Patch the time so that we can replay canned request/response pairs""" super(SAMLTest, self).setUp() @staticmethod def fixed_time(): return OneLogin_Saml2_Utils.parse_SAML_to_time( '2015-05-09T03:57:22Z' ) now_patch = patch.object(OneLogin_Saml2_Utils, 'now', fixed_time) now_patch.start() self.addCleanup(now_patch.stop) def install_http_intercepts(self, start_url, return_url): # When we request start_url # (https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO...) # we will eventually get a redirect back, with SAML assertion # data in the query string. A pre-recorded correct response # is kept in this .txt file: name = path.join(DATA_DIR, 'saml_response.txt') with open(name, 'r') as response_file: response_url = response_file.read() HTTPretty.register_uri(HTTPretty.GET, start_url, status=301, location=response_url) HTTPretty.register_uri(HTTPretty.GET, return_url, status=200, body='foobar') def do_start(self): start_url = self.backend.start().url # Modify the start URL to make the SAML request consistent # from test to test: start_url = self.modify_start_url(start_url) # If the SAML Identity Provider recognizes the user, we will # be redirected back to: return_url = self.backend.redirect_uri self.install_http_intercepts(start_url, return_url) response = requests.get(start_url) self.assertTrue(response.url.startswith(return_url)) self.assertEqual(response.text, 'foobar') query_values = dict((k, v[0]) for k, v in parse_qs(urlparse(response.url).query).items()) self.assertNotIn(' ', query_values['SAMLResponse']) self.strategy.set_request_data(query_values, self.backend) return self.backend.complete() def test_metadata_generation(self): """Test that we can generate the metadata without error""" xml, errors = self.backend.generate_metadata_xml() self.assertEqual(len(errors), 0) self.assertEqual(xml.decode()[0], '<') def test_login(self): """Test that we can authenticate with a SAML IdP (TestShib)""" # pretend we've started with a URL like /login/saml/?idp=testshib: self.strategy.set_request_data({'idp': 'testshib'}, self.backend) self.do_login() def test_login_no_idp(self): """Logging in without an idp param should raise AuthMissingParameter""" with self.assertRaises(AuthMissingParameter): self.do_start() def modify_start_url(self, start_url): """ Given a SAML redirect URL, parse it and change the ID to a consistent value, so the request is always identical. """ # Parse the SAML Request URL to get the XML being sent to TestShib url_parts = urlparse(start_url) query = dict((k, v[0]) for (k, v) in parse_qs(url_parts.query).items()) xml = OneLogin_Saml2_Utils.decode_base64_and_inflate( query['SAMLRequest'] ) # Modify the XML: xml = xml.decode() xml, changed = re.subn(r'ID="[^"]+"', 'ID="TEST_ID"', xml) self.assertEqual(changed, 1) # Update the URL to use the modified query string: query['SAMLRequest'] = OneLogin_Saml2_Utils.deflate_and_base64_encode( xml ) url_parts = list(url_parts) url_parts[4] = urlencode(query) return urlunparse(url_parts) social-auth-core-3.1.0/social_core/tests/backends/test_podio.py0000644000175000017500000000321113336542652024457 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class PodioOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.podio.PodioOAuth2' user_data_url = 'https://api.podio.com/user/status' expected_username = 'user_1010101010' access_token_body = json.dumps({ 'token_type': 'bearer', 'access_token': '11309ea9016a4ad99f1a3bcb9bc7a9d1', 'refresh_token': '52d01df8b9ac46a4a6be1333d9f81ef2', 'expires_in': 28800, 'ref': { 'type': 'user', 'id': 1010101010, } }) user_data_body = json.dumps({ 'user': { 'user_id': 1010101010, 'activated_on': '2012-11-22 09:37:21', 'created_on': '2012-11-21 12:23:47', 'locale': 'en_GB', 'timezone': 'Europe/Copenhagen', 'mail': 'foo@bar.com', 'mails': [ { 'disabled': False, 'mail': 'foobar@example.com', 'primary': False, 'verified': True }, { 'disabled': False, 'mail': 'foo@bar.com', 'primary': True, 'verified': True } ], # more properties ... }, 'profile': { 'last_seen_on': '2013-05-16 12:21:13', 'link': 'https://podio.com/users/1010101010', 'name': 'Foo Bar', # more properties ... } # more properties ... }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_strava.py0000644000175000017500000000351213336542652024651 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class StravaOAuthTest(OAuth2Test): backend_path = 'social_core.backends.strava.StravaOAuth' user_data_url = 'https://www.strava.com/api/v3/athlete' expected_username = '227615' access_token_body = json.dumps({ "access_token": "83ebeabdec09f6670863766f792ead24d61fe3f9", "athlete": { "id": 227615, "resource_state": 3, "firstname": "John", "lastname": "Applestrava", "profile_medium": "http://pics.com/227615/medium.jpg", "profile": "http://pics.com/227615/large.jpg", "city": "San Francisco", "state": "California", "country": "United States", "sex": "M", "friend": "null", "follower": "null", "premium": "true", "created_at": "2008-01-01T17:44:00Z", "updated_at": "2013-09-04T20:00:50Z", "follower_count": 273, "friend_count": 19, "mutual_friend_count": 0, "date_preference": "%m/%d/%Y", "measurement_preference": "feet", "email": "john@applestrava.com", "clubs": [], "bikes": [], "shoes": [] } }) user_data_body = json.dumps({ "id": 227615, "resource_state": 2, "firstname": "John", "lastname": "Applestrava", "profile_medium": "http://pics.com/227615/medium.jpg", "profile": "http://pics.com/227615/large.jpg", "city": "San Francisco", "state": "CA", "country": "United States", "sex": "M", "friend": "null", "follower": "accepted", "premium": "true", "created_at": "2011-03-19T21:59:57Z", "updated_at": "2013-09-05T16:46:54Z", "approve_followers": "false" }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_kakao.py0000644000175000017500000000151713336542652024442 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class KakaoOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.kakao.KakaoOAuth2' user_data_url = 'https://kapi.kakao.com/v1/user/me' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar' }) user_data_body = json.dumps({ 'id': '101010101', 'properties': { 'nickname': 'foobar', 'thumbnail_image': 'http://mud-kage.kakao.co.kr/14/dn/btqbh1AKmRf/' 'ujlHpQhxtMSbhKrBisrxe1/o.jpg', 'profile_image': 'http://mud-kage.kakao.co.kr/14/dn/btqbjCnl06Q/' 'wbMJSVAUZB7lzSImgGdsoK/o.jpg' } }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_google.py0000644000175000017500000002311113432605330024610 0ustar omabomab00000000000000import datetime import json import unittest2 from httpretty import HTTPretty from six.moves.urllib_parse import urlencode from ...actions import do_disconnect from ..models import User from .oauth import OAuth1Test, OAuth2Test from .open_id import OpenIdTest from .open_id_connect import OpenIdConnectTestMixin class GoogleOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.google.GoogleOAuth2' user_data_url = 'https://www.googleapis.com/oauth2/v3/userinfo' expected_username = 'foo' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps({ 'profile': 'https://plus.google.com/101010101010101010101', 'family_name': 'Bar', 'sub': '101010101010101010101', 'picture': 'https://lh5.googleusercontent.com/-ui-GqpNh5Ms/' 'AAAAAAAAAAI/AAAAAAAAAZw/a7puhHMO_fg/photo.jpg', 'locale': 'en', 'email_verified': True, 'given_name': 'Foo', 'email': 'foo@bar.com', 'name': 'Foo Bar', }) def test_login(self): self.do_login() last_request = HTTPretty.last_request self.assertEqual(last_request.method, 'GET') self.assertTrue(self.user_data_url.endswith(last_request.path)) self.assertEqual( last_request.headers['Authorization'], 'Bearer foobar', ) self.assertEqual(last_request.querystring, {}) def test_partial_pipeline(self): self.do_partial_pipeline() def test_with_unique_user_id(self): self.strategy.set_settings({ 'SOCIAL_AUTH_GOOGLE_OAUTH2_USE_UNIQUE_USER_ID': True, }) self.do_login() class GoogleOAuth1Test(OAuth1Test): backend_path = 'social_core.backends.google.GoogleOAuth' user_data_url = 'https://www.googleapis.com/userinfo/email' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) request_token_body = urlencode({ 'oauth_token_secret': 'foobar-secret', 'oauth_token': 'foobar', 'oauth_callback_confirmed': 'true' }) user_data_body = urlencode({ 'email': 'foobar@gmail.com', 'isVerified': 'true', 'id': '101010101010101010101' }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() def test_with_unique_user_id(self): self.strategy.set_settings({ 'SOCIAL_AUTH_GOOGLE_OAUTH_USE_UNIQUE_USER_ID': True }) self.do_login() def test_with_anonymous_key_and_secret(self): self.strategy.set_settings({ 'SOCIAL_AUTH_GOOGLE_OAUTH_KEY': None, 'SOCIAL_AUTH_GOOGLE_OAUTH_SECRET': None }) self.do_login() JANRAIN_NONCE = datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%SZ') class GoogleOpenIdTest(OpenIdTest): backend_path = 'social_core.backends.google.GoogleOpenId' expected_username = 'FooBar' discovery_body = ''.join([ '', '', '', '', 'http://specs.openid.net/auth/2.0/signon', 'http://openid.net/srv/ax/1.0', 'http://specs.openid.net/extensions/ui/1.0/mode/popup', 'http://specs.openid.net/extensions/ui/1.0/icon', 'http://specs.openid.net/extensions/pape/1.0', 'https://www.google.com/accounts/o8/ud', '', '', 'http://specs.openid.net/auth/2.0/signon', 'http://openid.net/srv/ax/1.0', 'http://specs.openid.net/extensions/ui/1.0/mode/popup', 'http://specs.openid.net/extensions/ui/1.0/icon', 'http://specs.openid.net/extensions/pape/1.0', 'https://www.google.com/accounts/o8/ud?source=mail', '', '', 'http://specs.openid.net/auth/2.0/signon', 'http://openid.net/srv/ax/1.0', 'http://specs.openid.net/extensions/ui/1.0/mode/popup', 'http://specs.openid.net/extensions/ui/1.0/icon', 'http://specs.openid.net/extensions/pape/1.0', 'https://www.google.com/accounts/o8/ud?source=gmail.com', '', '', 'http://specs.openid.net/auth/2.0/signon', 'http://openid.net/srv/ax/1.0', 'http://specs.openid.net/extensions/ui/1.0/mode/popup', 'http://specs.openid.net/extensions/ui/1.0/icon', 'http://specs.openid.net/extensions/pape/1.0', '', 'https://www.google.com/accounts/o8/ud?source=googlemail.com', '', '', '', 'http://specs.openid.net/auth/2.0/signon', 'http://openid.net/srv/ax/1.0', 'http://specs.openid.net/extensions/ui/1.0/mode/popup', 'http://specs.openid.net/extensions/ui/1.0/icon', 'http://specs.openid.net/extensions/pape/1.0', 'https://www.google.com/accounts/o8/ud?source=profiles', '', '', '' ]) server_response = urlencode({ 'janrain_nonce': JANRAIN_NONCE, 'openid.assoc_handle': 'assoc-handle', 'openid.claimed_id': 'https://www.google.com/accounts/o8/id?' 'id=some-google-id', 'openid.ext1.mode': 'fetch_response', 'openid.ext1.type.email': 'http://axschema.org/contact/email', 'openid.ext1.type.first_name': 'http://axschema.org/namePerson/first', 'openid.ext1.type.last_name': 'http://axschema.org/namePerson/last', 'openid.ext1.type.old_email': 'http://schema.openid.net/contact/email', 'openid.ext1.value.email': 'foo@bar.com', 'openid.ext1.value.first_name': 'Foo', 'openid.ext1.value.last_name': 'Bar', 'openid.ext1.value.old_email': 'foo@bar.com', 'openid.identity': 'https://www.google.com/accounts/o8/id?' 'id=some-google-id', 'openid.mode': 'id_res', 'openid.ns': 'http://specs.openid.net/auth/2.0', 'openid.ns.ext1': 'http://openid.net/srv/ax/1.0', 'openid.op_endpoint': 'https://www.google.com/accounts/o8/ud', 'openid.response_nonce': JANRAIN_NONCE + 'by95cT34vX7p9g', 'openid.return_to': 'http://myapp.com/complete/google/?' 'janrain_nonce=' + JANRAIN_NONCE, 'openid.sig': 'brT2kmu3eCzb1gQ1pbaXdnWioVM=', 'openid.signed': 'op_endpoint,claimed_id,identity,return_to,' 'response_nonce,assoc_handle,ns.ext1,ext1.mode,' 'ext1.type.old_email,ext1.value.old_email,' 'ext1.type.first_name,ext1.value.first_name,' 'ext1.type.last_name,ext1.value.last_name,' 'ext1.type.email,ext1.value.email' }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() class GoogleRevokeTokenTest(GoogleOAuth2Test): def test_revoke_token(self): self.strategy.set_settings({ 'SOCIAL_AUTH_GOOGLE_OAUTH2_REVOKE_TOKENS_ON_DISCONNECT': True }) self.do_login() user = User.get(self.expected_username) user.password = 'password' HTTPretty.register_uri(self._method(self.backend.REVOKE_TOKEN_METHOD), self.backend.REVOKE_TOKEN_URL, status=200) do_disconnect(self.backend, user) class GoogleOpenIdConnectTest(OpenIdConnectTestMixin, OAuth2Test): backend_path = \ 'social_core.backends.google_openidconnect.GoogleOpenIdConnect' user_data_url = \ 'https://www.googleapis.com/plus/v1/people/me/openIdConnect' issuer = 'accounts.google.com' openid_config_body = json.dumps({ 'issuer': 'https://accounts.google.com', 'authorization_endpoint': 'https://accounts.google.com/o/oauth2/v2/auth', 'token_endpoint': 'https://www.googleapis.com/oauth2/v4/token', 'userinfo_endpoint': 'https://www.googleapis.com/oauth2/v3/userinfo', 'revocation_endpoint': 'https://accounts.google.com/o/oauth2/revoke', 'jwks_uri': 'https://www.googleapis.com/oauth2/v3/certs', 'response_types_supported': [ 'code', 'token', 'id_token', 'code token', 'code id_token', 'token id_token', 'code token id_token', 'none', ], 'subject_types_supported': [ 'public', ], 'id_token_signing_alg_values_supported': [ 'RS256', ], 'scopes_supported': [ 'openid', 'email', 'profile', ], 'token_endpoint_auth_methods_supported': [ 'client_secret_post', 'client_secret_basic', ], 'claims_supported': [ 'aud', 'email', 'email_verified', 'exp', 'family_name', 'given_name', 'iat', 'iss', 'locale', 'name', 'picture', 'sub', ], }) social-auth-core-3.1.0/social_core/tests/backends/test_yahoo.py0000644000175000017500000000474513336542652024501 0ustar omabomab00000000000000import json import requests from httpretty import HTTPretty from six.moves.urllib_parse import urlencode from .oauth import OAuth1Test class YahooOAuth1Test(OAuth1Test): backend_path = 'social_core.backends.yahoo.YahooOAuth' user_data_url = 'https://social.yahooapis.com/v1/user/a-guid/profile?' \ 'format=json' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) request_token_body = urlencode({ 'oauth_token_secret': 'foobar-secret', 'oauth_token': 'foobar', 'oauth_callback_confirmed': 'true' }) guid_body = json.dumps({ 'guid': { 'uri': 'https://social.yahooapis.com/v1/me/guid', 'value': 'a-guid' } }) user_data_body = json.dumps({ 'profile': { 'bdRestricted': True, 'memberSince': '2007-12-11T14:40:30Z', 'image': { 'width': 192, 'imageUrl': 'http://l.yimg.com/dh/ap/social/profile/' 'profile_b192.png', 'size': '192x192', 'height': 192 }, 'created': '2013-03-18T04:15:08Z', 'uri': 'https://social.yahooapis.com/v1/user/a-guid/profile', 'isConnected': False, 'profileUrl': 'http://profile.yahoo.com/a-guid', 'guid': 'a-guid', 'nickname': 'foobar', 'emails': [{ 'handle': 'foobar@yahoo.com', 'id': 1, 'primary': True, 'type': 'HOME', }, { 'handle': 'foobar@email.com', 'id': 2, 'type': 'HOME', }], } }) def test_login(self): HTTPretty.register_uri( HTTPretty.GET, 'https://social.yahooapis.com/v1/me/guid?format=json', status=200, body=self.guid_body ) self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() def test_get_user_details(self): HTTPretty.register_uri( HTTPretty.GET, self.user_data_url, status=200, body=self.user_data_body ) response = requests.get(self.user_data_url) user_details = self.backend.get_user_details( response.json()['profile'] ) self.assertEqual(user_details['email'], 'foobar@yahoo.com') social-auth-core-3.1.0/social_core/tests/backends/test_stocktwits.py0000644000175000017500000000315213336542652025567 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class StocktwitsOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.stocktwits.StocktwitsOAuth2' user_data_url = 'https://api.stocktwits.com/api/2/account/verify.json' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps({ 'response': { 'status': 200 }, 'user': { 'username': 'foobar', 'name': 'Foo Bar', 'classification': [], 'avatar_url': 'http://avatars.stocktwits.net/images/' 'default_avatar_thumb.jpg', 'avatar_url_ssl': 'https://s3.amazonaws.com/st-avatars/images/' 'default_avatar_thumb.jpg', 'id': 101010, 'identity': 'User' } }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() class StocktwitsOAuth2UsernameAlternativeTest(StocktwitsOAuth2Test): user_data_body = json.dumps({ 'response': { 'status': 200 }, 'user': { 'username': 'foobar', 'name': 'Foobar', 'classification': [], 'avatar_url': 'http://avatars.stocktwits.net/images/' 'default_avatar_thumb.jpg', 'avatar_url_ssl': 'https://s3.amazonaws.com/st-avatars/images/' 'default_avatar_thumb.jpg', 'id': 101010, 'identity': 'User' } }) social-auth-core-3.1.0/social_core/tests/backends/test_globus.py0000644000175000017500000000272313432605066024643 0ustar omabomab00000000000000import json import unittest2 from .oauth import OAuth1Test, OAuth2Test from .open_id_connect import OpenIdConnectTestMixin class GlobusOpenIdConnectTest(OpenIdConnectTestMixin, OAuth2Test): backend_path = \ 'social_core.backends.globus.GlobusOpenIdConnect' issuer = 'https://auth.globus.org' openid_config_body = json.dumps({ 'issuer': 'https://auth.globus.org', 'authorization_endpoint': 'https://auth.globus.org/v2/oauth2/authorize', 'userinfo_endpoint': 'https://auth.globus.org/v2/oauth2/userinfo', 'token_endpoint': 'https://auth.globus.org/v2/oauth2/token', 'revocation_endpoint': 'https://auth.globus.org/v2/oauth2/token/revoke', 'jwks_uri': 'https://auth.globus.org/jwk.json', 'response_types_supported': [ 'code', 'token', 'token id_token', 'id_token' ], 'id_token_signing_alg_values_supported': [ 'RS512' ], 'scopes_supported': [ 'openid', 'email', 'profile' ], 'token_endpoint_auth_methods_supported': [ 'client_secret_basic' ], 'claims_supported': [ 'at_hash', 'aud', 'email', 'exp', 'name', 'nonce', 'preferred_username', 'iat', 'iss', 'sub' ], 'subject_types_supported' : ['public'] }) social-auth-core-3.1.0/social_core/tests/backends/test_dribbble.py0000644000175000017500000000112113336542652025110 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class DribbbleOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.dribbble.DribbbleOAuth2' user_data_url = 'https://api.dribbble.com/v1/user' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps({ 'id': 'foobar', 'username': 'foobar', 'name': 'Foo Bar' }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_skyrock.py0000644000175000017500000000247513336542652025045 0ustar omabomab00000000000000import json from six.moves.urllib_parse import urlencode from .oauth import OAuth1Test class SkyrockOAuth1Test(OAuth1Test): backend_path = 'social_core.backends.skyrock.SkyrockOAuth' user_data_url = 'https://api.skyrock.com/v2/user/get.json' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) request_token_body = urlencode({ 'oauth_token_secret': 'foobar-secret', 'oauth_token': 'foobar', }) user_data_body = json.dumps({ 'locale': 'en_US', 'city': '', 'has_blog': False, 'web_messager_enabled': True, 'email': 'foo@bar.com', 'username': 'foobar', 'firstname': 'Foo', 'user_url': '', 'address1': '', 'address2': '', 'has_profile': False, 'allow_messages_from': 'everybody', 'is_online': False, 'postalcode': '', 'lang': 'en', 'id_user': 10101010, 'name': 'Bar', 'gender': 0, 'avatar_url': 'http://www.skyrock.com/img/avatars/default-0.jpg', 'nb_friends': 0, 'country': 'US', 'birth_date': '1980-06-10' }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_ngpvan.py0000644000175000017500000002051213336542652024641 0ustar omabomab00000000000000"""Tests for NGP VAN ActionID Backend""" import datetime from httpretty import HTTPretty from six.moves.urllib_parse import urlencode from .open_id import OpenIdTest JANRAIN_NONCE = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ') class NGPVANActionIDOpenIDTest(OpenIdTest): """Test the NGP VAN ActionID OpenID 1.1 Backend""" backend_path = 'social_core.backends.ngpvan.ActionIDOpenID' expected_username = 'testuser@user.local' discovery_body = ' '.join([ '', '', '', '', 'http://specs.openid.net/auth/2.0/signon', 'http://openid.net/extensions/sreg/1.1', 'http://axschema.org/contact/email', 'https://accounts.ngpvan.com/OpenId/Provider', '', '', 'http://openid.net/signon/1.0', 'http://openid.net/extensions/sreg/1.1', 'http://axschema.org/contact/email', 'https://accounts.ngpvan.com/OpenId/Provider', '', '', '' ]) server_response = urlencode({ 'openid.claimed_id': 'https://accounts.ngpvan.com/user/abcd123', 'openid.identity': 'https://accounts.ngpvan.com/user/abcd123', 'openid.sig': 'Midw8F/rCDwW7vMz3y+vK6rjz6s=', 'openid.signed': 'claimed_id,identity,assoc_handle,op_endpoint,return_' 'to,response_nonce,ns.alias3,alias3.mode,alias3.type.' 'alias1,alias3.value.alias1,alias3.type.alias2,alias3' '.value.alias2,alias3.type.alias3,alias3.value.alias3' ',alias3.type.alias4,alias3.value.alias4,alias3.type.' 'alias5,alias3.value.alias5,alias3.type.alias6,alias3' '.value.alias6,alias3.type.alias7,alias3.value.alias7' ',alias3.type.alias8,alias3.value.alias8,ns.sreg,sreg' '.fullname', 'openid.assoc_handle': '{635790678917902781}{GdSyFA==}{20}', 'openid.op_endpoint': 'https://accounts.ngpvan.com/OpenId/Provider', 'openid.return_to': 'http://myapp.com/complete/actionid-openid/', 'openid.response_nonce': JANRAIN_NONCE + 'MMgBGEre', 'openid.mode': 'id_res', 'openid.ns': 'http://specs.openid.net/auth/2.0', 'openid.ns.alias3': 'http://openid.net/srv/ax/1.0', 'openid.alias3.mode': 'fetch_response', 'openid.alias3.type.alias1': 'http://openid.net/schema/contact/phone/b' 'usiness', 'openid.alias3.value.alias1': '+12015555555', 'openid.alias3.type.alias2': 'http://openid.net/schema/contact/interne' 't/email', 'openid.alias3.value.alias2': 'testuser@user.local', 'openid.alias3.type.alias3': 'http://openid.net/schema/namePerson/firs' 't', 'openid.alias3.value.alias3': 'John', 'openid.alias3.type.alias4': 'http://openid.net/schema/namePerson/las' 't', 'openid.alias3.value.alias4': 'Smith', 'openid.alias3.type.alias5': 'http://axschema.org/namePerson/first', 'openid.alias3.value.alias5': 'John', 'openid.alias3.type.alias6': 'http://axschema.org/namePerson/last', 'openid.alias3.value.alias6': 'Smith', 'openid.alias3.type.alias7': 'http://axschema.org/namePerson', 'openid.alias3.value.alias7': 'John Smith', 'openid.alias3.type.alias8': 'http://openid.net/schema/namePerson', 'openid.alias3.value.alias8': 'John Smith', 'openid.ns.sreg': 'http://openid.net/extensions/sreg/1.1', 'openid.sreg.fullname': 'John Smith', }) def setUp(self): """Setup the test""" super(NGPVANActionIDOpenIDTest, self).setUp() # Mock out the NGP VAN endpoints HTTPretty.register_uri( HTTPretty.POST, 'https://accounts.ngpvan.com/Home/Xrds', status=200, body=self.discovery_body ) HTTPretty.register_uri( HTTPretty.GET, 'https://accounts.ngpvan.com/user/abcd123', status=200, body=self.discovery_body ) HTTPretty.register_uri( HTTPretty.GET, 'https://accounts.ngpvan.com/OpenId/Provider', status=200, body=self.discovery_body ) def test_login(self): """Test the login flow using python-social-auth's built in test""" self.do_login() def test_partial_pipeline(self): """Test the partial flow using python-social-auth's built in test""" self.do_partial_pipeline() def test_get_ax_attributes(self): """Test that the AX attributes that NGP VAN responds with are present""" records = self.backend.get_ax_attributes() self.assertEqual(records, [ ('http://openid.net/schema/contact/internet/email', 'email'), ('http://openid.net/schema/contact/phone/business', 'phone'), ('http://openid.net/schema/namePerson/first', 'first_name'), ('http://openid.net/schema/namePerson/last', 'last_name'), ('http://openid.net/schema/namePerson', 'fullname'), ]) def test_setup_request(self): """Test the setup_request functionality in the NGP VAN backend""" # We can grab the requested attributes by grabbing the HTML of the # OpenID auth form and pulling out the hidden fields _, inputs = self.get_form_data(self.backend.auth_html()) # Confirm that the only required attribute is email self.assertEqual(inputs['openid.ax.required'], 'ngpvanemail') # Confirm that the 3 optional attributes are requested "if available" self.assertIn('ngpvanphone', inputs['openid.ax.if_available']) self.assertIn('ngpvanfirstname', inputs['openid.ax.if_available']) self.assertIn('ngpvanlastname', inputs['openid.ax.if_available']) # Verify the individual attribute properties self.assertEqual( inputs['openid.ax.type.ngpvanemail'], 'http://openid.net/schema/contact/internet/email' ) self.assertEqual( inputs['openid.ax.type.ngpvanfirstname'], 'http://openid.net/schema/namePerson/first' ) self.assertEqual( inputs['openid.ax.type.ngpvanlastname'], 'http://openid.net/schema/namePerson/last' ) self.assertEqual( inputs['openid.ax.type.ngpvanphone'], 'http://openid.net/schema/contact/phone/business' ) def test_user_data(self): """Ensure that the correct user data is being passed to create_user""" self.strategy.set_settings({ 'USER_FIELDS': [ 'email', 'first_name', 'last_name', 'username', 'phone', 'fullname' ] }) user = self.do_start() self.assertEqual(user.username, u'testuser@user.local') self.assertEqual(user.email, u'testuser@user.local') self.assertEqual(user.extra_user_fields['phone'], u'+12015555555') self.assertEqual(user.extra_user_fields['first_name'], u'John') self.assertEqual(user.extra_user_fields['last_name'], u'Smith') self.assertEqual(user.extra_user_fields['fullname'], u'John Smith') def test_extra_data_phone(self): """Confirm that you can get a phone number via the relevant setting""" self.strategy.set_settings({ 'SOCIAL_AUTH_ACTIONID_OPENID_AX_EXTRA_DATA': [ ('http://openid.net/schema/contact/phone/business', 'phone') ] }) user = self.do_start() self.assertEqual(user.social_user.extra_data['phone'], u'+12015555555') def test_association_uid(self): """Test that the correct association uid is stored in the database""" user = self.do_start() self.assertEqual( user.social_user.uid, 'https://accounts.ngpvan.com/user/abcd123' ) social-auth-core-3.1.0/social_core/tests/backends/test_gitlab.py0000644000175000017500000000637413336542652024624 0ustar omabomab00000000000000import json from httpretty import HTTPretty from ...exceptions import AuthFailed from .oauth import OAuth2Test class GitLabOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.gitlab.GitLabOAuth2' user_data_url = 'https://gitlab.com/api/v4/user' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer', 'expires_in': 7200, 'refresh_token': 'barfoo' }) user_data_body = json.dumps({ 'two_factor_enabled': False, 'can_create_project': True, 'confirmed_at': '2016-12-28T12:26:19.256Z', 'twitter': '', 'linkedin': '', 'color_scheme_id': 1, 'web_url': 'https://gitlab.com/foobar', 'skype': '', 'identities': [], 'id': 123456, 'projects_limit': 100000, 'current_sign_in_at': '2016-12-28T12:26:19.795Z', 'state': 'active', 'location': None, 'email': 'foobar@example.com', 'website_url': '', 'username': 'foobar', 'bio': None, 'last_sign_in_at': '2016-12-28T12:26:19.795Z', 'is_admin': False, 'external': False, 'organization': None, 'name': 'Foo Bar', 'can_create_group': True, 'created_at': '2016-12-28T12:26:19.638Z', 'avatar_url': 'https://secure.gravatar.com/avatar/94d093eda664addd6e450d7e9881bcae?s=32&d=identicon', 'theme_id': 2 }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() class GitLabCustomDomainOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.gitlab.GitLabOAuth2' user_data_url = 'https://example.com/api/v4/user' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer', 'expires_in': 7200, 'refresh_token': 'barfoo' }) user_data_body = json.dumps({ 'two_factor_enabled': False, 'can_create_project': True, 'confirmed_at': '2016-12-28T12:26:19.256Z', 'twitter': '', 'linkedin': '', 'color_scheme_id': 1, 'web_url': 'https://example.com/foobar', 'skype': '', 'identities': [], 'id': 123456, 'projects_limit': 100000, 'current_sign_in_at': '2016-12-28T12:26:19.795Z', 'state': 'active', 'location': None, 'email': 'foobar@example.com', 'website_url': '', 'username': 'foobar', 'bio': None, 'last_sign_in_at': '2016-12-28T12:26:19.795Z', 'is_admin': False, 'external': False, 'organization': None, 'name': 'Foo Bar', 'can_create_group': True, 'created_at': '2016-12-28T12:26:19.638Z', 'avatar_url': 'https://secure.gravatar.com/avatar/94d093eda664addd6e450d7e9881bcae?s=32&d=identicon', 'theme_id': 2 }) def test_login(self): self.strategy.set_settings({ 'SOCIAL_AUTH_GITLAB_API_URL': 'https://example.com' }) self.do_login() def test_partial_pipeline(self): self.strategy.set_settings({ 'SOCIAL_AUTH_GITLAB_API_URL': 'https://example.com' }) self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_keycloak.py0000644000175000017500000001051613432605566025156 0ustar omabomab00000000000000import json import time import unittest import jwt from .oauth import OAuth2Test _PRIVATE_KEY_HEADERLESS = ''' MIIEowIBAAKCAQEAvyo2hx1L3ALHeUd/6xk/lIhTyZ/HJZ+Sss/ge6T6gPdES4Dw BvwGlAp21iEbmjmizsv6+ZsyuKZUiC1J4A90lmIA57aYXHHoh9GBWQZzXeCNgghP JpGYYSCN+1qeD4nbwD9cQBtPrGBpPpPtv2a/xdPqDm5ko6adMhmbm8e4Me/ppWPi 0U+skWQJepBhjEt3x+AOMKDv2TUBWOc3mYFNkr9qNOPe7FxnqUk6ZtkI3QNjZTky AU7cat87u1vT5thAxVY18i1GfSZwtQbU3Ba6hXI5SIHB1lS88SJ9/+E/flJJPD2N Nzv2z3HAVuTUOYi48fnXFHpJLGv+mGLNtE77hwIDAQABAoIBAQCUyQYno2Wnl4Ip orys/rm9oV2VUAZwAgLrqV/O3Fkch1dgbLpktUNpdbuIbbxODQ3qZliwbVrM3Khu VNFq0pyrbxvFPRjY2s9g5m8GGz8vkdaRnmX8XtV6wxu+xoi/D006FBZ4zsj0IRXI 3tnsXsxj7Mv+72zk8ojmtYend4qlUfzBVTpMRDc4XDC4Ya91fgFgfibtUE1qc8Ap ctCzk9wZgN9SOKXHcKANhqC8BmQv7NspI0RT0Oq0n/U921P/+y1M++Z0Z2vQvjiR GvDpSlnmlLB3S3E7zHbmksyUyK4Ab7xYi51yFKgrYjtaM3QLCTlSmTQBM8EhjZNG VJvgEI4BAoGBAOQzd4PdB3jW6OnUXDrzWqfCAgpMxWtAmP5h+LrmsrV+upXggRnv iNzTqiq0QpdrgYrikFGDkGEsqfrnd7IkskDMT8PMuncmDkuF67kg6G4/vzyz5QyA jolf6qmHQPMCfxtCPUZZOUcuRKnj5KVIGfJu2gu5Z2lEysuV5ZQE0OiNAoGBANZz wqLhqN376MT3YEbsOcWXYBDXX+FzWAYHsxf4APHJrNr6pkM4dTsvXU2tQIUV4N3c SJvMVSI91VL8mdgQxHKaECUizye19brJ1BysbeFjBCK+Y6bYMd8n0Hdxxi/ZyeF9 AzfIPQN2uZSzU/I+Nt0tlz/SCoL4Qi3FQpgtLoFjAoGAb8XVsDy+wC1jf8SIOEei C7E3FpxrxhCp309VaRY+Si98bJS+J1nwC1mRa8FHLKt3k/NNBOAQA8jAqShetF7N AHgSSbEpU9rL/anmv5Kixf1rSexDMFB3gEn+wnKBGYYLg+p54M8rAvZio2QARgR+ 0QQCwONbB3Cuc/FDtbB2MrECgYAhSqtGmf2bKIZEPZsGp5l4YT2an7TUzRE3Lm7R I8ERyBs7i3nQKa2ZWIsFigXgIztbdd0Xwqrcu/in/2rqrf+xQtWKzlKWeZsCOl7h bKtKOBLmSeQyfJGRcR7dzB3WQ9shVETxnfZK2V2KBiTcEGh4AaHfWH4lQuETNfJW qXz0vQKBgDVz+ZvULA/OZWXrOI1il7KoahWdb9vr8VhWgHKnDW7hInDFh6SEQHn6 mSNns0AssDwr4TheET7klb7AvbBKrNSP/Tz9AzkwMz148T2ffkPFMZRuvRT+eQ5Z ey4gIBKESJF6X9fefiawCrI3+PC7x9x0ngP9R4t/OzDWVAYn9gmd '''.strip() _PRIVATE_KEY = ''' -----BEGIN RSA PRIVATE KEY----- {_PRIVATE_KEY_HEADERLESS} -----END RSA PRIVATE KEY----- '''.format( _PRIVATE_KEY_HEADERLESS=_PRIVATE_KEY_HEADERLESS ) _PUBLIC_KEY_HEADERLESS = ''' MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvyo2hx1L3ALHeUd/6xk/ lIhTyZ/HJZ+Sss/ge6T6gPdES4DwBvwGlAp21iEbmjmizsv6+ZsyuKZUiC1J4A90 lmIA57aYXHHoh9GBWQZzXeCNgghPJpGYYSCN+1qeD4nbwD9cQBtPrGBpPpPtv2a/ xdPqDm5ko6adMhmbm8e4Me/ppWPi0U+skWQJepBhjEt3x+AOMKDv2TUBWOc3mYFN kr9qNOPe7FxnqUk6ZtkI3QNjZTkyAU7cat87u1vT5thAxVY18i1GfSZwtQbU3Ba6 hXI5SIHB1lS88SJ9/+E/flJJPD2NNzv2z3HAVuTUOYi48fnXFHpJLGv+mGLNtE77 hwIDAQAB '''.strip() _PUBLIC_KEY = ''' -----BEGIN PUBLIC KEY----- {_PUBLIC_KEY_HEADERLESS} -----END PUBLIC KEY----- '''.format( _PUBLIC_KEY_HEADERLESS=_PUBLIC_KEY_HEADERLESS ) _KEY = 'example' _SECRET = '1234abcd-1234-abcd-1234-abcd1234adcd' _AUTHORIZATION_URL = 'https://sso.example.com/auth/realms/example/protocol/openid-connect/auth' _ACCESS_TOKEN_URL = 'https://sso.example.com/auth/realms/example/protocol/openid-connect/token' _ALGORITHM = 'RS256' _AUTH_TIME = int(time.time()) _PAYLOAD = { 'preferred_username': 'john.doe', 'email': 'john.doe@example.com', 'name': 'John Doe', 'given_name': 'John', 'family_name': 'Doe', 'iss': 'https://sso.example.com', 'sub': 'john.doe', 'aud': _KEY, 'exp': _AUTH_TIME + 3600, 'iat': _AUTH_TIME, } def _encode( payload, key=_PRIVATE_KEY, algorithm=_ALGORITHM ): return jwt.encode(payload, key=key, algorithm=algorithm).decode('utf-8') def _decode( token, key=_PUBLIC_KEY, algorithms=_ALGORITHM, audience=_KEY, ): return jwt.decode(token, key=key, algorithms=algorithms, audience=audience) class KeycloakOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.keycloak.KeycloakOAuth2' expected_username = 'john.doe' access_token_body = json.dumps({ 'token_type': 'Bearer', 'id_token': _encode(_PAYLOAD), 'access_token': _encode(_PAYLOAD), }) def extra_settings(self): return { 'SOCIAL_AUTH_KEYCLOAK_KEY': _KEY, 'SOCIAL_AUTH_KEYCLOAK_SECRET': _SECRET, 'SOCIAL_AUTH_KEYCLOAK_PUBLIC_KEY': _PUBLIC_KEY_HEADERLESS, 'SOCIAL_AUTH_KEYCLOAK_ALGORITHM': _ALGORITHM, 'SOCIAL_AUTH_KEYCLOAK_AUTHORIZATION_URL': _AUTHORIZATION_URL, 'SOCIAL_AUTH_KEYCLOAK_ACCESS_TOKEN_URL': _ACCESS_TOKEN_URL, } def test_encode_decode(self): token = _encode(_PAYLOAD) self.assertEqual(_PAYLOAD, _decode(token)) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_facebook.py0000644000175000017500000000367413336542652025133 0ustar omabomab00000000000000import json from ...backends.facebook import API_VERSION from ...exceptions import AuthUnknownError, AuthCanceled from .oauth import OAuth2Test class FacebookOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.facebook.FacebookOAuth2' user_data_url = 'https://graph.facebook.com/v{version}/me'.format( version=API_VERSION ) expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps({ 'username': 'foobar', 'first_name': 'Foo', 'last_name': 'Bar', 'verified': True, 'name': 'Foo Bar', 'gender': 'male', 'updated_time': '2013-02-13T14:59:42+0000', 'link': 'http://www.facebook.com/foobar', 'id': '110011001100010' }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() class FacebookOAuth2WrongUserDataTest(FacebookOAuth2Test): user_data_body = 'null' def test_login(self): with self.assertRaises(AuthUnknownError): self.do_login() def test_partial_pipeline(self): with self.assertRaises(AuthUnknownError): self.do_partial_pipeline() class FacebookOAuth2AuthCancelTest(FacebookOAuth2Test): access_token_status = 400 access_token_body = json.dumps({ 'error': { 'message': "redirect_uri isn't an absolute URI. Check RFC 3986.", 'code': 191, 'type': 'OAuthException', 'fbtrace_id': '123Abc' } }) def test_login(self): with self.assertRaises(AuthCanceled) as cm: self.do_login() self.assertIn('error', cm.exception.response.json()) def test_partial_pipeline(self): with self.assertRaises(AuthCanceled) as cm: self.do_partial_pipeline() self.assertIn('error', cm.exception.response.json()) social-auth-core-3.1.0/social_core/tests/backends/test_broken.py0000644000175000017500000000221213336542652024625 0ustar omabomab00000000000000import unittest2 as unittest from ...backends.base import BaseAuth from ..strategy import TestStrategy from ..models import TestStorage class BrokenBackendAuth(BaseAuth): name = 'broken' class BrokenBackendTest(unittest.TestCase): def setUp(self): self.backend = BrokenBackendAuth(TestStrategy(TestStorage)) def tearDown(self): self.backend = None def test_auth_url(self): with self.assertRaisesRegexp(NotImplementedError, 'Implement in subclass'): self.backend.auth_url() def test_auth_html(self): with self.assertRaisesRegexp(NotImplementedError, 'Implement in subclass'): self.backend.auth_html() def test_auth_complete(self): with self.assertRaisesRegexp(NotImplementedError, 'Implement in subclass'): self.backend.auth_complete() def test_get_user_details(self): with self.assertRaisesRegexp(NotImplementedError, 'Implement in subclass'): self.backend.get_user_details(None) social-auth-core-3.1.0/social_core/tests/backends/test_angel.py0000644000175000017500000000210113336542652024430 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class AngelOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.angel.AngelOAuth2' user_data_url = 'https://api.angel.co/1/me/' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps({ 'facebook_url': 'http://www.facebook.com/foobar', 'bio': None, 'name': 'Foo Bar', 'roles': [], 'github_url': None, 'angellist_url': 'https://angel.co/foobar', 'image': 'https://graph.facebook.com/foobar/picture?type=square', 'linkedin_url': None, 'locations': [], 'twitter_url': None, 'what_ive_built': None, 'dribbble_url': None, 'behance_url': None, 'blog_url': None, 'aboutme_url': None, 'follower_count': 0, 'online_bio_url': None, 'id': 101010 }) expected_username = 'foobar' def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_twitch.py0000644000175000017500000000201213336542652024645 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class TwitchOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.twitch.TwitchOAuth2' user_data_url = 'https://api.twitch.tv/kraken/user/' expected_username = 'test_user1' access_token_body = json.dumps({ 'access_token': 'foobar', }) user_data_body = json.dumps({ 'type': 'user', 'name': 'test_user1', 'created_at': '2011-06-03T17:49:19Z', 'updated_at': '2012-06-18T17:19:57Z', '_links': { 'self': 'https://api.twitch.tv/kraken/users/test_user1' }, 'logo': 'http://static-cdn.jtvnw.net/jtv_user_pictures/' 'test_user1-profile_image-62e8318af864d6d7-300x300.jpeg', '_id': 22761313, 'display_name': 'test_user1', 'email': 'asdf@asdf.com', 'partnered': True, 'bio': 'test bio woo I\'m a test user' }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_qiita.py0000644000175000017500000000105613336542652024461 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class QiitaOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.qiita.QiitaOAuth2' user_data_url = 'https://qiita.com/api/v2/authenticated_user' expected_username = 'foobar' access_token_body = json.dumps({ 'token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps({ 'id': 'foobar', 'name': 'Foo Bar' }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_taobao.py0000644000175000017500000000135413336542652024620 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class TaobaoOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.taobao.TAOBAOAuth' user_data_url = 'https://eco.taobao.com/router/rest' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps({ 'w2_expires_in': 0, 'taobao_user_id': '1', 'taobao_user_nick': 'foobar', 'w1_expires_in': 1800, 're_expires_in': 0, 'r2_expires_in': 0, 'expires_in': 86400, 'r1_expires_in': 1800 }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_live.py0000644000175000017500000000165113336542652024312 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class LiveOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.live.LiveOAuth2' user_data_url = 'https://apis.live.net/v5.0/me' expected_username = 'FooBar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps({ 'first_name': 'Foo', 'last_name': 'Bar', 'name': 'Foo Bar', 'locale': 'en_US', 'gender': None, 'emails': { 'personal': None, 'account': 'foobar@live.com', 'business': None, 'preferred': 'foobar@live.com' }, 'link': 'https://profile.live.com/', 'updated_time': '2013-03-17T05:51:30+0000', 'id': '1010101010101010' }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_elixir.py0000644000175000017500000001262313432622240024635 0ustar omabomab00000000000000import unittest2 from .oauth import OAuth1Test, OAuth2Test from .open_id_connect import OpenIdConnectTestMixin class ElixirOpenIdConnectTest(OpenIdConnectTestMixin, OAuth2Test): backend_path = 'social_core.backends.elixir.ElixirOpenIdConnect' issuer = 'https://login.elixir-czech.org/oidc/' openid_config_body = """ { "claims_supported": [ "sub", "name", "preferred_username", "given_name", "family_name", "middle_name", "nickname", "profile", "picture", "website", "gender", "zoneinfo", "locale", "updated_at", "birthdate", "email", "email_verified", "phone_number", "phone_number_verified", "address" ], "op_policy_uri": "https://login.elixir-czech.org/oidc/about", "subject_types_supported": [ "public", "pairwise" ], "request_parameter_supported": true, "userinfo_signing_alg_values_supported": [ "HS256", "HS384", "HS512", "RS256", "RS384", "RS512", "ES256", "ES384", "ES512", "PS256", "PS384", "PS512" ], "revocation_endpoint": "https://login.elixir-czech.org/oidc/revoke", "issuer": "https://login.elixir-czech.org/oidc/", "id_token_encryption_enc_values_supported": [ "A256CBC+HS512", "A256GCM", "A192GCM", "A128GCM", "A128CBC-HS256", "A192CBC-HS384", "A256CBC-HS512", "A128CBC+HS256" ], "require_request_uri_registration": false, "grant_types_supported": [ "authorization_code", "implicit", "urn:ietf:params:oauth:grant-type:jwt-bearer", "client_credentials", "urn:ietf:params:oauth:grant_type:redelegate", "urn:ietf:params:oauth:grant-type:device_code" ], "token_endpoint": "https://login.elixir-czech.org/oidc/token", "request_uri_parameter_supported": false, "service_documentation": "https://login.elixir-czech.org/oidc/about", "registration_endpoint": "https://login.elixir-czech.org/oidc/register", "jwks_uri": "https://login.elixir-czech.org/oidc/jwk", "userinfo_encryption_alg_values_supported": [ "RSA-OAEP", "RSA-OAEP-256", "RSA1_5" ], "scopes_supported": [], "token_endpoint_auth_methods_supported": [ "client_secret_post", "client_secret_basic", "client_secret_jwt", "private_key_jwt", "none" ], "userinfo_encryption_enc_values_supported": [ "A256CBC+HS512", "A256GCM", "A192GCM", "A128GCM", "A128CBC-HS256", "A192CBC-HS384", "A256CBC-HS512", "A128CBC+HS256" ], "claim_types_supported": [ "normal" ], "request_object_encryption_enc_values_supported": [ "A256CBC+HS512", "A256GCM", "A192GCM", "A128GCM", "A128CBC-HS256", "A192CBC-HS384", "A256CBC-HS512", "A128CBC+HS256" ], "claims_parameter_supported": false, "id_token_encryption_alg_values_supported": [ "RSA-OAEP", "RSA-OAEP-256", "RSA1_5" ], "code_challenge_methods_supported": [ "plain", "S256" ], "token_endpoint_auth_signing_alg_values_supported": [ "HS256", "HS384", "HS512", "RS256", "RS384", "RS512", "ES256", "ES384", "ES512", "PS256", "PS384", "PS512" ], "userinfo_endpoint": "https://login.elixir-czech.org/oidc/userinfo", "introspection_endpoint": "https://login.elixir-czech.org/oidc/introspect", "id_token_signing_alg_values_supported": [ "HS256", "HS384", "HS512", "RS256", "RS384", "RS512", "ES256", "ES384", "ES512", "PS256", "PS384", "PS512", "none" ], "device_authorization_endpoint": "https://login.elixir-czech.org/oidc/devicecode", "op_tos_uri": "https://login.elixir-czech.org/oidc/about", "request_object_encryption_alg_values_supported": [ "RSA-OAEP", "RSA-OAEP-256", "RSA1_5" ], "request_object_signing_alg_values_supported": [ "HS256", "HS384", "HS512", "RS256", "RS384", "RS512", "ES256", "ES384", "ES512", "PS256", "PS384", "PS512" ], "response_types_supported": [ "code", "token" ], "end_session_endpoint": "https://login.elixir-czech.org/oidc/endsession", "authorization_endpoint": "https://login.elixir-czech.org/oidc/authorize" } """ social-auth-core-3.1.0/social_core/tests/backends/test_coinbase.py0000644000175000017500000000164613336542652025142 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class CoinbaseOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.coinbase.CoinbaseOAuth2' user_data_url = 'https://api.coinbase.com/v2/user' expected_username = 'satoshi_nakomoto' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps({ "data": { "id": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", 'name': "Satoshi Nakamoto", "username": "satoshi_nakomoto", "profile_location": None, "profile_bio": None, "profile_url": "https://coinbase.com/satoshi_nakomoto", "avatar_url": None, "resource": "user", "resource_path": "/v2/user" } }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_foursquare.py0000644000175000017500000000777413336542652025563 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class FoursquareOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.foursquare.FoursquareOAuth2' user_data_url = 'https://api.foursquare.com/v2/users/self' expected_username = 'FooBar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps({ 'notifications': [{ 'item': { 'unreadCount': 0 }, 'type': 'notificationTray' }], 'meta': { 'errorType': 'deprecated', 'code': 200, 'errorDetail': 'Please provide an API version to avoid future ' 'errors.See http://bit.ly/vywCav' }, 'response': { 'user': { 'photo': 'https://is0.4sqi.net/userpix_thumbs/' 'BYKIT01VN4T4BISN.jpg', 'pings': False, 'homeCity': 'Foo, Bar', 'id': '1010101', 'badges': { 'count': 0, 'items': [] }, 'friends': { 'count': 1, 'groups': [{ 'count': 0, 'items': [], 'type': 'friends', 'name': 'Mutual friends' }, { 'count': 1, 'items': [{ 'bio': '', 'gender': 'male', 'firstName': 'Baz', 'relationship': 'friend', 'photo': 'https://is0.4sqi.net/userpix_thumbs/' 'BYKIT01VN4T4BISN.jpg', 'lists': { 'groups': [{ 'count': 1, 'items': [], 'type': 'created' }] }, 'homeCity': 'Baz, Qux', 'lastName': 'Qux', 'tips': { 'count': 0 }, 'id': '10101010' }], 'type': 'others', 'name': 'Other friends' }] }, 'referralId': 'u-1010101', 'tips': { 'count': 0 }, 'type': 'user', 'todos': { 'count': 0 }, 'bio': '', 'relationship': 'self', 'lists': { 'groups': [{ 'count': 1, 'items': [], 'type': 'created' }] }, 'photos': { 'count': 0, 'items': [] }, 'checkinPings': 'off', 'scores': { 'max': 0, 'checkinsCount': 0, 'goal': 50, 'recent': 0 }, 'checkins': { 'count': 0 }, 'firstName': 'Foo', 'gender': 'male', 'contact': { 'email': 'foo@bar.com' }, 'lastName': 'Bar', 'following': { 'count': 0 }, 'requests': { 'count': 0 }, 'mayorships': { 'count': 0, 'items': [] } } } }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_box.py0000644000175000017500000000434613336542652024147 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class BoxOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.box.BoxOAuth2' user_data_url = 'https://api.box.com/2.0/users/me' expected_username = 'sean+awesome@box.com' access_token_body = json.dumps({ 'access_token': 'T9cE5asGnuyYCCqIZFoWjFHvNbvVqHjl', 'expires_in': 3600, 'restricted_to': [], 'token_type': 'bearer', 'refresh_token': 'J7rxTiWOHMoSC1isKZKBZWizoRXjkQzig5C6jFgCVJ9bU' 'nsUfGMinKBDLZWP9BgR' }) user_data_body = json.dumps({ 'type': 'user', 'id': '181216415', 'name': 'sean rose', 'login': 'sean+awesome@box.com', 'created_at': '2012-05-03T21:39:11-07:00', 'modified_at': '2012-11-14T11:21:32-08:00', 'role': 'admin', 'language': 'en', 'space_amount': 11345156112, 'space_used': 1237009912, 'max_upload_size': 2147483648, 'tracking_codes': [], 'can_see_managed_users': True, 'is_sync_enabled': True, 'status': 'active', 'job_title': '', 'phone': '6509241374', 'address': '', 'avatar_url': 'https://www.box.com/api/avatar/large/181216415', 'is_exempt_from_device_limits': False, 'is_exempt_from_login_verification': False, 'enterprise': { 'type': 'enterprise', 'id': '17077211', 'name': 'seanrose enterprise' } }) refresh_token_body = json.dumps({ 'access_token': 'T9cE5asGnuyYCCqIZFoWjFHvNbvVqHjl', 'expires_in': 3600, 'restricted_to': [], 'token_type': 'bearer', 'refresh_token': 'J7rxTiWOHMoSC1isKZKBZWizoRXjkQzig5C6jFgCVJ9b' 'UnsUfGMinKBDLZWP9BgR' }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() def refresh_token_arguments(self): uri = self.strategy.build_absolute_uri('/complete/box/') return {'redirect_uri': uri} def test_refresh_token(self): user, social = self.do_refresh_token() self.assertEqual(social.extra_data['access_token'], 'T9cE5asGnuyYCCqIZFoWjFHvNbvVqHjl') social-auth-core-3.1.0/social_core/tests/backends/test_flat.py0000644000175000017500000000211313342001632024254 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class FlatOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.flat.FlatOAuth2' user_data_url = 'https://api.flat.io/v2/me' expected_username = 'vincent' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps({ "id": "541a137946fd04d57cb2e3c0", "username": "vincent", "name": "Vincent Foo", "printableName": "Vincent Foo", "bio": "Foo bio", "instruments": [], "picture": "https://flat-prod-public.s3.amazonaws.com/00000000/a0d2cb86-ab1e-4fdb-9286-dd94aa6d386c.jpeg", "registrationDate": "2014-09-17T23:04:25.042Z", "htmlUrl": "https://flat.io/vincent", "starredScoresCount": 85, "likedScoresCount": 85, "followersCount": 183, "followingCount": 52, "ownedPublicScoresCount": 15, "isPowerUser": True }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline()social-auth-core-3.1.0/social_core/tests/backends/base.py0000644000175000017500000001353213336542652023227 0ustar omabomab00000000000000import unittest2 as unittest import requests from httpretty import HTTPretty from ...utils import module_member, parse_qs, PARTIAL_TOKEN_SESSION_NAME from ...backends.utils import user_backends_data, load_backends from ..strategy import TestStrategy from ..models import User, TestUserSocialAuth, TestNonce, \ TestAssociation, TestCode, TestStorage class BaseBackendTest(unittest.TestCase): backend = None backend_path = None name = None complete_url = '' raw_complete_url = '/complete/{0}' def setUp(self): HTTPretty.enable() Backend = module_member(self.backend_path) self.strategy = TestStrategy(TestStorage) self.backend = Backend(self.strategy, redirect_uri=self.complete_url) self.name = self.backend.name.upper().replace('-', '_') self.complete_url = self.strategy.build_absolute_uri( self.raw_complete_url.format(self.backend.name) ) backends = (self.backend_path, 'social_core.tests.backends.test_broken.BrokenBackendAuth') self.strategy.set_settings({ 'SOCIAL_AUTH_AUTHENTICATION_BACKENDS': backends }) self.strategy.set_settings(self.extra_settings()) # Force backends loading to trash PSA cache load_backends(backends, force_load=True) User.reset_cache() TestUserSocialAuth.reset_cache() TestNonce.reset_cache() TestAssociation.reset_cache() TestCode.reset_cache() def tearDown(self): HTTPretty.disable() self.backend = None self.strategy = None self.name = None self.complete_url = None User.reset_cache() TestUserSocialAuth.reset_cache() TestNonce.reset_cache() TestAssociation.reset_cache() TestCode.reset_cache() def extra_settings(self): return {} def do_start(self): raise NotImplementedError('Implement in subclass') def do_login(self): user = self.do_start() username = self.expected_username self.assertEqual(user.username, username) self.assertEqual(self.strategy.session_get('username'), username) self.assertEqual(self.strategy.get_user(user.id), user) self.assertEqual(self.backend.get_user(user.id), user) user_backends = user_backends_data( user, self.strategy.get_setting('SOCIAL_AUTH_AUTHENTICATION_BACKENDS'), self.strategy.storage ) self.assertEqual(len(list(user_backends.keys())), 3) self.assertEqual('associated' in user_backends, True) self.assertEqual('not_associated' in user_backends, True) self.assertEqual('backends' in user_backends, True) self.assertEqual(len(user_backends['associated']), 1) self.assertEqual(len(user_backends['not_associated']), 1) self.assertEqual(len(user_backends['backends']), 2) return user def pipeline_settings(self): self.strategy.set_settings({ 'SOCIAL_AUTH_PIPELINE': ( 'social_core.pipeline.social_auth.social_details', 'social_core.pipeline.social_auth.social_uid', 'social_core.pipeline.social_auth.auth_allowed', 'social_core.tests.pipeline.ask_for_password', 'social_core.tests.pipeline.ask_for_slug', 'social_core.pipeline.social_auth.social_user', 'social_core.pipeline.user.get_username', 'social_core.pipeline.social_auth.associate_by_email', 'social_core.pipeline.user.create_user', 'social_core.pipeline.social_auth.associate_user', 'social_core.pipeline.social_auth.load_extra_data', 'social_core.tests.pipeline.set_password', 'social_core.tests.pipeline.set_slug', 'social_core.pipeline.user.user_details' ) }) def pipeline_handlers(self, url): HTTPretty.register_uri(HTTPretty.GET, url, status=200, body='foobar') HTTPretty.register_uri(HTTPretty.POST, url, status=200) def pipeline_password_handling(self, url): password = 'foobar' requests.get(url) requests.post(url, data={'password': password}) data = parse_qs(HTTPretty.last_request.body) self.assertEqual(data['password'], password) self.strategy.session_set('password', data['password']) return password def pipeline_slug_handling(self, url): slug = 'foo-bar' requests.get(url) requests.post(url, data={'slug': slug}) data = parse_qs(HTTPretty.last_request.body) self.assertEqual(data['slug'], slug) self.strategy.session_set('slug', data['slug']) return slug def do_partial_pipeline(self): url = self.strategy.build_absolute_uri('/password') self.pipeline_settings() redirect = self.do_start() self.assertEqual(redirect.url, url) self.pipeline_handlers(url) password = self.pipeline_password_handling(url) token = self.strategy.session_pop(PARTIAL_TOKEN_SESSION_NAME) partial = self.strategy.partial_load(token) self.assertEqual(partial.backend, self.backend.name) redirect = self.backend.continue_pipeline(partial) url = self.strategy.build_absolute_uri('/slug') self.assertEqual(redirect.url, url) self.pipeline_handlers(url) slug = self.pipeline_slug_handling(url) token = self.strategy.session_pop(PARTIAL_TOKEN_SESSION_NAME) partial = self.strategy.partial_load(token) self.assertEqual(partial.backend, self.backend.name) user = self.backend.continue_pipeline(partial) self.assertEqual(user.username, self.expected_username) self.assertEqual(user.slug, slug) self.assertEqual(user.password, password) return user social-auth-core-3.1.0/social_core/tests/backends/test_reddit.py0000644000175000017500000000331213336542652024622 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class RedditOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.reddit.RedditOAuth2' user_data_url = 'https://oauth.reddit.com/api/v1/me.json' expected_username = 'foobar' access_token_body = json.dumps({ 'name': 'foobar', 'created': 1203420772.0, 'access_token': 'foobar-token', 'created_utc': 1203420772.0, 'expires_in': 3600.0, 'link_karma': 34, 'token_type': 'bearer', 'comment_karma': 167, 'over_18': True, 'is_gold': False, 'is_mod': True, 'scope': 'identity', 'has_verified_email': False, 'id': '33bma', 'refresh_token': 'foobar-refresh-token' }) user_data_body = json.dumps({ 'name': 'foobar', 'created': 1203420772.0, 'created_utc': 1203420772.0, 'link_karma': 34, 'comment_karma': 167, 'over_18': True, 'is_gold': False, 'is_mod': True, 'has_verified_email': False, 'id': '33bma' }) refresh_token_body = json.dumps({ 'access_token': 'foobar-new-token', 'token_type': 'bearer', 'expires_in': 3600.0, 'refresh_token': 'foobar-new-refresh-token', 'scope': 'identity' }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() def refresh_token_arguments(self): uri = self.strategy.build_absolute_uri('/complete/reddit/') return {'redirect_uri': uri} def test_refresh_token(self): user, social = self.do_refresh_token() self.assertEqual(social.extra_data['access_token'], 'foobar-new-token') social-auth-core-3.1.0/social_core/tests/backends/test_dummy.py0000644000175000017500000001117613336542652024511 0ustar omabomab00000000000000import json import datetime import time from httpretty import HTTPretty from ...actions import do_disconnect from ...backends.oauth import BaseOAuth2 from ...exceptions import AuthForbidden from ..models import User from .oauth import OAuth2Test class DummyOAuth2(BaseOAuth2): name = 'dummy' AUTHORIZATION_URL = 'http://dummy.com/oauth/authorize' ACCESS_TOKEN_URL = 'http://dummy.com/oauth/access_token' REVOKE_TOKEN_URL = 'https://dummy.com/oauth/revoke' REVOKE_TOKEN_METHOD = 'GET' GET_ALL_EXTRA_DATA = False EXTRA_DATA = [ ('id', 'id'), ('expires', 'expires'), ('empty', 'empty', True), 'url' ] def get_user_details(self, response): """Return user details from Github account""" return {'username': response.get('username'), 'email': response.get('email', ''), 'first_name': response.get('first_name', ''), 'last_name': response.get('last_name', '')} def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" return self.get_json('http://dummy.com/user', params={ 'access_token': access_token }) class Dummy2OAuth2(DummyOAuth2): GET_ALL_EXTRA_DATA = True class DummyOAuth2Test(OAuth2Test): backend_path = 'social_core.tests.backends.test_dummy.DummyOAuth2' user_data_url = 'http://dummy.com/user' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps({ 'id': 1, 'username': 'foobar', 'url': 'http://dummy.com/user/foobar', 'first_name': 'Foo', 'last_name': 'Bar', 'email': 'foo@bar.com' }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() def test_tokens(self): user = self.do_login() self.assertEqual(user.social[0].access_token, 'foobar') def test_revoke_token(self): self.strategy.set_settings({ 'SOCIAL_AUTH_REVOKE_TOKENS_ON_DISCONNECT': True }) self.do_login() user = User.get(self.expected_username) user.password = 'password' HTTPretty.register_uri(self._method(self.backend.REVOKE_TOKEN_METHOD), self.backend.REVOKE_TOKEN_URL, status=200) do_disconnect(self.backend, user) class WhitelistEmailsTest(DummyOAuth2Test): def test_valid_login(self): self.strategy.set_settings({ 'SOCIAL_AUTH_WHITELISTED_EMAILS': ['foo@bar.com'] }) self.do_login() def test_invalid_login(self): self.strategy.set_settings({ 'SOCIAL_AUTH_WHITELISTED_EMAILS': ['foo2@bar.com'] }) with self.assertRaises(AuthForbidden): self.do_login() class WhitelistDomainsTest(DummyOAuth2Test): def test_valid_login(self): self.strategy.set_settings({ 'SOCIAL_AUTH_WHITELISTED_DOMAINS': ['bar.com'] }) self.do_login() def test_invalid_login(self): self.strategy.set_settings({ 'SOCIAL_AUTH_WHITELISTED_EMAILS': ['bar2.com'] }) with self.assertRaises(AuthForbidden): self.do_login() DELTA = datetime.timedelta(days=1) class ExpirationTimeTest(DummyOAuth2Test): user_data_body = json.dumps({ 'id': 1, 'username': 'foobar', 'url': 'http://dummy.com/user/foobar', 'first_name': 'Foo', 'last_name': 'Bar', 'email': 'foo@bar.com', 'expires': time.mktime((datetime.datetime.utcnow() + DELTA).timetuple()) }) def test_expires_time(self): user = self.do_login() social = user.social[0] expiration = social.expiration_timedelta() self.assertEqual(expiration <= DELTA, True) class AllExtraDataTest(DummyOAuth2Test): backend_path = 'social_core.tests.backends.test_dummy.Dummy2OAuth2' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps({ 'id': 1, 'username': 'foobar', 'url': 'http://dummy.com/user/foobar', 'first_name': 'Foo', 'last_name': 'Bar', 'email': 'foo@bar.com', 'not_normally_in_extra_data': 'value' }) def test_get_all_extra_data(self): user = self.do_login() social = user.social[0] self.assertIn('not_normally_in_extra_data', social.extra_data) self.assertEqual(len(social.extra_data), 10) # Includes auth_time. social-auth-core-3.1.0/social_core/tests/backends/test_stackoverflow.py0000644000175000017500000000321313336542652026240 0ustar omabomab00000000000000import json from six.moves.urllib_parse import urlencode from .oauth import OAuth2Test class StackoverflowOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.stackoverflow.StackoverflowOAuth2' user_data_url = 'https://api.stackexchange.com/2.1/me' expected_username = 'foobar' access_token_body = urlencode({ 'access_token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps({ 'items': [{ 'user_id': 101010, 'user_type': 'registered', 'creation_date': 1278525551, 'display_name': 'foobar', 'profile_image': 'http: //www.gravatar.com/avatar/' '5280f15cedf540b544eecc30fcf3027c?' 'd=identicon&r=PG', 'reputation': 547, 'reputation_change_day': 0, 'reputation_change_week': 0, 'reputation_change_month': 0, 'reputation_change_quarter': 65, 'reputation_change_year': 65, 'age': 22, 'last_access_date': 1363544705, 'last_modified_date': 1354035327, 'is_employee': False, 'link': 'http: //stackoverflow.com/users/101010/foobar', 'location': 'Fooland', 'account_id': 101010, 'badge_counts': { 'gold': 0, 'silver': 3, 'bronze': 6 } }], 'quota_remaining': 9997, 'quota_max': 10000, 'has_more': False }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_tumblr.py0000644000175000017500000000331513336542652024657 0ustar omabomab00000000000000import json from six.moves.urllib_parse import urlencode from .oauth import OAuth1Test class TumblrOAuth1Test(OAuth1Test): backend_path = 'social_core.backends.tumblr.TumblrOAuth' user_data_url = 'http://api.tumblr.com/v2/user/info' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) request_token_body = urlencode({ 'oauth_token_secret': 'foobar-secret', 'oauth_token': 'foobar', 'oauth_callback_confirmed': 'true' }) user_data_body = json.dumps({ 'meta': { 'status': 200, 'msg': 'OK' }, 'response': { 'user': { 'following': 1, 'blogs': [{ 'updated': 0, 'description': '', 'drafts': 0, 'title': 'Untitled', 'url': 'http://foobar.tumblr.com/', 'messages': 0, 'tweet': 'N', 'share_likes': True, 'posts': 0, 'primary': True, 'queue': 0, 'admin': True, 'followers': 0, 'ask': False, 'facebook': 'N', 'type': 'public', 'facebook_opengraph_enabled': 'N', 'name': 'foobar' }], 'default_post_format': 'html', 'name': 'foobar', 'likes': 0 } } }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_instagram.py0000644000175000017500000000335313336542652025341 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class InstagramOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.instagram.InstagramOAuth2' user_data_url = 'https://api.instagram.com/v1/users/self' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer', 'meta': { 'code': 200 }, 'data': { 'username': 'foobar', 'bio': '', 'website': '', 'profile_picture': 'http://images.instagram.com/profiles/' 'anonymousUser.jpg', 'full_name': '', 'counts': { 'media': 0, 'followed_by': 2, 'follows': 0 }, 'id': '101010101' }, 'user': { 'username': 'foobar', 'bio': '', 'website': '', 'profile_picture': 'http://images.instagram.com/profiles/' 'anonymousUser.jpg', 'full_name': '', 'id': '101010101' } }) user_data_body = json.dumps({ 'meta': { 'code': 200 }, 'data': { 'username': 'foobar', 'bio': '', 'website': '', 'profile_picture': 'http://images.instagram.com/profiles/' 'anonymousUser.jpg', 'full_name': '', 'counts': { 'media': 0, 'followed_by': 2, 'follows': 0 }, 'id': '101010101' } }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_readability.py0000644000175000017500000000244013336542652025641 0ustar omabomab00000000000000import json from six.moves.urllib_parse import urlencode from .oauth import OAuth1Test class ReadabilityOAuth1Test(OAuth1Test): backend_path = 'social_core.backends.readability.ReadabilityOAuth' user_data_url = 'https://www.readability.com/api/rest/v1/users/_current' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) request_token_body = urlencode({ 'oauth_token_secret': 'foobar-secret', 'oauth_token': 'foobar', 'oauth_callback_confirmed': 'true' }) user_data_body = json.dumps({ 'username': 'foobar', 'first_name': 'Foo', 'last_name': 'Bar', 'has_active_subscription': False, 'tags': [], 'is_publisher': False, 'email_into_address': 'foobar+sharp@inbox.readability.com', 'kindle_email_address': None, 'avatar_url': 'https://secure.gravatar.com/avatar/' '5280f15cedf540b544eecc30fcf3027c?d=' 'https://www.readability.com/media/images/' 'avatar.png&s=36', 'date_joined': '2013-03-18 02:51:02' }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_soundcloud.py0000644000175000017500000000311713336542652025531 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class SoundcloudOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.soundcloud.SoundcloudOAuth2' user_data_url = 'https://api.soundcloud.com/me.json' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps({ 'website': None, 'myspace_name': None, 'public_favorites_count': 0, 'followings_count': 0, 'full_name': 'Foo Bar', 'id': 10101010, 'city': None, 'track_count': 0, 'playlist_count': 0, 'discogs_name': None, 'private_tracks_count': 0, 'followers_count': 0, 'online': True, 'username': 'foobar', 'description': None, 'subscriptions': [], 'kind': 'user', 'quota': { 'unlimited_upload_quota': False, 'upload_seconds_left': 7200, 'upload_seconds_used': 0 }, 'website_title': None, 'primary_email_confirmed': False, 'permalink_url': 'http://soundcloud.com/foobar', 'private_playlists_count': 0, 'permalink': 'foobar', 'upload_seconds_left': 7200, 'country': None, 'uri': 'https://api.soundcloud.com/users/10101010', 'avatar_url': 'https://a1.sndcdn.com/images/' 'default_avatar_large.png?ca77017', 'plan': 'Free' }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_clef.py0000644000175000017500000000116213336542652024261 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class ClefOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.clef.ClefOAuth2' user_data_url = 'https://clef.io/api/v1/info' expected_username = 'test' access_token_body = json.dumps({ 'access_token': 'foobar' }) user_data_body = json.dumps({ 'info': { 'id': '123456789', 'first_name': 'Test', 'last_name': 'User', 'email': 'test@example.com' } }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_nationbuilder.py0000644000175000017500000002043113336542652026207 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class NationBuilderOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.nationbuilder.NationBuilderOAuth2' user_data_url = 'https://foobar.nationbuilder.com/api/v1/people/me' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer', 'created_at': 1422937981, 'expires_in': 2592000 }) user_data_body = json.dumps({ 'person': { 'twitter_followers_count': None, 'last_name': 'Bar', 'rule_violations_count': 0, 'linkedin_id': None, 'recruiter_id': None, 'membership_expires_at': None, 'donations_raised_count': 0, 'last_contacted_at': None, 'prefix': None, 'profile_content_html': None, 'email4': None, 'email2': None, 'availability': None, 'occupation': None, 'user_submitted_address': None, 'could_vote_status': None, 'state_upper_district': None, 'salesforce_id': None, 'van_id': None, 'phone_time': None, 'profile_content': None, 'auto_import_id': None, 'parent_id': None, 'email4_is_bad': False, 'twitter_updated_at': None, 'email3_is_bad': False, 'bio': None, 'party_member': None, 'unsubscribed_at': None, 'fax_number': None, 'last_contacted_by': None, 'active_customer_expires_at': None, 'federal_donotcall': False, 'warnings_count': 0, 'first_supporter_at': '2015-02-02T19:30:28-08:00', 'previous_party': None, 'donations_raised_amount_this_cycle_in_cents': 0, 'call_status_name': None, 'marital_status': None, 'facebook_updated_at': None, 'donations_count': 0, 'note_updated_at': None, 'closed_invoices_count': None, 'profile_headline': None, 'fire_district': None, 'mobile_normalized': None, 'import_id': None, 'last_call_id': None, 'donations_raised_amount_in_cents': 0, 'facebook_address': None, 'is_profile_private': False, 'last_rule_violation_at': None, 'sex': None, 'full_name': 'Foo Bar', 'last_donated_at': None, 'donations_pledged_amount_in_cents': 0, 'primary_email_id': 1, 'media_market_name': None, 'capital_amount_in_cents': 500, 'datatrust_id': None, 'precinct_code': None, 'email3': None, 'religion': None, 'first_prospect_at': None, 'judicial_district': None, 'donations_count_this_cycle': 0, 'work_address': None, 'is_twitter_follower': False, 'email1': 'foobar@gmail.com', 'email': 'foobar@gmail.com', 'contact_status_name': None, 'mobile_opt_in': True, 'twitter_description': None, 'parent': None, 'tags': [], 'first_volunteer_at': None, 'inferred_support_level': None, 'banned_at': None, 'first_invoice_at': None, 'donations_raised_count_this_cycle': 0, 'is_donor': False, 'twitter_location': None, 'email1_is_bad': False, 'legal_name': None, 'language': None, 'registered_at': None, 'call_status_id': None, 'last_invoice_at': None, 'school_sub_district': None, 'village_district': None, 'twitter_name': None, 'membership_started_at': None, 'subnations': [], 'meetup_address': None, 'author_id': None, 'registered_address': None, 'external_id': None, 'twitter_login': None, 'inferred_party': None, 'spent_capital_amount_in_cents': 0, 'suffix': None, 'mailing_address': None, 'is_leaderboardable': True, 'twitter_website': None, 'nbec_guid': None, 'city_district': None, 'church': None, 'is_profile_searchable': True, 'employer': None, 'is_fundraiser': False, 'email_opt_in': True, 'recruits_count': 0, 'email2_is_bad': False, 'county_district': None, 'recruiter': None, 'twitter_friends_count': None, 'facebook_username': None, 'active_customer_started_at': None, 'pf_strat_id': None, 'locale': None, 'twitter_address': None, 'is_supporter': True, 'do_not_call': False, 'profile_image_url_ssl': 'https://d3n8a8pro7vhmx.cloudfront.net' '/assets/icons/buddy.png', 'invoices_amount_in_cents': None, 'username': None, 'donations_amount_in_cents': 0, 'is_volunteer': False, 'civicrm_id': None, 'supranational_district': None, 'precinct_name': None, 'invoice_payments_amount_in_cents': None, 'work_phone_number': None, 'phone': '213.394.4623', 'received_capital_amount_in_cents': 500, 'primary_address': None, 'is_possible_duplicate': False, 'invoice_payments_referred_amount_in_cents': None, 'donations_amount_this_cycle_in_cents': 0, 'priority_level': None, 'first_fundraised_at': None, 'phone_normalized': '2133944623', 'rnc_regid': None, 'twitter_id': None, 'birthdate': None, 'mobile': None, 'federal_district': None, 'donations_to_raise_amount_in_cents': 0, 'support_probability_score': None, 'invoices_count': None, 'nbec_precinct_code': None, 'website': None, 'closed_invoices_amount_in_cents': None, 'home_address': None, 'school_district': None, 'support_level': None, 'demo': None, 'children_count': 0, 'updated_at': '2015-02-02T19:30:28-08:00', 'membership_level_name': None, 'billing_address': None, 'is_ignore_donation_limits': False, 'signup_type': 0, 'precinct_id': None, 'rnc_id': None, 'id': 2, 'ethnicity': None, 'is_survey_question_private': False, 'middle_name': None, 'author': None, 'last_fundraised_at': None, 'state_file_id': None, 'note': None, 'submitted_address': None, 'support_level_changed_at': None, 'party': None, 'contact_status_id': None, 'outstanding_invoices_amount_in_cents': None, 'page_slug': None, 'outstanding_invoices_count': None, 'first_recruited_at': None, 'county_file_id': None, 'first_name': 'Foo', 'facebook_profile_url': None, 'city_sub_district': None, 'has_facebook': False, 'is_deceased': False, 'labour_region': None, 'state_lower_district': None, 'dw_id': None, 'created_at': '2015-02-02T19:30:28-08:00', 'is_prospect': False, 'priority_level_changed_at': None, 'is_mobile_bad': False, 'overdue_invoices_count': None, 'ngp_id': None, 'do_not_contact': False, 'first_donated_at': None, 'turnout_probability_score': None }, 'precinct': None }) def test_login(self): self.strategy.set_settings({ 'SOCIAL_AUTH_NATIONBUILDER_SLUG': 'foobar' }) self.do_login() def test_partial_pipeline(self): self.strategy.set_settings({ 'SOCIAL_AUTH_NATIONBUILDER_SLUG': 'foobar' }) self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_tripit.py0000644000175000017500000001017013336542652024662 0ustar omabomab00000000000000import json from six.moves.urllib_parse import urlencode from .oauth import OAuth1Test class TripitOAuth1Test(OAuth1Test): backend_path = 'social_core.backends.tripit.TripItOAuth' user_data_url = 'https://api.tripit.com/v1/get/profile' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) request_token_body = urlencode({ 'oauth_token_secret': 'foobar-secret', 'oauth_token': 'foobar', 'oauth_callback_confirmed': 'true' }) user_data_content_type = 'text/xml' user_data_body = \ '' \ '1363590451' \ '1040' \ '' \ '' \ '' \ '
foobar@gmail.com
' \ 'false' \ 'true' \ 'true' \ '' \ 'true' \ '' \ '
' \ '
' \ 'true' \ 'false' \ 'foobar' \ 'Foo Bar' \ 'people/foobar' \ 'Foo, Barland' \ '' \ 'https://www.tripit.com/feed/activities/private/' \ 'ignore-this/activities.atom' \ '' \ '' \ 'https://www.tripit.com/feed/alerts/private/' \ 'ignore-this/alerts.atom' \ '' \ '' \ 'webcal://www.tripit.com/feed/ical/private/' \ 'ignore-this/tripit.ics' \ '' \ '
' \ '
' def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() class TripitOAuth1UsernameAlternativesTest(TripitOAuth1Test): user_data_body = \ '' \ '1363590451' \ '1040' \ '' \ '' \ '' \ '
foobar@gmail.com
' \ 'false' \ 'true' \ 'true' \ '' \ 'true' \ '' \ '
' \ '
' \ 'true' \ 'false' \ 'foobar' \ 'Foobar' \ 'people/foobar' \ 'Foo, Barland' \ '' \ 'https://www.tripit.com/feed/activities/private/' \ 'ignore-this/activities.atom' \ '' \ '' \ 'https://www.tripit.com/feed/alerts/private/' \ 'ignore-this/alerts.atom' \ '' \ '' \ 'webcal://www.tripit.com/feed/ical/private/' \ 'ignore-this/tripit.ics' \ '' \ '
' \ '
' social-auth-core-3.1.0/social_core/tests/backends/test_email.py0000644000175000017500000000072413336542652024442 0ustar omabomab00000000000000from .legacy import BaseLegacyTest class EmailTest(BaseLegacyTest): backend_path = 'social_core.backends.email.EmailAuth' expected_username = 'foo' response_body = 'email=foo@bar.com' form = """
""" def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_yandex.py0000644000175000017500000000270013417122006024622 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class YandexOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.yandex.YandexOAuth2' user_data_url = 'https://login.yandex.ru/info' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps({ 'display_name': 'foobar', 'real_name': 'Foo Bar', 'sex': None, 'id': '101010101', 'default_email': 'foobar@yandex.com', 'emails': ['foobar@yandex.com'] }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() class YandexOAuth2TestEmptyEmail(OAuth2Test): """ When user log in to yandex service with social network account (e.g. vk.com), they `default_email` could be empty. """ backend_path = 'social_core.backends.yandex.YandexOAuth2' user_data_url = 'https://login.yandex.ru/info' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps({ 'display_name': 'foobar', 'real_name': 'Foo Bar', 'sex': None, 'id': '101010101', 'default_email': '', 'emails': [] }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_dropbox.py0000644000175000017500000000202013336542652025017 0ustar omabomab00000000000000import json from six.moves.urllib_parse import urlencode from .oauth import OAuth1Test class DropboxOAuth1Test(OAuth1Test): backend_path = 'social_core.backends.dropbox.DropboxOAuth' user_data_url = 'https://api.dropbox.com/1/account/info' expected_username = '10101010' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) request_token_body = urlencode({ 'oauth_token_secret': 'foobar-secret', 'oauth_token': 'foobar', 'oauth_callback_confirmed': 'true' }) user_data_body = json.dumps({ 'referral_link': 'https://www.dropbox.com/referrals/foobar', 'display_name': 'Foo Bar', 'uid': 10101010, 'country': 'US', 'quota_info': { 'shared': 138573, 'quota': 2952790016, 'normal': 157327 }, 'email': 'foo@bar.com' }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_mineid.py0000644000175000017500000000107613336542652024621 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class MineIDOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.mineid.MineIDOAuth2' user_data_url = 'https://www.mineid.org/api/user' expected_username = 'foo@bar.com' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps({ 'email': 'foo@bar.com', 'primary_profile': None, }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_naver.py0000644000175000017500000000236613336542652024472 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class NaverOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.naver.NaverOAuth2' user_data_url = 'https://openapi.naver.com/v1/nid/getUserProfile.xml' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer', }) user_data_content_type = 'text/xml' user_data_body = \ '' \ '' \ '' \ '00' \ 'success' \ '' \ '' \ '' \ '' \ '' \ '' \ '' \ '' \ 'M' \ '' \ '' \ '' \ '' \ '' def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_bitbucket.py0000644000175000017500000001312013336542652025321 0ustar omabomab00000000000000import json from httpretty import HTTPretty from six.moves.urllib_parse import urlencode from ...exceptions import AuthForbidden from .oauth import OAuth1Test, OAuth2Test class BitbucketOAuthMixin(object): user_data_url = 'https://api.bitbucket.org/2.0/user' expected_username = 'foobar' bb_api_user_emails = 'https://api.bitbucket.org/2.0/user/emails' user_data_body = json.dumps({ u'created_on': u'2012-03-29T18:07:38+00:00', u'display_name': u'Foo Bar', u'links': { u'avatar': {u'href': u'https://bitbucket.org/account/foobar/avatar/32/'}, u'followers': {u'href': u'https://api.bitbucket.org/2.0/users/foobar/followers'}, u'following': {u'href': u'https://api.bitbucket.org/2.0/users/foobar/following'}, u'hooks': {u'href': u'https://api.bitbucket.org/2.0/users/foobar/hooks'}, u'html': {u'href': u'https://bitbucket.org/foobar'}, u'repositories': {u'href': u'https://api.bitbucket.org/2.0/repositories/foobar'}, u'self': {u'href': u'https://api.bitbucket.org/2.0/users/foobar'}}, u'location': u'Fooville, Bar', u'type': u'user', u'username': u'foobar', u'uuid': u'{397621dc-0f78-329f-8d6d-727396248e3f}', u'website': u'http://foobar.com' }) emails_body = json.dumps({ u'page': 1, u'pagelen': 10, u'size': 2, u'values': [ { u'email': u'foo@bar.com', u'is_confirmed': True, u'is_primary': True, u'links': { u'self': {u'href': u'https://api.bitbucket.org/2.0/user/emails/foo@bar.com'}}, u'type': u'email' }, { u'email': u'not@confirme.com', u'is_confirmed': False, u'is_primary': False, u'links': {u'self': {u'href': u'https://api.bitbucket.org/2.0/user/emails/not@confirmed.com'}}, u'type': u'email' } ] }) class BitbucketOAuth1Test(BitbucketOAuthMixin, OAuth1Test): backend_path = 'social_core.backends.bitbucket.BitbucketOAuth' request_token_body = urlencode({ 'oauth_token_secret': 'foobar-secret', 'oauth_token': 'foobar', 'oauth_callback_confirmed': 'true' }) access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) def test_login(self): HTTPretty.register_uri(HTTPretty.GET, self.bb_api_user_emails, status=200, body=self.emails_body) self.do_login() def test_partial_pipeline(self): HTTPretty.register_uri(HTTPretty.GET, self.bb_api_user_emails, status=200, body=self.emails_body) self.do_partial_pipeline() class BitbucketOAuth1FailTest(BitbucketOAuth1Test): emails_body = json.dumps({ u'page': 1, u'pagelen': 10, u'size': 1, u'values': [ { u'email': u'foo@bar.com', u'is_confirmed': False, u'is_primary': True, u'links': { u'self': {u'href': u'https://api.bitbucket.org/2.0/user/emails/foo@bar.com'}}, u'type': u'email' } ] }) def test_login(self): self.strategy.set_settings({ 'SOCIAL_AUTH_BITBUCKET_VERIFIED_EMAILS_ONLY': True }) with self.assertRaises(AuthForbidden): super(BitbucketOAuth1FailTest, self).test_login() def test_partial_pipeline(self): self.strategy.set_settings({ 'SOCIAL_AUTH_BITBUCKET_VERIFIED_EMAILS_ONLY': True }) with self.assertRaises(AuthForbidden): super(BitbucketOAuth1FailTest, self).test_partial_pipeline() class BitbucketOAuth2Test(BitbucketOAuthMixin, OAuth2Test): backend_path = 'social_core.backends.bitbucket.BitbucketOAuth2' access_token_body = json.dumps({ 'access_token': 'foobar_access', 'scopes': 'foo_scope', 'expires_in': 3600, 'refresh_token': 'foobar_refresh', 'token_type': 'bearer' }) def test_login(self): HTTPretty.register_uri(HTTPretty.GET, self.bb_api_user_emails, status=200, body=self.emails_body) self.do_login() def test_partial_pipeline(self): HTTPretty.register_uri(HTTPretty.GET, self.bb_api_user_emails, status=200, body=self.emails_body) self.do_partial_pipeline() class BitbucketOAuth2FailTest(BitbucketOAuth2Test): emails_body = json.dumps({ u'page': 1, u'pagelen': 10, u'size': 1, u'values': [ { u'email': u'foo@bar.com', u'is_confirmed': False, u'is_primary': True, u'links': { u'self': {u'href': u'https://api.bitbucket.org/2.0/user/emails/foo@bar.com'}}, u'type': u'email' } ] }) def test_login(self): self.strategy.set_settings({ 'SOCIAL_AUTH_BITBUCKET_OAUTH2_VERIFIED_EMAILS_ONLY': True }) with self.assertRaises(AuthForbidden): super(BitbucketOAuth2FailTest, self).test_login() def test_partial_pipeline(self): self.strategy.set_settings({ 'SOCIAL_AUTH_BITBUCKET_OAUTH2_VERIFIED_EMAILS_ONLY': True }) with self.assertRaises(AuthForbidden): super(BitbucketOAuth2FailTest, self).test_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_chatwork.py0000644000175000017500000000240513336542652025173 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class ChatworkOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.chatwork.ChatworkOAuth2' user_data_url = 'https://api.chatwork.com/v2/me' expected_username = 'hogehoge' access_token_body = json.dumps({ 'access_token': 'pyopyopyopyopyopyopyopyopyopyo', 'token_type': 'Bearer', 'expires_in': '1501138041000', 'refresh_token': 'pyopyopyopyopyopyo', 'scope': 'rooms.all:read_write' }) user_data_body = json.dumps({ 'account_id': 123, 'room_id': 322, 'name': 'Foo Bar', 'chatwork_id': 'hogehoge', 'organization_id': 101, 'organization_name': 'Foo foobar', 'department': 'Support', 'title': 'CMO', 'url': 'http://www.example.com', 'introduction': '', 'mail': 'hogehoge@example.com', 'tel_organization': '', 'tel_extension': '', 'tel_mobile': '', 'skype': '', 'facebook': '', 'twitter': '', 'avatar_image_url': 'https://www.example.com/hogehoge.jpg', 'login_mail': 'hogehoge@example.com' }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_digitalocean.py0000644000175000017500000000172113336542652025774 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class DigitalOceanOAuthTest(OAuth2Test): backend_path = 'social_core.backends.digitalocean.DigitalOceanOAuth' user_data_url = 'https://api.digitalocean.com/v2/account' expected_username = 'sammy@digitalocean.com' access_token_body = json.dumps({ 'access_token': '547cac21118ae7', 'token_type': 'bearer', 'expires_in': 2592000, 'refresh_token': '00a3aae641658d', 'scope': 'read write', 'info': { 'name': 'Sammy Shark', 'email': 'sammy@digitalocean.com' } }) user_data_body = json.dumps({ "account": { 'droplet_limit': 25, 'email': 'sammy@digitalocean.com', 'uuid': 'b6fr89dbf6d9156cace5f3c78dc9851d957381ef', 'email_verified': True } }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_evernote.py0000644000175000017500000000316013336542652025177 0ustar omabomab00000000000000from requests import HTTPError from six.moves.urllib_parse import urlencode from ...exceptions import AuthCanceled from .oauth import OAuth1Test class EvernoteOAuth1Test(OAuth1Test): backend_path = 'social_core.backends.evernote.EvernoteOAuth' expected_username = '101010' access_token_body = urlencode({ 'edam_webApiUrlPrefix': 'https://sandbox.evernote.com/shard/s1/', 'edam_shard': 's1', 'oauth_token': 'foobar', 'edam_expires': '1395118279645', 'edam_userId': '101010', 'edam_noteStoreUrl': 'https://sandbox.evernote.com/shard/s1/notestore' }) request_token_body = urlencode({ 'oauth_token_secret': 'foobar-secret', 'oauth_token': 'foobar', 'oauth_callback_confirmed': 'true' }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() class EvernoteOAuth1CanceledTest(EvernoteOAuth1Test): access_token_status = 401 def test_login(self): with self.assertRaises(AuthCanceled) as cm: self.do_login() self.assertTrue(cm.exception.response is not None) def test_partial_pipeline(self): with self.assertRaises(AuthCanceled) as cm: self.do_partial_pipeline() self.assertTrue(cm.exception.response is not None) class EvernoteOAuth1ErrorTest(EvernoteOAuth1Test): access_token_status = 500 def test_login(self): with self.assertRaises(HTTPError): self.do_login() def test_partial_pipeline(self): with self.assertRaises(HTTPError): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_vk.py0000644000175000017500000000147713336542652024001 0ustar omabomab00000000000000# coding: utf-8 from __future__ import unicode_literals import json from .oauth import OAuth2Test class VKOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.vk.VKOAuth2' user_data_url = 'https://api.vk.com/method/users.get' expected_username = 'durov' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps({ 'response': [{ 'uid': '1', 'first_name': 'Павел', 'last_name': 'Дуров', 'screen_name': 'durov', 'nickname': '', 'photo': "http:\/\/cs7003.vk.me\/v7003815\/22a1\/xgG9fb-IJ3Y.jpg" }] }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_patreon.py0000644000175000017500000000405513336542652025024 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class PatreonOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.patreon.PatreonOAuth2' user_data_url = 'https://api.patreon.com/oauth2/api/current_user' expected_username = 'JohnInterwebs' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer', }) user_data_body = json.dumps({ "data": { "relationships": { "pledges": { "data": [{ "type": "pledge", "id": "123456" }] } }, "attributes": { "last_name": "Interwebs", "is_suspended": False, "has_password": True, "full_name": "John Interwebs", "is_nuked": False, "first_name": "John", "social_connections": { "spotify": None, "discord": None, "twitter": None, "youtube": None, "facebook": None, "deviantart": None, "twitch": None }, "twitter": None, "is_email_verified": True, "facebook_id": None, "email": "john@example.com", "facebook": None, "thumb_url": "https://c8.patreon.com/100/123456", "vanity": None, "about": None, "is_deleted": False, "created": "2017-05-05T05:16:34+00:00", "url": "https://www.patreon.com/user?u=123456", "gender": 0, "youtube": None, "discord_id": None, "image_url": "https://c8.patreon.com/400/123456", "twitch": None }, "type": "user", "id": "123456" } }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_khanacademy.py0000644000175000017500000000151713336542652025621 0ustar omabomab00000000000000import json from six.moves.urllib_parse import urlencode from .oauth import OAuth1Test class KhanAcademyOAuth1Test(OAuth1Test): backend_path = 'social_core.backends.khanacademy.KhanAcademyOAuth1' user_data_url = 'https://www.khanacademy.org/api/v1/user' expected_username = 'foo@bar.com' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) request_token_body = urlencode({ 'oauth_token_secret': 'foobar-secret', 'oauth_token': 'foobar', 'oauth_callback_confirmed': 'true' }) user_data_body = json.dumps({ "email": "foo@bar.com", "user_id": "http://googleid.khanacademy.org/11111111111111", }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_dailymotion.py0000644000175000017500000000110013336542652025670 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class DailymotionOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.dailymotion.DailymotionOAuth2' user_data_url = 'https://api.dailymotion.com/auth/' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps({ 'id': 'foobar', 'screenname': 'foobar' }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_lyft.py0000644000175000017500000000126313336542652024330 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class LyftOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.lyft.LyftOAuth2' user_data_url = 'https://api.lyft.com/v1/profile' access_token_body = json.dumps({ 'access_token': 'atoken_foo', 'refresh_token': 'rtoken_bar', 'token_type': 'bearer', 'expires_in': 3600, 'scope': 'public profile rides.read rides.request', 'id': 'user_foobar' }) user_data_body = json.dumps({ 'id': 'user_foobar' }) expected_username = 'user_foobar' def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_scistarter.py0000644000175000017500000000151013342000533025510 0ustar omabomab00000000000000import json from httpretty import HTTPretty from ...exceptions import AuthFailed from .oauth import OAuth2Test class ScistarterOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.scistarter.SciStarterOAuth2' user_data_url = 'https://scistarter.com/api/user_info' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps({ 'profile_id': 42006, 'user_id': 5, 'url': 'https://scistarter.com/user/foobar', 'result': 'success', 'handle': 'foobar', 'email': 'foo@bar.com', 'first_name': 'foo', 'last_name': 'bar' }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_five_hundred_px.py0000644000175000017500000000754713336542652026536 0ustar omabomab00000000000000import json from six.moves.urllib_parse import urlencode from .oauth import OAuth1Test class FiveHundredPxOAuth1Test(OAuth1Test): backend_path = 'social_core.backends.five_hundred_px.FiveHundredPxOAuth' user_data_url = 'https://api.500px.com/v1/users' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) request_token_body = urlencode({ 'oauth_token_secret': 'foobar-secret', 'oauth_token': 'foobar', 'oauth_callback_confirmed': 'true' }) user_data_body = json.dumps({ 'user': { 'id': 10101010, 'username': 'foobar', 'firstname': '', 'lastname': '', 'birthday': None, 'sex': 1, 'city': '', 'state': '', 'country': '', 'registration_date': '2011-11-11T00:00:00-00:00', 'about': '', 'usertype': 0, 'domain': 'UserName.500px.com', 'fotomoto_on': False, 'locale': 'en', 'show_nude': False, 'allow_sale_requests': 1, 'fullname': 'UserName', 'userpic_url': 'https://graph.facebook.com/v2.7/' '1000000000/picture?height=100&width=100', 'userpic_https_url': 'https://graph.facebook.com/v2.7/' '1000000000/picture?' 'height=100&width=100', 'cover_url': None, 'upgrade_status': 0, 'store_on': False, 'photos_count': 0, 'galleries_count': 0, 'affection': 51, 'in_favorites_count': 0, 'friends_count': 2, 'followers_count': 3, 'analytics_code': None, 'invite_pending': False, 'invite_accepted': False, 'email': 'user@user.com', 'shadow_email': 'user@user.com', 'upload_limit': 20, 'upload_limit_expiry': '2021-11-11T00:00:00-00:00', 'upgrade_type': 0, 'upgrade_status_expiry': '2011-11-21', 'portfolio_enabled': False, 'auth': { 'facebook': 1, 'twitter': 0, 'google_oauth2': 1 }, 'presubmit_for_licensing': None, 'contacts': { 'facebook': '1000000000' }, 'equipment': {}, 'avatars': { 'default': { 'http': 'https://graph.facebook.com/v2.7/' '1000000000/picture?height=100&width=100', 'https': 'https://graph.facebook.com/v2.7/' '1000000000/picture?height=100&width=100' }, 'large': { 'http': 'https://graph.facebook.com/v2.7/' '1000000000/picture?height=100&width=100', 'https': 'https://graph.facebook.com/v2.7/' '1000000000/picture?height=100&width=100' }, 'small': { 'http': 'https://graph.facebook.com/v2.7/' '1000000000/picture?height=100&width=100', 'https': 'https://graph.facebook.com/v2.7/' '1000000000/picture?height=100&width=100' }, 'tiny': { 'http': 'https://graph.facebook.com/v2.7/' '1000000000/picture?height=100&width=100', 'https': 'https://graph.facebook.com/v2.7/' '1000000000/picture?height=100&width=100' } } } }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_mapmyfitness.py0000644000175000017500000001143313336542652026071 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class MapMyFitnessOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.mapmyfitness.MapMyFitnessOAuth2' user_data_url = 'https://oauth2-api.mapmyapi.com/v7.0/user/self/' expected_username = 'FredFlinstone' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'Bearer', 'expires_in': 4000000, 'refresh_token': 'bambaz', 'scope': 'read' }) user_data_body = json.dumps({ 'last_name': 'Flinstone', 'weight': 91.17206637, 'communication': { 'promotions': True, 'newsletter': True, 'system_messages': True }, 'height': 1.778, 'token_type': 'Bearer', 'id': 112233, 'date_joined': '2011-08-26T06:06:19+00:00', 'first_name': 'Fred', 'display_name': 'Fred Flinstone', 'display_measurement_system': 'imperial', 'expires_in': 4000000, '_links': { 'stats': [ { 'href': '/v7.0/user_stats/112233/?' 'aggregate_by_period=month', 'id': '112233', 'name': 'month' }, { 'href': '/v7.0/user_stats/112233/?' 'aggregate_by_period=year', 'id': '112233', 'name': 'year' }, { 'href': '/v7.0/user_stats/112233/?aggregate_by_period=day', 'id': '112233', 'name': 'day' }, { 'href': '/v7.0/user_stats/112233/?' 'aggregate_by_period=week', 'id': '112233', 'name': 'week' }, { 'href': '/v7.0/user_stats/112233/?' 'aggregate_by_period=lifetime', 'id': '112233', 'name': 'lifetime' } ], 'friendships': [ { 'href': '/v7.0/friendship/?from_user=112233' } ], 'privacy': [ { 'href': '/v7.0/privacy_option/3/', 'id': '3', 'name': 'profile' }, { 'href': '/v7.0/privacy_option/3/', 'id': '3', 'name': 'workout' }, { 'href': '/v7.0/privacy_option/3/', 'id': '3', 'name': 'activity_feed' }, { 'href': '/v7.0/privacy_option/1/', 'id': '1', 'name': 'food_log' }, { 'href': '/v7.0/privacy_option/3/', 'id': '3', 'name': 'email_search' }, { 'href': '/v7.0/privacy_option/3/', 'id': '3', 'name': 'route' } ], 'image': [ { 'href': '/v7.0/user_profile_photo/112233/', 'id': '112233', 'name': 'user_profile_photo' } ], 'documentation': [ { 'href': 'https://www.mapmyapi.com/docs/User' } ], 'workouts': [ { 'href': '/v7.0/workout/?user=112233&' 'order_by=-start_datetime' } ], 'deactivation': [ { 'href': '/v7.0/user_deactivation/' } ], 'self': [ { 'href': '/v7.0/user/112233/', 'id': '112233' } ] }, 'location': { 'country': 'US', 'region': 'NC', 'locality': 'Bedrock', 'address': '150 Dinosaur Ln' }, 'last_login': '2014-02-23T22:36:52+00:00', 'email': 'fredflinstone@gmail.com', 'username': 'FredFlinstone', 'sharing': { 'twitter': False, 'facebook': False }, 'scope': 'read', 'refresh_token': 'bambaz', 'last_initial': 'S.', 'access_token': 'foobar', 'gender': 'M', 'time_zone': 'America/Denver', 'birthdate': '1983-04-15' }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_sketchfab.py0000644000175000017500000000122213336542652025277 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class SketchfabOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.sketchfab.SketchfabOAuth2' user_data_url = 'https://sketchfab.com/v2/users/me' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps({ 'uid': '42', 'email': 'foo@bar.com', 'displayName': 'foo bar', 'username': 'foobar', 'apiToken': 'XXX' }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_utils.py0000644000175000017500000000334713336542652024517 0ustar omabomab00000000000000import unittest2 as unittest from ..models import TestStorage from ..strategy import TestStrategy from ...backends.utils import load_backends, get_backend from ...backends.github import GithubOAuth2 from ...exceptions import MissingBackend class BaseBackendUtilsTest(unittest.TestCase): def setUp(self): self.strategy = TestStrategy(storage=TestStorage) def tearDown(self): self.strategy = None class LoadBackendsTest(BaseBackendUtilsTest): def test_load_backends(self): loaded_backends = load_backends(( 'social_core.backends.github.GithubOAuth2', 'social_core.backends.facebook.FacebookOAuth2', 'social_core.backends.flickr.FlickrOAuth' ), force_load=True) keys = list(loaded_backends.keys()) self.assertEqual(keys, ['github', 'facebook', 'flickr']) backends = () loaded_backends = load_backends(backends, force_load=True) self.assertEqual(len(list(loaded_backends.keys())), 0) class GetBackendTest(BaseBackendUtilsTest): def test_get_backend(self): backend = get_backend(( 'social_core.backends.github.GithubOAuth2', 'social_core.backends.facebook.FacebookOAuth2', 'social_core.backends.flickr.FlickrOAuth' ), 'github') self.assertEqual(backend, GithubOAuth2) def test_get_missing_backend(self): with self.assertRaisesRegexp(MissingBackend, 'Missing backend "foobar" entry'): get_backend(('social_core.backends.github.GithubOAuth2', 'social_core.backends.facebook.FacebookOAuth2', 'social_core.backends.flickr.FlickrOAuth'), 'foobar') social-auth-core-3.1.0/social_core/tests/backends/__init__.py0000644000175000017500000000000013336542652024036 0ustar omabomab00000000000000social-auth-core-3.1.0/social_core/tests/backends/test_pinterest.py0000644000175000017500000000244113336542652025366 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class PinterestOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.pinterest.PinterestOAuth2' user_data_url = 'https://api.pinterest.com/v1/me/' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps({ 'id': '4788400174839062', 'first_name': 'Foo', 'last_name': 'Bar', 'username': 'foobar', }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() class PinterestOAuth2BrokenServerResponseTest(OAuth2Test): backend_path = 'social_core.backends.pinterest.PinterestOAuth2' user_data_url = 'https://api.pinterest.com/v1/me/' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps({ 'data': { 'id': '4788400174839062', 'first_name': 'Foo', 'last_name': 'Bar', 'url': 'https://www.pinterest.com/foobar/', } }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_slack.py0000644000175000017500000000363513336542652024454 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class SlackOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.slack.SlackOAuth2' user_data_url = 'https://slack.com/api/users.identity' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps({ 'ok': True, 'user': { 'email': 'foobar@example.com', 'name': 'Foo Bar', 'id': u'123456' }, 'team': { 'id': u'456789' }, 'scope': u'identity.basic,identity.email' }) expected_username = 'foobar' def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() class SlackOAuth2TestTeamName(SlackOAuth2Test): expected_username = 'foobar@Square' user_data_body = json.dumps({ 'ok': True, 'user': { 'email': 'foobar@example.com', 'name': 'Foo Bar', 'id': u'123456' }, 'team': { 'id': u'456789', 'name': u'Square', }, 'scope': u'identity.basic,identity.email,identity.team' }) class SlackOAuth2TestUnicodeTeamName(SlackOAuth2Test): user_data_body = json.dumps({ 'ok': True, 'user': { 'email': 'foobar@example.com', 'name': 'Foo Bar', 'id': u'123456' }, 'team': { 'id': u'456789', 'name': u'Square \u221a team', }, 'scope': u'identity.basic,identity.email,identity.team' }) def test_login(self): self.strategy.set_settings({ 'SOCIAL_AUTH_SLACK_USERNAME_WITH_TEAM': False }) self.do_login() def test_partial_pipeline(self): self.strategy.set_settings({ 'SOCIAL_AUTH_SLACK_USERNAME_WITH_TEAM': False }) self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_uber.py0000644000175000017500000000174513336542652024314 0ustar omabomab00000000000000import json from httpretty import HTTPretty from six.moves.urllib_parse import urlencode from ...exceptions import AuthForbidden from .oauth import OAuth1Test, OAuth2Test class UberOAuth2Test(OAuth2Test): user_data_url = 'https://api.uber.com/v1/me' backend_path = 'social_core.backends.uber.UberOAuth2' expected_username = 'foo@bar.com' user_data_body = json.dumps({ "first_name": "Foo", "last_name": "Bar", "email": "foo@bar.com", "picture": "https://", "promo_code": "barfoo", "uuid": "91d81273-45c2-4b57-8124-d0165f8240c0" }) access_token_body = json.dumps({ "access_token": "EE1IDxytP04tJ767GbjH7ED9PpGmYvL", "token_type": "Bearer", "expires_in": 2592000, "refresh_token": "Zx8fJ8qdSRRseIVlsGgtgQ4wnZBehr", "scope": "profile history request" }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/oauth.py0000644000175000017500000001106013432611366023423 0ustar omabomab00000000000000import requests from httpretty import HTTPretty from six.moves.urllib_parse import urlencode, urlparse from ...utils import parse_qs, url_add_parameters from ..models import User from .base import BaseBackendTest class BaseOAuthTest(BaseBackendTest): backend = None backend_path = None user_data_body = None user_data_url = '' user_data_content_type = 'application/json' access_token_body = None access_token_status = 200 expected_username = '' def extra_settings(self): return {'SOCIAL_AUTH_' + self.name + '_KEY': 'a-key', 'SOCIAL_AUTH_' + self.name + '_SECRET': 'a-secret-key'} def _method(self, method): return {'GET': HTTPretty.GET, 'POST': HTTPretty.POST}[method] def handle_state(self, start_url, target_url): start_query = parse_qs(urlparse(start_url).query) redirect_uri = start_query.get('redirect_uri') if getattr(self.backend, 'STATE_PARAMETER', False): if start_query.get('state'): target_url = url_add_parameters(target_url, { 'state': start_query['state'] }) if redirect_uri and getattr(self.backend, 'REDIRECT_STATE', False): redirect_query = parse_qs(urlparse(redirect_uri).query) if redirect_query.get('redirect_state'): target_url = url_add_parameters(target_url, { 'redirect_state': redirect_query['redirect_state'] }) return target_url def auth_handlers(self, start_url): target_url = self.handle_state(start_url, self.strategy.build_absolute_uri( self.complete_url )) HTTPretty.register_uri(HTTPretty.GET, start_url, status=301, location=target_url) HTTPretty.register_uri(HTTPretty.GET, target_url, status=200, body='foobar') HTTPretty.register_uri(self._method(self.backend.ACCESS_TOKEN_METHOD), uri=self.backend.access_token_url(), status=self.access_token_status, body=self.access_token_body or '', content_type='text/json') if self.user_data_url: HTTPretty.register_uri(HTTPretty.GET, self.user_data_url, body=self.user_data_body or '', content_type=self.user_data_content_type) return target_url def do_start(self): start_url = self.backend.start().url target_url = self.auth_handlers(start_url) response = requests.get(start_url) self.assertEqual(response.url, target_url) self.assertEqual(response.text, 'foobar') self.strategy.set_request_data(parse_qs(urlparse(start_url).query), self.backend) self.strategy.set_request_data(parse_qs(urlparse(target_url).query), self.backend) return self.backend.complete() class OAuth1Test(BaseOAuthTest): request_token_body = None raw_complete_url = '/complete/{0}/?oauth_verifier=bazqux&' \ 'oauth_token=foobar' def request_token_handler(self): HTTPretty.register_uri(self._method(self.backend.REQUEST_TOKEN_METHOD), self.backend.REQUEST_TOKEN_URL, body=self.request_token_body, status=200) def do_start(self): self.request_token_handler() return super(OAuth1Test, self).do_start() class OAuth2Test(BaseOAuthTest): raw_complete_url = '/complete/{0}/?code=foobar' refresh_token_body = '' def refresh_token_arguments(self): return {} def do_refresh_token(self): self.do_login() HTTPretty.register_uri(self._method(self.backend.REFRESH_TOKEN_METHOD), self.backend.refresh_token_url(), status=200, body=self.refresh_token_body) user = list(User.cache.values())[0] social = user.social[0] social.refresh_token(strategy=self.strategy, **self.refresh_token_arguments()) return user, social social-auth-core-3.1.0/social_core/tests/backends/test_zotero.py0000644000175000017500000000127113336542652024673 0ustar omabomab00000000000000from six.moves.urllib_parse import urlencode from .oauth import OAuth1Test class ZoteroOAuth1Test(OAuth1Test): backend_path = 'social_core.backends.zotero.ZoteroOAuth' expected_username = 'FooBar' access_token_body = urlencode({ 'oauth_token': 'foobar', 'oauth_token_secret': 'rodgsNDK4hLJU1504Atk131G', 'userID': '123456_abcdef', 'username': 'FooBar' }) request_token_body = urlencode({ 'oauth_token_secret': 'foobar-secret', 'oauth_token': 'foobar', 'oauth_callback_confirmed': 'true' }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_fitbit.py0000644000175000017500000000302313336542652024627 0ustar omabomab00000000000000import json from six.moves.urllib_parse import urlencode from .oauth import OAuth1Test class FitbitOAuth1Test(OAuth1Test): backend_path = 'social_core.backends.fitbit.FitbitOAuth1' expected_username = 'foobar' access_token_body = urlencode({ 'oauth_token_secret': 'a-secret', 'encoded_user_id': '101010', 'oauth_token': 'foobar' }) request_token_body = urlencode({ 'oauth_token_secret': 'foobar-secret', 'oauth_token': 'foobar', 'oauth_callback_confirmed': 'true' }) user_data_url = 'https://api.fitbit.com/1/user/-/profile.json' user_data_body = json.dumps({ 'user': { 'weightUnit': 'en_US', 'strideLengthWalking': 0, 'displayName': 'foobar', 'weight': 62.6, 'foodsLocale': 'en_US', 'heightUnit': 'en_US', 'locale': 'en_US', 'gender': 'NA', 'memberSince': '2011-12-26', 'offsetFromUTCMillis': -25200000, 'height': 0, 'timezone': 'America/Los_Angeles', 'dateOfBirth': '', 'encodedId': '101010', 'avatar': 'http://www.fitbit.com/images/profile/' 'defaultProfile_100_male.gif', 'waterUnit': 'en_US', 'distanceUnit': 'en_US', 'glucoseUnit': 'en_US', 'strideLengthRunning': 0 } }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_wunderlist.py0000644000175000017500000000134513336542652025553 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class WunderlistOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.wunderlist.WunderlistOAuth2' user_data_url = 'https://a.wunderlist.com/api/v1/user' expected_username = '12345' access_token_body = json.dumps({ 'access_token': 'foobar-token', 'token_type': 'foobar'}) user_data_body = json.dumps({ 'created_at': '2015-01-21T00:56:51.442Z', 'email': 'foo@bar.com', 'id': 12345, 'name': 'foobar', 'revision': 1, 'type': 'user', 'updated_at': '2015-01-21T00:56:51.442Z'}) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_github_enterprise.py0000644000175000017500000002352213336542652027076 0ustar omabomab00000000000000import json from httpretty import HTTPretty from ...exceptions import AuthFailed from .oauth import OAuth2Test class GithubEnterpriseOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.github_enterprise.GithubEnterpriseOAuth2' user_data_url = 'https://www.example.com/api/v3/user' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps({ 'login': 'foobar', 'id': 1, 'avatar_url': 'https://www.example.com/images/error/foobar_happy.gif', 'gravatar_id': 'somehexcode', 'url': 'https://www.example.com/api/v3/users/foobar', 'name': 'monalisa foobar', 'company': 'GitHub', 'blog': 'https://www.example.com/blog', 'location': 'San Francisco', 'email': 'foo@bar.com', 'hireable': False, 'bio': 'There once was...', 'public_repos': 2, 'public_gists': 1, 'followers': 20, 'following': 0, 'html_url': 'https://www.example.com/foobar', 'created_at': '2008-01-14T04:33:35Z', 'type': 'User', 'total_private_repos': 100, 'owned_private_repos': 100, 'private_gists': 81, 'disk_usage': 10000, 'collaborators': 8, 'plan': { 'name': 'Medium', 'space': 400, 'collaborators': 10, 'private_repos': 20 } }) def test_login(self): self.strategy.set_settings({ 'SOCIAL_AUTH_GITHUB_ENTERPRISE_URL': 'https://www.example.com'}) self.strategy.set_settings({ 'SOCIAL_AUTH_GITHUB_ENTERPRISE_API_URL': 'https://www.example.com/api/v3'}) self.do_login() def test_partial_pipeline(self): self.strategy.set_settings({ 'SOCIAL_AUTH_GITHUB_ENTERPRISE_URL': 'https://www.example.com'}) self.strategy.set_settings({ 'SOCIAL_AUTH_GITHUB_ENTERPRISE_API_URL': 'https://www.example.com/api/v3'}) self.do_partial_pipeline() class GithubEnterpriseOAuth2NoEmailTest(GithubEnterpriseOAuth2Test): user_data_body = json.dumps({ 'login': 'foobar', 'id': 1, 'avatar_url': 'https://www.example.com/images/error/foobar_happy.gif', 'gravatar_id': 'somehexcode', 'url': 'https://www.example.com/api/v3/users/foobar', 'name': 'monalisa foobar', 'company': 'GitHub', 'blog': 'https://www.example.com/blog', 'location': 'San Francisco', 'email': '', 'hireable': False, 'bio': 'There once was...', 'public_repos': 2, 'public_gists': 1, 'followers': 20, 'following': 0, 'html_url': 'https://www.example.com/foobar', 'created_at': '2008-01-14T04:33:35Z', 'type': 'User', 'total_private_repos': 100, 'owned_private_repos': 100, 'private_gists': 81, 'disk_usage': 10000, 'collaborators': 8, 'plan': { 'name': 'Medium', 'space': 400, 'collaborators': 10, 'private_repos': 20 } }) def test_login(self): self.strategy.set_settings({ 'SOCIAL_AUTH_GITHUB_ENTERPRISE_URL': 'https://www.example.com'}) self.strategy.set_settings({ 'SOCIAL_AUTH_GITHUB_ENTERPRISE_API_URL': 'https://www.example.com/api/v3'}) url = 'https://www.example.com/api/v3/user/emails' HTTPretty.register_uri(HTTPretty.GET, url, status=200, body=json.dumps(['foo@bar.com']), content_type='application/json') self.do_login() def test_login_next_format(self): self.strategy.set_settings({ 'SOCIAL_AUTH_GITHUB_ENTERPRISE_URL': 'https://www.example.com'}) self.strategy.set_settings({ 'SOCIAL_AUTH_GITHUB_ENTERPRISE_API_URL': 'https://www.example.com/api/v3'}) url = 'https://www.example.com/api/v3/user/emails' HTTPretty.register_uri(HTTPretty.GET, url, status=200, body=json.dumps([{'email': 'foo@bar.com'}]), content_type='application/json') self.do_login() def test_partial_pipeline(self): self.strategy.set_settings({ 'SOCIAL_AUTH_GITHUB_ENTERPRISE_URL': 'https://www.example.com'}) self.strategy.set_settings({ 'SOCIAL_AUTH_GITHUB_ENTERPRISE_API_URL': 'https://www.example.com/api/v3'}) self.do_partial_pipeline() class GithubEnterpriseOrganizationOAuth2Test(GithubEnterpriseOAuth2Test): backend_path = 'social_core.backends.github_enterprise.GithubEnterpriseOrganizationOAuth2' def auth_handlers(self, start_url): url = 'https://www.example.com/api/v3/orgs/foobar/members/foobar' HTTPretty.register_uri(HTTPretty.GET, url, status=204, body='') return super(GithubEnterpriseOrganizationOAuth2Test, self).auth_handlers( start_url ) def test_login(self): self.strategy.set_settings({ 'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_URL': 'https://www.example.com'}) self.strategy.set_settings({ 'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_API_URL': 'https://www.example.com/api/v3'}) self.strategy.set_settings({'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_NAME': 'foobar'}) self.do_login() def test_partial_pipeline(self): self.strategy.set_settings({ 'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_URL': 'https://www.example.com'}) self.strategy.set_settings({ 'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_API_URL': 'https://www.example.com/api/v3'}) self.strategy.set_settings({'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_NAME': 'foobar'}) self.do_partial_pipeline() class GithubEnterpriseOrganizationOAuth2FailTest(GithubEnterpriseOAuth2Test): backend_path = 'social_core.backends.github_enterprise.GithubEnterpriseOrganizationOAuth2' def auth_handlers(self, start_url): url = 'https://www.example.com/api/v3/orgs/foobar/members/foobar' HTTPretty.register_uri(HTTPretty.GET, url, status=404, body='{"message": "Not Found"}', content_type='application/json') return super(GithubEnterpriseOrganizationOAuth2FailTest, self).auth_handlers( start_url ) def test_login(self): self.strategy.set_settings({ 'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_URL': 'https://www.example.com'}) self.strategy.set_settings({ 'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_API_URL': 'https://www.example.com/api/v3'}) self.strategy.set_settings({'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_NAME': 'foobar'}) with self.assertRaises(AuthFailed): self.do_login() def test_partial_pipeline(self): self.strategy.set_settings({ 'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_URL': 'https://www.example.com'}) self.strategy.set_settings({ 'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_API_URL': 'https://www.example.com/api/v3'}) self.strategy.set_settings({'SOCIAL_AUTH_GITHUB_ENTERPRISE_ORG_NAME': 'foobar'}) with self.assertRaises(AuthFailed): self.do_partial_pipeline() class GithubEnterpriseTeamOAuth2Test(GithubEnterpriseOAuth2Test): backend_path = 'social_core.backends.github_enterprise.GithubEnterpriseTeamOAuth2' def auth_handlers(self, start_url): url = 'https://www.example.com/api/v3/teams/123/members/foobar' HTTPretty.register_uri(HTTPretty.GET, url, status=204, body='') return super(GithubEnterpriseTeamOAuth2Test, self).auth_handlers( start_url ) def test_login(self): self.strategy.set_settings({ 'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_URL': 'https://www.example.com'}) self.strategy.set_settings({ 'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_API_URL': 'https://www.example.com/api/v3'}) self.strategy.set_settings({'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_ID': '123'}) self.do_login() def test_partial_pipeline(self): self.strategy.set_settings({ 'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_URL': 'https://www.example.com'}) self.strategy.set_settings({ 'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_API_URL': 'https://www.example.com/api/v3'}) self.strategy.set_settings({'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_ID': '123'}) self.do_partial_pipeline() class GithubEnterpriseTeamOAuth2FailTest(GithubEnterpriseOAuth2Test): backend_path = 'social_core.backends.github_enterprise.GithubEnterpriseTeamOAuth2' def auth_handlers(self, start_url): url = 'https://www.example.com/api/v3/teams/123/members/foobar' HTTPretty.register_uri(HTTPretty.GET, url, status=404, body='{"message": "Not Found"}', content_type='application/json') return super(GithubEnterpriseTeamOAuth2FailTest, self).auth_handlers( start_url ) def test_login(self): self.strategy.set_settings({ 'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_URL': 'https://www.example.com'}) self.strategy.set_settings({ 'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_API_URL': 'https://www.example.com/api/v3'}) self.strategy.set_settings({'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_ID': '123'}) with self.assertRaises(AuthFailed): self.do_login() def test_partial_pipeline(self): self.strategy.set_settings({ 'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_URL': 'https://www.example.com'}) self.strategy.set_settings({ 'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_API_URL': 'https://www.example.com/api/v3'}) self.strategy.set_settings({'SOCIAL_AUTH_GITHUB_ENTERPRISE_TEAM_ID': '123'}) with self.assertRaises(AuthFailed): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_quizlet.py0000644000175000017500000000101213336542652025037 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class QuizletOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.quizlet.QuizletOAuth2' expected_username = 'foo_bar' access_token_body = json.dumps({ "access_token": "EE1IDxytP04tJ767GbjH7ED9PpGmYvL", "token_type": "Bearer", "expires_in": 3600, "scope": "read", 'user_id': 'foo_bar' }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_livejournal.py0000644000175000017500000000716313336542652025711 0ustar omabomab00000000000000import datetime from httpretty import HTTPretty from six.moves.urllib_parse import urlencode from ...exceptions import AuthMissingParameter from .open_id import OpenIdTest JANRAIN_NONCE = datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%SZ') class LiveJournalOpenIdTest(OpenIdTest): backend_path = 'social_core.backends.livejournal.LiveJournalOpenId' expected_username = 'foobar' discovery_body = ''.join([ '', '', '', 'http://specs.openid.net/auth/2.0/signon', 'http://www.livejournal.com/openid/server.bml', 'http://foobar.livejournal.com/', '', '', '' ]) server_response = urlencode({ 'janrain_nonce': JANRAIN_NONCE, 'openid.mode': 'id_res', 'openid.claimed_id': 'http://foobar.livejournal.com/', 'openid.identity': 'http://foobar.livejournal.com/', 'openid.op_endpoint': 'http://www.livejournal.com/openid/server.bml', 'openid.return_to': 'http://myapp.com/complete/livejournal/?' 'janrain_nonce=' + JANRAIN_NONCE, 'openid.response_nonce': JANRAIN_NONCE + 'wGp2rj', 'openid.assoc_handle': '1364932966:ZTiur8sem3r2jzZougMZ:4d1cc3b44e', 'openid.ns': 'http://specs.openid.net/auth/2.0', 'openid.signed': 'mode,claimed_id,identity,op_endpoint,return_to,' 'response_nonce,assoc_handle', 'openid.sig': 'Z8MOozVPTOBhHG5ZS1NeGofxs1Q=', }) server_bml_body = '\n'.join([ 'assoc_handle:1364935340:ZhruPQ7DJ9eGgUkeUA9A:27f8c32464', 'assoc_type:HMAC-SHA1', 'dh_server_public:WzsRyLomvAV3vwvGUrfzXDgfqnTF+m1l3JWb55fyHO7visPT4tmQ' 'iTjqFFnSVAtAOvQzoViMiZQisxNwnqSK4lYexoez1z6pP5ry3pqxJAEYj60vFGvRztict' 'Eo0brjhmO1SNfjK1ppjOymdykqLpZeaL5fsuLtMCwTnR/JQZVA=', 'enc_mac_key:LiOEVlLJSVUqfNvb5zPd76nEfvc=', 'expires_in:1207060', 'ns:http://specs.openid.net/auth/2.0', 'session_type:DH-SHA1', '' ]) def openid_url(self): return super(LiveJournalOpenIdTest, self).openid_url() + '/data/yadis' def post_start(self): self.strategy.remove_from_request_data('openid_lj_user') def _setup_handlers(self): HTTPretty.register_uri( HTTPretty.POST, 'http://www.livejournal.com/openid/server.bml', headers={'Accept-Encoding': 'identity', 'Content-Type': 'application/x-www-form-urlencoded'}, status=200, body=self.server_bml_body ) HTTPretty.register_uri( HTTPretty.GET, 'http://foobar.livejournal.com/', headers={ 'Accept-Encoding': 'identity', 'Accept': 'text/html; q=0.3,' 'application/xhtml+xml; q=0.5,' 'application/xrds+xml' }, status=200, body=self.discovery_body ) def test_login(self): self.strategy.set_request_data({'openid_lj_user': 'foobar'}, self.backend) self._setup_handlers() self.do_login() def test_partial_pipeline(self): self.strategy.set_request_data({'openid_lj_user': 'foobar'}, self.backend) self._setup_handlers() self.do_partial_pipeline() def test_failed_login(self): self._setup_handlers() with self.assertRaises(AuthMissingParameter): self.do_login() social-auth-core-3.1.0/social_core/tests/backends/legacy.py0000644000175000017500000000271113336542652023556 0ustar omabomab00000000000000import requests from httpretty import HTTPretty from ...utils import parse_qs from .base import BaseBackendTest class BaseLegacyTest(BaseBackendTest): form = '' response_body = '' def setUp(self): super(BaseLegacyTest, self).setUp() self.strategy.set_settings({ 'SOCIAL_AUTH_{0}_FORM_URL'.format(self.name): self.strategy.build_absolute_uri('/login/{0}'.format( self.backend.name)) }) def extra_settings(self): return {'SOCIAL_AUTH_{0}_FORM_URL'.format(self.name): '/login/{0}'.format(self.backend.name)} def do_start(self): start_url = self.strategy.build_absolute_uri(self.backend.start().url) HTTPretty.register_uri( HTTPretty.GET, start_url, status=200, body=self.form.format(self.complete_url) ) HTTPretty.register_uri( HTTPretty.POST, self.complete_url, status=200, body=self.response_body, content_type='application/x-www-form-urlencoded' ) response = requests.get(start_url) self.assertEqual(response.text, self.form.format(self.complete_url)) response = requests.post( self.complete_url, data=parse_qs(self.response_body) ) self.strategy.set_request_data(parse_qs(response.text), self.backend) return self.backend.complete() social-auth-core-3.1.0/social_core/tests/backends/test_asana.py0000644000175000017500000000157713336542652024445 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class AsanaOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.asana.AsanaOAuth2' user_data_url = 'https://app.asana.com/api/1.0/users/me' expected_username = 'erlich@bachmanity.com' access_token_body = json.dumps({ 'access_token': 'aviato', 'token_type': 'bearer' }) # https://asana.com/developers/api-reference/users user_data_body = json.dumps({ 'data': { 'id': 12345, 'name': 'Erlich Bachman', 'email': 'erlich@bachmanity.com', 'photo': None, 'workspaces': [ { 'id': 123456, 'name': 'Pied Piper' } ] } }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_arcgis.py0000644000175000017500000000154613336542652024626 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class ArcGISOAuth2Test(OAuth2Test): user_data_url = 'https://www.arcgis.com/sharing/rest/community/self' backend_path = 'social_core.backends.arcgis.ArcGISOAuth2' expected_username = 'gis@rocks.com' user_data_body = json.dumps({ 'first_name': 'Gis', 'last_name': 'Rocks', 'email': 'gis@rocks.com', 'fullName': 'Gis Rocks', 'username': 'gis@rocks.com' }) access_token_body = json.dumps({ 'access_token': 'CM-gcB85taGhRmoI7l3PSGaXUNsaLkTg-dHH7XtA9Dnlin' \ 'PYKBBrIvFzhd1JtDhh7hEwSv_6eLLcLtUqe3gD6i1yaYYF' \ 'pUQJwy8KEujke5AE87tP9XIoMtp4_l320pUL', 'expires_in': 86400 }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_amazon.py0000644000175000017500000000243213336542652024636 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class AmazonOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.amazon.AmazonOAuth2' user_data_url = 'https://www.amazon.com/ap/user/profile' expected_username = 'FooBar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps({ 'user_id': 'amzn1.account.ABCDE1234', 'email': 'foo@bar.com', 'name': 'Foo Bar' }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() class AmazonOAuth2BrokenServerResponseTest(OAuth2Test): backend_path = 'social_core.backends.amazon.AmazonOAuth2' user_data_url = 'https://www.amazon.com/ap/user/profile' expected_username = 'FooBar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps({ 'Request-Id': '02GGTU7CWMNFTV3KH3J6', 'Profile': { 'Name': 'Foo Bar', 'CustomerId': 'amzn1.account.ABCDE1234', 'PrimaryEmail': 'foo@bar.com' } }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_stripe.py0000644000175000017500000000113213336542652024653 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class StripeOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.stripe.StripeOAuth2' access_token_body = json.dumps({ 'stripe_publishable_key': 'pk_test_foobar', 'access_token': 'foobar', 'livemode': False, 'token_type': 'bearer', 'scope': 'read_only', 'refresh_token': 'rt_foobar', 'stripe_user_id': 'acct_foobar' }) expected_username = 'acct_foobar' def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_spotify.py0000644000175000017500000000157013336542652025050 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class SpotifyOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.spotify.SpotifyOAuth2' user_data_url = 'https://api.spotify.com/v1/me' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps({ 'display_name': None, 'external_urls': { 'spotify': 'https://open.spotify.com/user/foobar' }, 'followers': { 'href': None, 'total': 0 }, 'href': 'https://api.spotify.com/v1/users/foobar', 'id': 'foobar', 'images': [], 'type': 'user', 'uri': 'spotify:user:foobar' }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/data/0000755000175000017500000000000013433256445022650 5ustar omabomab00000000000000social-auth-core-3.1.0/social_core/tests/backends/data/saml_response.txt0000644000175000017500000004212713336542652026271 0ustar omabomab00000000000000http://myapp.com/?RelayState=testshib&SAMLResponse=PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c2FtbDJwOlJlc3BvbnNlIHhtbG5zOnNhbWwycD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiBEZXN0aW5hdGlvbj0iaHR0cDovL215YXBwLmNvbSIgSUQ9Il8yNTk2NTFlOTY3ZGIwOGZjYTQ4MjdkODI3YWY1M2RkMCIgSW5SZXNwb25zZVRvPSJURVNUX0lEIiBJc3N1ZUluc3RhbnQ9IjIwMTUtMDUtMDlUMDM6NTc6NDMuNzkyWiIgVmVyc2lvbj0iMi4wIj48c2FtbDI6SXNzdWVyIHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpuYW1laWQtZm9ybWF0OmVudGl0eSI%2BaHR0cHM6Ly9pZHAudGVzdHNoaWIub3JnL2lkcC9zaGliYm9sZXRoPC9zYW1sMjpJc3N1ZXI%2BPHNhbWwycDpTdGF0dXM%2BPHNhbWwycDpTdGF0dXNDb2RlIFZhbHVlPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6c3RhdHVzOlN1Y2Nlc3MiLz48L3NhbWwycDpTdGF0dXM%2BPHNhbWwyOkVuY3J5cHRlZEFzc2VydGlvbiB4bWxuczpzYW1sMj0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiI%2BPHhlbmM6RW5jcnlwdGVkRGF0YSB4bWxuczp4ZW5jPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyMiIElkPSJfMGM0NzYzNzIyOWFkNmEzMTY1OGU0MDc2ZDNlYzBmNmQiIFR5cGU9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jI0VsZW1lbnQiPjx4ZW5jOkVuY3J5cHRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNhZXMxMjgtY2JjIiB4bWxuczp4ZW5jPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyMiLz48ZHM6S2V5SW5mbyB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI%2BPHhlbmM6RW5jcnlwdGVkS2V5IElkPSJfYjZmNmU2YWZjMzYyNGI3NmM1N2JmOWZhODA5YzAzNmMiIHhtbG5zOnhlbmM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jIyI%2BPHhlbmM6RW5jcnlwdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jI3JzYS1vYWVwLW1nZjFwIiB4bWxuczp4ZW5jPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyMiPjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjc2hhMSIgeG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiLz48L3hlbmM6RW5jcnlwdGlvbk1ldGhvZD48ZHM6S2V5SW5mbz48ZHM6WDUwOURhdGE%2BPGRzOlg1MDlDZXJ0aWZpY2F0ZT5NSUlDc0RDQ0FobWdBd0lCQWdJSkFPN0J3ZGpEWmNVV01BMEdDU3FHU0liM0RRRUJCUVVBTUVVeEN6QUpCZ05WQkFZVEFrTkJNUmt3CkZ3WURWUVFJRXhCQ2NtbDBhWE5vSUVOdmJIVnRZbWxoTVJzd0dRWURWUVFLRXhKd2VYUm9iMjR0YzI5amFXRnNMV0YxZEdnd0hoY04KTVRVd05UQTRNRGMxT0RRMldoY05NalV3TlRBM01EYzFPRFEyV2pCRk1Rc3dDUVlEVlFRR0V3SkRRVEVaTUJjR0ExVUVDQk1RUW5KcApkR2x6YUNCRGIyeDFiV0pwWVRFYk1Ca0dBMVVFQ2hNU2NIbDBhRzl1TFhOdlkybGhiQzFoZFhSb01JR2ZNQTBHQ1NxR1NJYjNEUUVCCkFRVUFBNEdOQURDQmlRS0JnUUNxM2cxQ2wrM3VSNXZDbk40SGJnalRnK20zbkhodGVFTXliKyt5Y1pZcmUyYnhVZnNzaEVSNngzM2wKMjN0SGNrUll3bTdNZEJicnAzTHJWb2lPQ2RQYmxUbWwxSWhFUFRDd0tNaEJLdnZXcVR2Z2ZjU1NuUnpBV2tMbFFZU3VzYXl5Wks0bgo5cWNZa1Y1TUZuaTFyYmp4K01yNWFPRW1iNXUzM2FtTUtMd1NUd0lEQVFBQm80R25NSUdrTUIwR0ExVWREZ1FXQkJSUmlCUjZ6UzY2CmZLVm9rcDB5SkhiZ3YzUlltakIxQmdOVkhTTUViakJzZ0JSUmlCUjZ6UzY2ZktWb2twMHlKSGJndjNSWW1xRkpwRWN3UlRFTE1Ba0cKQTFVRUJoTUNRMEV4R1RBWEJnTlZCQWdURUVKeWFYUnBjMmdnUTI5c2RXMWlhV0V4R3pBWkJnTlZCQW9URW5CNWRHaHZiaTF6YjJOcApZV3d0WVhWMGFJSUpBTzdCd2RqRFpjVVdNQXdHQTFVZEV3UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUZCUUFEZ1lFQUp3c01VM1lTCmF5YlZqdUo4VVMwZlVobFBPbE00MFFGQ0dMNHZCM1RFYmIyNE1xOEhyalV3clUwSkZQR2xzOWEyT1l6TjJCM2UzNU5vck11eHMrZ3IKR3RyMnlQNkx2dVgrblY2QTkzd2I0b29HSG9HZkM3VkxseXhTU25zOTM3U1M1UjFwelE0Z1d6Wm1hMktHV0tJQ1dwaDV6UTBBUlZoTAo2Mzk2N21HTG1vST08L2RzOlg1MDlDZXJ0aWZpY2F0ZT48L2RzOlg1MDlEYXRhPjwvZHM6S2V5SW5mbz48eGVuYzpDaXBoZXJEYXRhIHhtbG5zOnhlbmM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jIyI%2BPHhlbmM6Q2lwaGVyVmFsdWU%2BTElQdkVNVUVGeXhrVHowQ2N4QVA5TjV4Y3NYT2V4aVV4cXBvR2VIeVFMV0R5RVBBUDVnZ1daL3NLZ1ViL2xWSk92bCtuQXhSdVhXUlc5dGxSWWx3R2orRVhIOWhIbmdEY1BWMDNqSUJMQnFJbElBL1RmMGw4cVliOHFKRy9ZM0RTS2RQNkwvUURtYXBtTXpFM29YOEJxMW5Ea3YrUWh4cmQwMGVGK2ZMYVQ0PTwveGVuYzpDaXBoZXJWYWx1ZT48L3hlbmM6Q2lwaGVyRGF0YT48L3hlbmM6RW5jcnlwdGVkS2V5PjwvZHM6S2V5SW5mbz48eGVuYzpDaXBoZXJEYXRhIHhtbG5zOnhlbmM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jIyI%2BPHhlbmM6Q2lwaGVyVmFsdWU%2BRVpUWDhHTkM0My9yWStTUVlBMXRudHlUTTVVNkN2dUNCaktsVEVlekZPRjBZZHhCWUdFQVVjYU8xNVNKOXBMemJ1L1h0WGxzTkVMZTdKdEx4RUpwYUxubWFENnIranNWczdLaTBLNHRTMGNBUERDWHV2R1FoMmFOVjVQOGJ3N1JWUGhLOGQwYlJ1RklGR09FOHMwTTZYOUpxWDN4S0MvL1lSbVVoeDlybnU3ZWlwMGh5ZitPaUZiVGR2SDY2NTB2LzQ3aVdKcDNZeFlUV0QyMHBNbVRJMUpwWUEwYjByWVFQRkR0RU93d0JxYktxanRJc3ZYVFJzeXJhQkxvbnFOeHN5dHpEWHEra0JsMXp3WGUvSE5QcUVQblczdnNxaFhZcDVGM3dkWThkKzNCOTRMZlpOdUd4a0p3VDNzdVR0OGY5VHRBSlI4VytBUmtzT2M4eDBVaVNsVG5BNHFHOTBLMTR5dkVoVHcvd2drZjFXV01RT3dpZDNpakFYbUV4MU5MbVZvYUxYb3p4VExkTjN6YnJ6VEJIRXc3R2J3ZEdrdU5pMlhZOW16YUgwaWtGRm51VUxjMHUwc0pycEdGdzlaK0VlUk44RzNVUVZ5MjhtS2g3ZFBwWU5KbzhyajIxZFFaK2JaeUtTUHZablU3REkyakdJRE5US1g2ZkVyVWFINGlOTzN4cUU2Vk90L2d4T3BMNE5VNUhLV0Q0bG93VzcwdUJjVEVQRmhwaThpYUovdTB6YzUvTEhvdVBjMzByc1RLZFc5cmJLL2NWaHNQUHErZzA5WHZpZ0QweTJvN2tOc1pVL25tRXFiSzBKOTBrazhCR3I5cXRSczY4bUJnSURtUHVwUkhwWjM4eXNnU2VZN3V0VlVaSG5tQ0dzTzZ2NDJ6OTVOK05Pb3RCTEVZbFd1ZEdzYnowQWc4VkRDSlY5ak95QW95MDZyL1AyUHBsOFhjdmJza2d2T1BMMWdDNnVYbVJJS1lmOEw4UDJCNXVjN0haK0dtUHNOWXRLS2VKRDFFUHovdCt2NlBIbXNVb3dsSDhSd3FMRHdtMUF4dlNLQTR3UXBlQ0dQd3A5YXRYS0lWMS84NUZzRWMzajVzNjd6VlRybThrVEpydXV2MDZEdFVRZDNMOFdwTkV4cWhQait6RUp6U3RxSG04ckhNMVhNQUVxdVozc0xycTVqLzFSNlpqS0dOdFJCbjhwOE5ERGtrWm0vWTV5TXlJNXJJS3U5bnA3bXdaaEVpeWVHeHdxblV3VVMvUzVDRjNnMHVidnd4eVVnalVvd1ZvTkNqYktBbkdtT2VCSW5abkh0eGdIVUhVOUVlTFdyd2pRc3JtUmpJV0R2RkZQa3l6SzJDL20yaitubmNxc2E1OGRLVXZxcGR1VTRJYnNPQng3UGpXdXRBNmY5bXd6YWxyRU1NK0lGR3VPdk9HMC93eUdzQjZLREV6bldjUC83NkQ4angzaHZFSlAzN3REbFgreGM4Qno5TXdKdkd6VG4xbTdCb2xoR0lzSXlCTys1ZXpXa3RDWVVIUURGVE9wbXA0MDlOWHp6ZUNTUGY1U2NDWG5YYjRPd01ULy9VM1JFUnRRbGMrNmU2WG1JRjhoRkJVc0taUUJsS2ppSDkwZHlzYWlsNmN2V3UyQW55Q3QxbWxXcHFLc0MzU2RTRVZDTG1qRjlUQUFUMEtFSGdZQjg3RjZtZUpTTysvOXkyZkRuYVVvUUlUVzdubnVuSCtkT3dWSGZMU0wyL2N5YTltNlQzR29TSVNMbGJPMVRzalhKclVkZW55OTcvM2tkNmhFQlphdGY1U3NETFQ3SjNsQUVJNDROeXJ0NkIxQWdod2JNdkpqd1JNTXRNdUJLc3ltUytKVzc4UFNEWXQ4MG9waDJQTTc1N0tBNCtUMTAvYnZaQkE5Vk1OdVpqNVV3NXRWMnFIS3dwS0t6ZVVETUFiQlBRaGpYcXlQZzFKa09rd2RQMUpnOHRITjJTelBZQTlmT1htV0pBZGJDS2tMb0F4ZTV6cDZBUzYzS3FXMmFmSUt6SHJ3RTJmS1VtamppeURvMnNuMkJHbWtBaTRzbnpiVzc2SUQvSVgwd044aDBaQ2VRc29vKzdtb1RCMEJxSnBkS1MycXlsUktoc3BSTC9henVQdmxaK1pwckJxdXpJdEZkNFVLMkpzQkp6VXcwZkpxcTV1bk9PZENzVWM3SUU3QTNmZ1NmZ3NBd1R3WFZJMEVoME5ySWZpMkFKV1Z2VFpEMys2eFZ3dS96WWhuVjc0VXkvMFE4Mi8yQWtpSGpFRjNJVGNLWHdTNTB6bWtLakxjZDJqa2h5TUFYMWRoQ0wwZElFMUJoN0RNamVvNC9YbjBqSlpPL3Rrbi9xZmYzc3RNb1BYVG9KTnBIU1RjR2ZheGtaMzJYNCt3Q0xPc0VBRWxlMVZSY0kwUkZyOFhHTSsxWU9BTjBodFdGcFMxaG9kSi9OczJqL1FnUVNEemNpQ1FZeUFDd3lFRWZDZjZybnR0VmJyTlJQZWlmSHhBM3B2UnZ5ZGRhNDE5cXl0ZXI0akJ3cmw3ZUpuVnJ2VEprR2VhU2FRbDdXWk5SQXBscXRnNnZPYmpiMHZDRWlFaFhKbmNzQUhxcXp5QTRGeWFUVGQ2R0FySU9adUNxRWVoWk51T01lOVlrMVpya0VkR3pIalJESWk3Q1BKQk12NEZ4ZHI3bnJvN0I1WEhKb0ZMNE1DSUtOWWU2aWZiTUtYOU5uN1FWdnphUmY2UXlaSW1BWENQZndvU1BkN2x6NXl3UDJLSUIyaGhFMWt5eVZ5YVc5T0praWpUY3dvUnZrSXhIU0RqMXFqeGxueXh0QzhVZ1pNWmlwcGgzQXJpcjRiekIzUDhIbGIzejZ0OW51KzZMemNiN2ZObVo0UHluaU50Vk9OQ0lHbEh4dTBSY3hQK3cwUXNsM1BtTzJLaHBpc2RIanhvSUJ1YVY1NXdoTlFFNmdNNFBrT0xINDc4Rzg4bUxkd2s2RFpkWVl4L2d6RWE3b3ZIL0pReFp2TzRLdFVTNmZjZHJxV2thTFg1cEhkNkdneFBGZ2NFc2Nad1ZqM2hCS0xFQmE5L0dodERINEhzRnNRbmpPZnNDQkNzN0tjRitmTi9oSUdUeHFqTVlKVHJRYmNtdWF5dk9xR3RQMDFPcXltR24rVm5FSVkzKytQcm95SFN3K0Q0b0JIVG1maFNXRmJLZCtuTlVFS3BhRVIxNkdCU256WktQRVRVSmdRWEw5QWJRQ3RXVjFHb0UzRWNnMDZYaVd2aHFHakpGNldtdEU4dHY4Q25rZmxMNm91TDRvNldpbmx2WnNEdkZrS0R6TDkwUTNsWC9NanBtRTFpWU9uYzdISXdEVGwraFRRcHdsYXJiTDVUNGNkZTg1akNwYU0xU3p1TStiQU5zMHlXVDA0ZXJUVFc2cnhlbXFDTHAra202TVVMTlZOcE1CazBiQjJpRU82UlRtc3VpRlhDUU1xdU5xZjdkWXUwTFFCZzQ0MkJzU1pBV1ZrWEVZblduOURLdTRSby8veEFsb2h5VHozWlZmSkhuWVBSdDloSUErRHVUL3c4T2ZzTURIWnlCelUvL0JEa1NiNkxjMHdraVA3QlhIdjBoNVdud2dNWUxlZDBPalR5UWI2aGxpVnQ5b0FjaDRFVy9EZUlBdkpaQ1BYVm1pUFFYTGVsOVJIRko2bXFiYVo0TCtaZG1ONmQwcFZNZ1FveXhmQTR3dEwwYVpiNnFZYkhibjJMd2VBQVZwL3M2TzVlMVExdnZpZDRTWHo0a2l3RW1LSStIeXZEQ1pnekpQQVN5Z1gvWDJFWEZ0NGV3SjVmUFQyVXZmWnhQWlpqMFZGSFpyUFQwWVd2VE16bjUva3hoT09oM2drVGdDSmNwNWVsZnp4cEFPNFl1a0NoNHJXdVNndDRqVUJyaWNYbFdWdWo5U3JSZVhUalNHTktLK202NWovUDllNHRHT0RkMk9BbjNKTVQ3Q3FuaDhreTZpZjVjbmpVMmU3UDhTZnBONGwxWEFiZEZEcGk5bVJYamEyTzR1RWFHNGNvNW4xcWNDT3ZNMWYyblFBY1ZGNUFoSXhueS96TWhmU2l2RXdOQ0Zyd2tBWDRyQVE0WldUNldFakFyUG5jb1Y4Z1VRclhxQVA4NDJmK1lNWWI5RHFncmFicEg1a3ZuMnQzcWRldGJHODJ0QWlTamhPcUxNYW9iU2F4cXdWa1lUOHRTMW9rUUt2MWZoZ2t6elpEOE5IQnVQQzdNVHdXS0VCS2tDRUUzRWRFMXhNQURLd1B1M3NSaGpSaExXZyszZ2srejJtdlU4cTBhTlc0Y3hObUdoekx4eEY0Q3NFNStMQ1cwOWFpUVJOM1VvWmg1aktBZzBiMlh3WHBLS3pycUVTY1BYdnI0L1dWUTMyMm5qRWRvQVdXR0t2WnBKMlRlREo0eDdiT21LVElFc2RHWU1UZzFVaEU2eFFQcnhqS3dWeGFJNVJyaVE4a0xpaGgwa0t0WHQvYTVsSDhzUjVwR0ZISGZ3dlNVb3liQTB1eUVDNnNRVitPbTVReUZmRmpqZHFCOGNpOGxQS1hLTHFCTHJ6bjNmUkh3TmQwbzFiRTg0aGllTkx5UlhZVmhrRCtFNEpGaVd3ZWt3U3VWM3BjQk9ybnRVU3RoWmx6M3hIUURUVGNJNWliOFJyQ2swZEZ6YTgvQmw3VUdtWlUwSXZ2UmdvVXF2TXNHT2dMY3pGWmRpZnJ5aGNiUTY4a2ZzZ3lCMHppdC9MN1BSV3V4RkdYdDFoTVZSVUZ3WXBJS04zVkI3cXVKZlgwamZsU1JaRndMaXdlK3VhYndmTVZ6c2doajUvOXZNNzcwK0JaMGtJcE45NzBTMG5BbHl6R0h0aW1nTUl1RXFhbUt5QTNTQlI1aHZIYmRyNENnTHFUbXIzbFFnWmpnSkNvN1FXYUJWTXdCR0RpdzVOVVhUUnBycWc4U3h2eDlnNWZwbXMrL0o2QjFEelNTM3ZRZzgxdHFRU1ZDWVJpc0Y3M2VqZlFuZk4zcUszd3RJRDkxQnRISmFvMEFaUUdKVFpKOXVsZ0kzV3hzdWR4ejB0VHVpNlJlSWpmSWsxekZRdFpwRExGMnB3NGpTQVdQTlJqNDBYdVIrRzFUVlI3OVFiME9FYkw4RDFoTU5zWmo3MTZNbUhSOTlKaUxNdm1FWHV5a1V4VGhGYjRMTzZVbW1kU3UwTlBpMXQ2NmNkYURpQWhMaVBFTGdUNkZsenA2T2FGSGNSNjRncEtyemtTNDJONEhJeFpNa2R6M0FsYkRhK2pOWHZPR1l3UWl5K0xNNENZWGtrTWtHR3ZTWis5R2xWQ0l5RXBJaXIzbEQ3bmdzZGk4emxGWDYvekNaczlQSUtwZFZlSGJGZi9GS20wV3AreHI0Ykd0R0RrVHR2Nk1Manh2YU8zanFHaUFWeERKVWFkTVBlS2VHSm5uempTdnpKbGdOVHV3c3grRnF5L2dPMkwxMGowWmhDWi92dE9NelVjNjl3cGhKZm9FNzU3V3lOeFJOcThJc0Y1Tkg5Y0x0b3UvbUNxOTc3YnZPSkRrSURCN3lKWEJ6YUhVQkJuSXJra1Qyemg3bGJmUm5SREJUSFZraVZMazVESUxqeC9XL1BSZEZpUUM2SzRmZGx4Y29JbzlMcnM4ZFVWZkt2TTNNYnJ6c1hGT3ZtVVh0K3NsZldvd3UyTC9ndG9mRFhvTUJZZnlEcWIvWlRaRWZ0MC83blliRm1relBEUlZacU5SR0F3YWZVNTU1UjB2SWtNbGR2VjdKUzhNT1BNYWlXQVBpelNLRG4yRzNvcys1MzRFQytaOGZnWmFPVWpZL0xLME9vME9RMmhvNUV6MGNMYWpwUjFINk9FNEhvUm1ydjQzZkFjdGpYc0hYdi81RXg3emdrWk1NZXZhTFNEdjZtcjFGcDk4QXR4L296VTFGVDBoMDUxcVcwR0g2VWpRRXk5aExSZDBBMnFkUTRMZXpReDNvbDFTblhsamt2MG4zTXFlaFozOC94bzZhdHFDdkJtQkc3amlUdXd6YnlVUngzRm1TM0NCNllOYnFON3hPYVRZRnlkOEZDL01nY0xGQmMwS3F4MXllQ2VUd1hucldQb0dvdllVQlYxYjA1cWtIa1d5V0RUaCsveXJFNzF0RjNxbUQvd3F6cUJyNE04NERtWWVuQkdFOWxtb3FIZEMyWnRpK09KVFZKcmlHZWxQQ3RjZnZRaUlQcHdDZ3BFNmg1ekZhRndLajRuZGtBUkRpTC95L1EwWTZxNU5rM1g5RURlTmdjY1pIcFdmOUpKQ3M2a29wdXRtYjdDczIrbVJYdER1S09DaGY5UVUyN3Bmb1NJaklYK3NGdHY1c0hhSms2aHBZMlpzUUhzaTBYbFowc3FMTnQ5ayszdTVnYnBSU1JCczlHaC9BaVY0dkNyYTRkOTh5U0dCdzRSR1FhSStpQ29RaG9YK3lxc3VrYkx6bXJUU3FXMVRXaXJReUlHZ1Q5VnFERE1mUzAxeGdQSlNFSTlIWlp6TGlFVXVGMm1CMi81Y2dqaEFUaWQrdGV1UVB4aldhN2NSc2t5YUhuTENjQURVUU9ESUFPVjJDWXROcnAwY29ZL091S3ZzaXlJT0lacVJ5dE1PMGVNZ1ZJWTBzWmdxeVEycXlubUx0NDBmWmd3SFVyV245Zm9TYTNtMkVRTy9uOS8yU2NuelJWdVZpVnNjM0tCSElQL3AzNlJlSWowTGlNcCtPQ0p3SHlLVW1UeDRBU1V0dXVhWktlRHl1QjlxcXJuUEFNWUVCeElsTGFvdXMzV1pHakIrcW9ub3QvNmk1UE40bUZjbHFDcUxhMGJHbks4ZnJxYy9yd2tuVGV0YUE0c2tXTEw1L21qNEd5MitFQkh3a0x3UXd2K0FKdmZTOXYvNDl1LzY0N1ZFYW15UzdZQ2ZEUHNBQUREQ1FFcWJNQ1h2Ui8xVmEwWi9YUWhoNlkrZUt0MEVpRDdpNmRZODJtQkFoNEJMRmRVV3VGZHVrdUVwaGZ2WXB3N2loVjNxTjB1NFM1NTRXU0dUa0ZsdlpYNG1hbkF4a1g2ekQxS0NWaEFMdEJnSDgzdkhxam9uc0lwOFMydHgwZ0tiYzEreHVaRVppVWlNVVlVdTByQVFsRFcrZHJoN3lVRHZqekFHSnBmTk01eThaMW45em93VzZ5YW5VZWFBNjhSZDd5TUxobFd0NVh6bGhBTVZDZmZYZ0pFelR1YzJEbENVOXNMLzVTVkRaV2N4R1E5aFM1cnJtK2VyQ1Jxd2FJQk1DNUtza0RCZHdOWmh2Q0FCdEpqS2Vla1FUSjd5MFp4SGNhbGVCaU1rbkYwZVRDZzFvUEhPUVZLQ3V3NE94cHRZUS9xS1V0TEFIWFZ2OTlLMGRWcWZDMmpVQWlHQmVYa0t3aGRYTGtJYlZxU0EyZmxraXBBeEhYNnByUEExQjF3eTVab3hPUFg4RVExOW92eXpBbFg1dHU0OXEwWC9PSExFN1o5T1cxenltRXR6ZFpyNXJZbWtFcVdtcHVSNU5jeHFwTWlZam93dUNXZWhubzIyeG5JM09IQ0xDZkFKaHRrcklhL1hPc0tZRFpCRzFJMGJsN2taR2R5cEtUQlhYdXl6WE5WUlU5L005ejhaVytwdG1oZ2NOUzBJS2VaaSs5bFl4cWRlS3lnbldTTTV3czdSYUpmNlRRZTNSaWJZUjFvNkhwRzB2VHpiTEtQZTZnRjJGODdiWlBJei9mcTNLWnZiM3UrSnhZcCtJVjBtQi9VN29YelhRRk1RK3VmWllpNzUxbkx6WlVxRE1ybU53TFJPVUFNUk8rVnJtblkwSVB1cFBVMXc0b0hBb1dnVGRnTk5pNk1uTFQ4V0pmUlhjT0pKMk1lbUc2K2ZNeHNZUU52UVJwa1RGY05vaFV6Y3ZjcHJ3NUV3WEVZQTJzbzczL2MvY3RIRGcreU05YlF4REppUlltRnFydkhYb29hS1JyekxnUjZLVWdoM3ltaWxaQ0lSSm9KbTE3aEtHM1pxTTE0Lzl5OUc5OE9BZjNkVTlqMDk3aUNlaEc3a2VxYXRJQ2hFWmJqbmQ4Y00rS3djN2FtVWp2ekQzQmNvMHl3MDJxT054OWF3OGhSblZiWDZhdkRJbGhySHZ6SU44MzFvUjljRHBwMG1DUEJXZFVDQlNqVGJ1RkZqRC90WElSbGxlT2JraFFKSUdSNlE2U1MxcXkzT29WT1VheFl6THY0U2s3dndrQUMwUitGREVIeVFZbFVhbVVkTWcyUmdwRUdhSVd1V3IxaGNnRm10QmREV2g3ZFBuWTF0U3VKOC95MXp4NkRvN2ZJYmNFenBBK2E0ODNtRG5vemdld3VmaFdqVCsvUS85WlEreFQ5UWJBT1pQSXhHV3VhSXVrVk8zSWxvZDhJM1NGZFJCTHY5ZXBDNzFLeXpSdVlpMktkOHJ5NVNINit1WnMxUHlZUlpRakdDK3Q4VzRtSE82Z1lFRWVXSkJ1UWhnSHdmV2xhZXlWb3hac0NBQVZKRUllT3hPZDZtNW45OHRCUDdHTmgxT1M0eDRCS2FVN1A0UVQzNVVIZW5meE84WWFQUThmbXlobUJhSVJVZklBTVN2ZTJZRFp5SWNNTTkrN0tNSVVabzJ0eXRvYzdCOGVvZzBNaUkrVkpFdFg0c29FRjFSWkhQZVV3NWlCTjI4OTh2MmVTcGNnVUJhWHFzOUN5VlZtTVJQMEtLUDJ1REt4MUdJcUhjS0ZCOXVQVWRkQS9vT3dNa0tVUWsraFZVVDVPbEVMdjd1a0FBUEE0eE4rZkczVmYxeUVKV0FiVGx5dWtGcThjNXBTRkY1cXVHbUgwVmVpQzVvVEFka1VES3Z6WGhWWUs5c3BRYjNVZ1Z0Qld6N1ZScnlOUVVST3BIZU5xeDlhZHA4YWREWCtRSHJUKytYblN4VVI3SVdGanlNTkZJRWlMWmkxdks1UVVrZlRDUU9qdjh2SHdiUi9MRHF3Z3M5bXdsT3pPY0RLdVBVK0dTb2lnVFdRejRWN0N2SHRaVDI3WUdKVG44RFFFM3IzdjB4aWxvODJ2U3VXSDg0WEU3VEJsTUpFb2R5eDNDRngwVUVkc3VhRHBPSEV3UjZYNlUyU0xseERYSXVZeEhlNXh2NjI4bXU0bDRMSnBYUjhkYmljTEZKQW55Q0FVeDJLb2dDamt1cmU4bXNUZktDbG8wamFlN1hNR05PSk15b0ZYbVlHZUh2eGhNUGMzTEtYLy9VY1p0c3p3dFJrQmNFdURXQysvQWNWZVBOSHVOWWI5MEpIcnRucGg1ZDlhL1lpTkpzY1N3QTFwUVZrdW1TQWtPQWdLdWRzcnl3c0N3Zkg1anNydVpHUTJDd1hKRXQzUU4wU2NLUlVnT1NCQ3FYa1BqZDVSVzJuOFZpamt4anovbWptakhCNmk0eHM5NEU2Nzk5STAyaldYNVd3UDZhTFRaTGt5TjhxNDUxT0RmeUZVZEY5WWsyZXQ5VUpsV1NzRFJMSWVCd0ZyQkEyZTdyRWsybWFLVUNCRW5PUWM2bUhVMXQvZ3gzK1VXVVFXbkpMZVUxbWUvbkFEdy96UGUwd3d0Vm9BaERZdDBoR1hQblJydjFoUHRGS01CeWtqckg3a0J5U0R3WDlQMi9XZkNkQlE5K1J4cHRsR2hvRmdpMUs0NVlOeEpEd05wTmd5MDV2WXUzVUtrMkpRYVNGUzcwK0Y1NzluRE5RenZpK0pPRlRsdDFmWDJGNXk5NEV2NHZobWRQSmRVOFVVRjU2Ymx0emxKREVFdmsySlFrOTM0aHpwTXJGZ1d3ZHUxUkxxSEhCN2h2T2hnaHNqV0ZGY01zNjZaRUtWcVhKUytxWWNVMHk0akwySVQrNlF2N2pvQ3BWbUdzUWtGY1FyblhxOUJiOTdaUS96UCtwaldmWTU0UmNRVlMydUU1YURObVVyVkdLK3E0d0xRcUhuRVViT2puSHFFeGlacUtxOVdRaUtUK2c3QS96bVlIQ2k0YzFTejRNVWhHb0t6U2l4aXoxYUNJUEJXdy9vczR2cUVqbXgzOGx6YnV0OWNWbElzeGNkTUpUTERRK3ZOZ0YyY1ZRaVcxRTQ0d3lWcnI3TUFaOE9KRVpFSzlEZWt5MzJQUkFuSkRUVXVqdGFscmJ0T2VOczhyS09uTjcvNFRqUEwvZmRlbEI4bjA4WXdSNXdmbU42VGpGWUhRSDFjbUZmK1AvNUxVMTI4Q1pEYjNQUStxMlFJazV3aE40eGwvcy9lb29pallmeWtDcm5aSEhHWkluTGhoU2pWbk5ISWdTL203VWV0NlhBTDdvZUl5UFRLeHVnbDJzRWtUQzNnZ0tjTnFZR0E5U3ZlYVlaQ00vWHNQRUtQbWs3QmlRNmprWFBKaE1yREd4Vkc0SW9aSDgrYjBrUWJYR2l0Mkw0L3hZdHh1bTVzcFNPSjdsTDltVFpRNnBxM2JOaTEwZU1mZ0ZWaDc3NU5JRlc0SEp3U1FtaTU0bk11blZTQjhxdjZKc0w3SGlsZ2N0ZHFSNThTTjVad1lCa2dOR1hzYjA1QXJWemVXbHh1Y21BSHNPT3dyczFnMzh6bTRZN2ZPZmducmFhV1kxanZZOFlEODZQZThkZzR4cE5paTg3UnNDZk5WK2NKVmMraktFdnpuZVY1Zzd0RmlxZCtsZHp4STlKemdSS2t0WUV6RUpRSVU5M2UvclJaN1lrVkZtNVV1cjVhMWYzcG83T0VtYkJUc2MrQ1FaOGNnYmIvbUphRXJoa3NyL3JURjBNcjNxeDl5SlJWSEJ6YWNWd0dScEFRaURPdnJkWU4xQXBVOTRyR1lrVFVzdWs1YjE1Wll2QVZxRlRzVlVMaS9HY29mbEljMm01Z2RFTFZOblRmdXY1Zlk5S1NlWHFoUU80S0pOYVZmbHAwQ0VKYWFFZFNLUXJJNXRaT2w1RkE4VXZlNmxTWVd5TVk0REl4a1RiT1JoWHVBdzR6b1RTMjgrN3d2TXhydVBkZnlKbUJCTkhQdCtEYmdKNHovcHJZWUhpTmFMTXNZamtQZE44ajNKZDczQXJFZk92Um52MzYxSVVVMFg1RDc1dlRSdlpkbzMzWERzanRlOU4weUo3K2lIQnF1a1FJY2pIVW9ic2RQN0hOajBVYWNSMHIvTmRlVTlGNFBNc1VLY2t6Tk4rZGhyMVI2d1J2R1VZb1pDRWJaWlJMWEt4QnA3SElUNEVQUktHakIvdW1xTFhhMXl6RWx2QW1WQUJhMDFZN3dGdk4wM2Ywb25FbUhTM2w1d1paRmV6cjVibnN5T01XVGxhMU5kaW1ZNXNVeE15VFliZmc4dzB2cXNEc28zWFAxYndLdzZ3M3VIRGQ1UHBSWnVDSnR0eWk0ZzJGeWI0Ymg1UU42ZkdORTI2ekRGN1Y4QmJwZXJLNkFKQ0xTWm5kaDZMMTlPUTBram4xUGpEMGk4c1BZcGFXOWxVeVJkZElPKzRWQS9LemxPUzJ4M2s5VUtUdElsTTBUSVdtZXFIS0dYUVpocGpvVGI2VlNKN203cjZaaVlQMnVsQVVvZmVWL0o2eCtzckxEQXkyQ2ZFNnFrREZ1OU9NWDBBSXVnN3loQUtOMDRyT3hVNk5tcGtjOUZ4bXUvVS9vR3hHdmIzeFVFTDYwdE1sSE9EaWtqY1I5RDJrKzRwbEc1WnV0d0FIY2kwRU02WHRrVEhQOU5QMlRTR1VFN1E5SGYvU0VEc2V0a25hZXhvWmhDczJLWDFMeU5JS0U0N2pkMkR3MTUreDRRVXV0VUFTbzU5Q1lHMVFBeW9BVVhrV3dtbXkzTGdTUWp5T3ZLV25qaE8veWpPd0FyWGd0NFBrSVVnZDQ1N05ReFpMbU41K0J4NVJoQ0FHdkUxYmxOZjlMek9keGJiaG5VZ2Z1RDM5MXVSRkhjS2RYREY3ZmVqb3gveThtaWZJcTRWVzQyajBHQnFOQUtkK0prMnJCMW9hOTRiT2hxcVVzanhqWnlRaGRXTzhNblR6T2tOaGVpZXU2blYxcW5yZ3JHU2huWTNJMlczb29GNFNnczRjZ3drZ2h2dHpFa0xUbU5OUm83RTdudVRuMkxJcmlGSnlvTmZQdUp0aWN0S0JtNzRGZytkWVBTMlIzTzNmOWxBZWxiVWZjbzZGNU9EL3hkS1VuRTh0V3FOMExVcDlWQUptWVZYZFVDaGJ4MjM4MWtDaStLNDJoRzUydFNQYU1hb1dTb0xQY2Zrb24rc1pYdjdEdEtwZi9HTzdhcUMza1pzRGpva29haHJGZGJWSlNTZWhrNGp5K3RzRHplQnJKSjBrMVZrUnJHN1NoVHZjTmd1cjVucVRUTEE5dlJMQmJNTTlhNlI1NEZ0Z1pQOWFKMU1aMEdCcUVpMnF6Ui8yd2tYQlhwcFhZdi9TcU1RV1dhbTVsSHBMVktxaDN4ZHRjNFdmck9mYldsbU1PNXA5Z0JUSFp1YUcxVGFkZXFRVVpKQmZBS01ENFdSR0NsMDFaeDRTVzE0YzZrdnFKdXExL080N215L3RsVHlLWndpYlBkQTNRMVVGd0I3R2Z4anEwaDN2ckxFbUNrS3Vsc0VBUkN6UnZNVjJSVnBVbFpUV240Y1Boc0hjcTNROElHSUYyKy9nOENFSU4vMU8xcVMvMkpXcXlDNmtIb0w4Y2R2R0VHbmkxSTNDTk1JcXhxaHhJL1V0R3REc2VwYmwrSHI0elh4MzZna3BCbXBoT2xkTFVYTHAzVEtibVVZRWJSWHcvZmRmeFQ3WDdZUFhHQ0hHVG1uTzk4WkxDOTA2Zmkvekd2b04rNlpzbCs3MkpWMGxJWEo0V3dZdWxFUmZHbkFDWGNoa0Yzei9ITWR3elcwTUFFaXptQmwvREo2ZUoyU01PSG1Uc25YbElGRDRlcFRrYnFBQ0dpZ2I1UExFdHdQRVRjYkNRckM5YUtTU1FnSTdEZXd1aWlxM2J0Y0RUWkIzeEI5WWxlbmhpU0FXNjIwcmwzc2ZjY3d3eGFSOHBDV2Rzd0x3dmFxcDhjM01PV3RCc2xPcmVTSkNEcWgvdzBYbm1WMFJVWFpNM2JvUmkwVXhsaHVUeDFlM1NTd09pbTlOczNYV3NoTmI4Lzc3VkhnUWhRVFlSUU1NRllYaWRmMElCKzBtSUpocWNoQTlUeUY3dGRjSDhrUUJUSHNEWS96bFpqK3EwNlFMd0JkbTkxc3IyK3VzZmxlaXB3WUMrcmdiNHROVnA3VU5rYkVqTnR6ZWZsTi9VRTlkbHZtT2x6V1dtZkh2NGVkUGkzMmJmeUNRS1d6SGJVVEV3NU0yVFpsZnpNaTFWUjVsaDBxQ1lqaDNITUlmL2MwcHBKd2I1b1lFTnBBenlxbnlmdmlTV3lBYzc2L1l1VWwvb2FVaysrYzBZc2d1TGo5ZGFQdVVvemhoZ3VjSytQRGlNckI0ODU1Mk83VWg0aHRwNmZ3S2dJa1JCTVFIUTd6MmV5WXovV1AwQm9ZZVhjOGc3aUprclhFNzA1bFo1bXhGU0poT3E1WlNleVJSb21pUm41K3VRemM5ZFdWQjBYb2JURXdOc0VRM2FIZ25JY29BczY2UGplUT09PC94ZW5jOkNpcGhlclZhbHVlPjwveGVuYzpDaXBoZXJEYXRhPjwveGVuYzpFbmNyeXB0ZWREYXRhPjwvc2FtbDI6RW5jcnlwdGVkQXNzZXJ0aW9uPjwvc2FtbDJwOlJlc3BvbnNlPg==social-auth-core-3.1.0/social_core/tests/backends/test_flickr.py0000644000175000017500000000125313336542652024623 0ustar omabomab00000000000000from six.moves.urllib_parse import urlencode from .oauth import OAuth1Test class FlickrOAuth1Test(OAuth1Test): backend_path = 'social_core.backends.flickr.FlickrOAuth' expected_username = 'foobar' access_token_body = urlencode({ 'oauth_token_secret': 'a-secret', 'username': 'foobar', 'oauth_token': 'foobar', 'user_nsid': '10101010@N01' }) request_token_body = urlencode({ 'oauth_token_secret': 'foobar-secret', 'oauth_token': 'foobar', 'oauth_callback_confirmed': 'true' }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_twitter.py0000644000175000017500000002225413336542652025057 0ustar omabomab00000000000000import json from six.moves.urllib_parse import urlencode from .oauth import OAuth1Test class TwitterOAuth1Test(OAuth1Test): backend_path = 'social_core.backends.twitter.TwitterOAuth' user_data_url = 'https://api.twitter.com/1.1/account/' \ 'verify_credentials.json' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) request_token_body = urlencode({ 'oauth_token_secret': 'foobar-secret', 'oauth_token': 'foobar', 'oauth_callback_confirmed': 'true' }) user_data_body = json.dumps({ 'follow_request_sent': False, 'profile_use_background_image': True, 'id': 10101010, 'description': 'Foo bar baz qux', 'verified': False, 'entities': { 'description': { 'urls': [] } }, 'profile_image_url_https': 'https://twimg0-a.akamaihd.net/' 'profile_images/532018826/' 'n587119531_1939735_9305_normal.jpg', 'profile_sidebar_fill_color': '252429', 'profile_text_color': '666666', 'followers_count': 77, 'profile_sidebar_border_color': '181A1E', 'location': 'Fooland', 'default_profile_image': False, 'listed_count': 4, 'status': { 'favorited': False, 'contributors': None, 'retweeted_status': { 'favorited': False, 'contributors': None, 'truncated': False, 'source': 'web', 'text': '"Foo foo foo foo', 'created_at': 'Fri Dec 21 18:12:00 +0000 2012', 'retweeted': True, 'in_reply_to_status_id': None, 'coordinates': None, 'id': 101010101010101010, 'entities': { 'user_mentions': [], 'hashtags': [], 'urls': [] }, 'in_reply_to_status_id_str': None, 'place': None, 'id_str': '101010101010101010', 'in_reply_to_screen_name': None, 'retweet_count': 8, 'geo': None, 'in_reply_to_user_id_str': None, 'in_reply_to_user_id': None }, 'truncated': False, 'source': 'web', 'text': 'RT @foo: "Foo foo foo foo', 'created_at': 'Fri Dec 21 18:24:10 +0000 2012', 'retweeted': True, 'in_reply_to_status_id': None, 'coordinates': None, 'id': 101010101010101010, 'entities': { 'user_mentions': [{ 'indices': [3, 10], 'id': 10101010, 'screen_name': 'foo', 'id_str': '10101010', 'name': 'Foo' }], 'hashtags': [], 'urls': [] }, 'in_reply_to_status_id_str': None, 'place': None, 'id_str': '101010101010101010', 'in_reply_to_screen_name': None, 'retweet_count': 8, 'geo': None, 'in_reply_to_user_id_str': None, 'in_reply_to_user_id': None }, 'utc_offset': -10800, 'statuses_count': 191, 'profile_background_color': '1A1B1F', 'friends_count': 151, 'profile_background_image_url_https': 'https://twimg0-a.akamaihd.net/' 'images/themes/theme9/bg.gif', 'profile_link_color': '2FC2EF', 'profile_image_url': 'http://a0.twimg.com/profile_images/532018826/' 'n587119531_1939735_9305_normal.jpg', 'is_translator': False, 'geo_enabled': False, 'id_str': '74313638', 'profile_background_image_url': 'http://a0.twimg.com/images/themes/' 'theme9/bg.gif', 'screen_name': 'foobar', 'lang': 'en', 'profile_background_tile': False, 'favourites_count': 2, 'name': 'Foo', 'notifications': False, 'url': None, 'created_at': 'Tue Sep 15 00:26:17 +0000 2009', 'contributors_enabled': False, 'time_zone': 'Buenos Aires', 'protected': False, 'default_profile': False, 'following': False }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() class TwitterOAuth1IncludeEmailTest(OAuth1Test): backend_path = 'social_core.backends.twitter.TwitterOAuth' user_data_url = 'https://api.twitter.com/1.1/account/' \ 'verify_credentials.json?include_email=true' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) request_token_body = urlencode({ 'oauth_token_secret': 'foobar-secret', 'oauth_token': 'foobar', 'oauth_callback_confirmed': 'true' }) user_data_body = json.dumps({ 'follow_request_sent': False, 'profile_use_background_image': True, 'id': 10101010, 'description': 'Foo bar baz qux', 'verified': False, 'entities': { 'description': { 'urls': [] } }, 'profile_image_url_https': 'https://twimg0-a.akamaihd.net/' 'profile_images/532018826/' 'n587119531_1939735_9305_normal.jpg', 'profile_sidebar_fill_color': '252429', 'profile_text_color': '666666', 'followers_count': 77, 'profile_sidebar_border_color': '181A1E', 'location': 'Fooland', 'default_profile_image': False, 'listed_count': 4, 'status': { 'favorited': False, 'contributors': None, 'retweeted_status': { 'favorited': False, 'contributors': None, 'truncated': False, 'source': 'web', 'text': '"Foo foo foo foo', 'created_at': 'Fri Dec 21 18:12:00 +0000 2012', 'retweeted': True, 'in_reply_to_status_id': None, 'coordinates': None, 'id': 101010101010101010, 'entities': { 'user_mentions': [], 'hashtags': [], 'urls': [] }, 'in_reply_to_status_id_str': None, 'place': None, 'id_str': '101010101010101010', 'in_reply_to_screen_name': None, 'retweet_count': 8, 'geo': None, 'in_reply_to_user_id_str': None, 'in_reply_to_user_id': None }, 'truncated': False, 'source': 'web', 'text': 'RT @foo: "Foo foo foo foo', 'created_at': 'Fri Dec 21 18:24:10 +0000 2012', 'retweeted': True, 'in_reply_to_status_id': None, 'coordinates': None, 'id': 101010101010101010, 'entities': { 'user_mentions': [{ 'indices': [3, 10], 'id': 10101010, 'screen_name': 'foo', 'id_str': '10101010', 'name': 'Foo' }], 'hashtags': [], 'urls': [] }, 'in_reply_to_status_id_str': None, 'place': None, 'id_str': '101010101010101010', 'in_reply_to_screen_name': None, 'retweet_count': 8, 'geo': None, 'in_reply_to_user_id_str': None, 'in_reply_to_user_id': None }, 'utc_offset': -10800, 'statuses_count': 191, 'profile_background_color': '1A1B1F', 'friends_count': 151, 'profile_background_image_url_https': 'https://twimg0-a.akamaihd.net/' 'images/themes/theme9/bg.gif', 'profile_link_color': '2FC2EF', 'profile_image_url': 'http://a0.twimg.com/profile_images/532018826/' 'n587119531_1939735_9305_normal.jpg', 'is_translator': False, 'geo_enabled': False, 'id_str': '74313638', 'profile_background_image_url': 'http://a0.twimg.com/images/themes/' 'theme9/bg.gif', 'screen_name': 'foobar', 'lang': 'en', 'profile_background_tile': False, 'favourites_count': 2, 'name': 'Foo', 'notifications': False, 'url': None, 'created_at': 'Tue Sep 15 00:26:17 +0000 2009', 'contributors_enabled': False, 'time_zone': 'Buenos Aires', 'protected': False, 'default_profile': False, 'following': False, 'email': 'foo@bar.bas' }) def test_login(self): user = self.do_login() self.assertEquals(user.email, 'foo@bar.bas') def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_username.py0000644000175000017500000000074113336542652025171 0ustar omabomab00000000000000from .legacy import BaseLegacyTest class UsernameTest(BaseLegacyTest): backend_path = 'social_core.backends.username.UsernameAuth' expected_username = 'foobar' response_body = 'username=foobar' form = """
""" def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_orbi.py0000644000175000017500000000140213336542652024300 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class OrbiOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.orbi.OrbiOAuth2' user_data_url = 'https://login.orbi.kr/oauth/user/get' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar', }) user_data_body = json.dumps({ 'username': 'foobar', 'first_name': 'Foo', 'last_name': 'Bar', 'name': 'Foo Bar', 'imin': '100000', 'nick': 'foobar', 'photo': 'http://s3.orbi.kr/data/member/wi/wizetdev_132894975780.jpeg', 'sex': 'M', 'birth': '1973-08-03', }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_coursera.py0000644000175000017500000000212313336542652025171 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class CourseraOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.coursera.CourseraOAuth2' user_data_url = \ 'https://api.coursera.org/api/externalBasicProfiles.v1?q=me' expected_username = '560e7ed2076e0d589e88bd74b6aad4b7' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'Bearer', 'expires_in': 1795 }) request_token_body = json.dumps({ 'code': 'foobar-code', 'client_id': 'foobar-client-id', 'client_secret': 'foobar-client-secret', 'redirect_uri': 'http://localhost:8000/accounts/coursera/', 'grant_type': 'authorization_code' }) user_data_body = json.dumps({ 'token_type': 'Bearer', 'paging': None, 'elements': [{ 'id': '560e7ed2076e0d589e88bd74b6aad4b7' }], 'access_token': 'foobar', 'expires_in': 1800, 'linked': None }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_drip.py0000644000175000017500000000124513336542652024310 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class DripOAuthTest(OAuth2Test): backend_path = 'social_core.backends.drip.DripOAuth' user_data_url = 'https://api.getdrip.com/v2/user' expected_username = 'other@example.com' access_token_body = json.dumps({ 'access_token': '822bbf7cd12243df', 'token_type': 'bearer', 'scope': 'public' }) user_data_body = json.dumps({ 'users': [ { 'email': 'other@example.com', 'name': None } ] }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_deezer.py0000644000175000017500000000227713336542652024636 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class DeezerOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.deezer.DeezerOAuth2' user_data_url = 'http://api.deezer.com/user/me' expected_username = 'foobar' access_token_body = 'access_token=foobar&expires=0' user_data_body = json.dumps({ 'id': '1', 'name': 'foobar', 'lastname': '', 'firstname': '', 'status': 0, 'birthday': '1970-01-01', 'inscription_date': '2015-01-01', 'gender': 'M', 'link': 'https://www.deezer.com/profile/1', 'picture': 'https://api.deezer.com/user/1/image', 'picture_small': 'https://cdns-images.dzcdn.net/images/user//56x56-000000-80-0-0.jpg', 'picture_medium': 'https://cdns-images.dzcdn.net/images/user//250x250-000000-80-0-0.jpg', 'picture_big': 'https://cdns-images.dzcdn.net/images/user//500x500-000000-80-0-0.jpg', 'country': 'FR', 'lang': 'FR', 'is_kid': False, 'tracklist': 'https://api.deezer.com/user/1/flow', 'type': 'user' }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_behance.py0000644000175000017500000000322413336542652024736 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class BehanceOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.behance.BehanceOAuth2' access_token_body = json.dumps({ 'access_token': 'foobar', 'valid': 1, 'user': { 'username': 'foobar', 'city': 'Foo City', 'first_name': 'Foo', 'last_name': 'Bar', 'display_name': 'Foo Bar', 'url': 'http://www.behance.net/foobar', 'country': 'Fooland', 'company': '', 'created_on': 1355152329, 'state': '', 'fields': [ 'Programming', 'Web Design', 'Web Development' ], 'images': { '32': 'https://www.behance.net/assets/img/profile/' 'no-image-32.jpg', '50': 'https://www.behance.net/assets/img/profile/' 'no-image-50.jpg', '115': 'https://www.behance.net/assets/img/profile/' 'no-image-138.jpg', '129': 'https://www.behance.net/assets/img/profile/' 'no-image-138.jpg', '138': 'https://www.behance.net/assets/img/profile/' 'no-image-138.jpg', '78': 'https://www.behance.net/assets/img/profile/' 'no-image-78.jpg' }, 'id': 1010101, 'occupation': 'Software Developer' } }) expected_username = 'foobar' def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_atlassian.py0000644000175000017500000000430713365406137025332 0ustar omabomab00000000000000import json from httpretty import HTTPretty from .oauth import OAuth2Test class AtlassianOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.atlassian.AtlassianOAuth2' tenant_url = 'https://api.atlassian.com/oauth/token/accessible-resources' user_data_url = 'https://api.atlassian.com/ex/jira/FAKED_CLOUD_ID/rest/api/2/myself' expected_username = 'erlich' access_token_body = json.dumps({ 'access_token': 'aviato', 'token_type': 'bearer' }) tenant_data_body = json.dumps([ { "id": "FAKED_CLOUD_ID", "name": "bachmanity.com", "avatarUrl": "https://bachmanity.atlassian.net/avatars/240/site.png", "scopes": [ "read:jira-user" ] } ]) user_data_body = json.dumps({ "self": "http://bachmanity.atlassian.net/rest/api/3/user?username=erlich", "key": "erlich", "accountId": "99:27935d01-92a7-4687-8272-a9b8d3b2ae2e", "name": "erlich", "emailAddress": "erlich@bachmanity.com", "avatarUrls": { "48x48": "http://bachmanity.atlassian.net/secure/useravatar?size=large&ownerId=erlich", "24x24": "http://bachmanity.atlassian.net/secure/useravatar?size=small&ownerId=erlich", "16x16": "http://bachmanity.atlassian.net/secure/useravatar?size=xsmall&ownerId=erlich", "32x32": "http://bachmanity.atlassian.net/secure/useravatar?size=medium&ownerId=erlich" }, "displayName": "Erlich Bachman", "active": True, "timeZone": "Australia/Sydney", "groups": { "size": 3, "items": [] }, "applicationRoles": { "size": 1, "items": [] } }) def auth_handlers(self, start_url): target_url = super(AtlassianOAuth2Test, self).auth_handlers(start_url) HTTPretty.register_uri(HTTPretty.GET, self.tenant_url, body=self.tenant_data_body, content_type='application/json') return target_url def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/open_id_connect.py0000644000175000017500000001474113432620722025436 0ustar omabomab00000000000000# -*- coding: utf-8 -*- import os import sys import json import datetime import unittest2 import base64 from calendar import timegm from jose import jwt from jose.jwk import RSAKey from httpretty import HTTPretty sys.path.insert(0, '..') from ...exceptions import AuthTokenError TEST_ROOT = os.path.dirname(os.path.dirname(__file__)) JWK_KEY = { 'kty': 'RSA', 'd': 'ZmswNokEvBcxW_Kvcy8mWUQOQCBdGbnM0xR7nhvGHC-Q24z3XAQWlMWbsmGc_R1o' \ '_F3zK7DBlc3BokdRaO1KJirNmnHCw5TlnBlJrXiWpFBtVglUg98-4sRRO0VWnGXK' \ 'JPOkBQ6b_DYRO3b0o8CSpWowpiV6HB71cjXTqKPZf-aXU9WjCCAtxVjfIxgQFu5I' \ '-G1Qah8mZeY8HK_y99L4f0siZcbUoaIcfeWBhxi14ODyuSAHt0sNEkhiIVBZE7QZ' \ 'm-SEP1ryT9VAaljbwHHPmg7NC26vtLZhvaBGbTTJnEH0ZubbN2PMzsfeNyoCIHy4' \ '4QDSpQDCHfgcGOlHY_t5gQ', 'e': 'AQAB', 'use': 'sig', 'kid': 'testkey', 'alg': 'RS256', 'n': 'pUfcJ8WFrVue98Ygzb6KEQXHBzi8HavCu8VENB2As943--bHPcQ-nScXnrRFAUg8' \ 'H5ZltuOcHWvsGw_AQifSLmOCSWJAPkdNb0w0QzY7Re8NrPjCsP58Tytp5LicF0Ao' \ 'Ag28UK3JioY9hXHGvdZsWR1Rp3I-Z3nRBP6HyO18pEgcZ91c9aAzsqu80An9X4DA' \ 'b1lExtZorvcd5yTBzZgr-MUeytVRni2lDNEpa6OFuopHXmg27Hn3oWAaQlbymd4g' \ 'ifc01oahcwl3ze2tMK6gJxa_TdCf1y99Yq6oilmVvZJ8kwWWnbPE-oDmOVPVnEyT' \ 'vYVCvN4rBT1DQ-x0F1mo2Q', } JWK_PUBLIC_KEY = {key: value for key, value in JWK_KEY.items() if key != 'd'} class OpenIdConnectTestMixin(object): """ Mixin to test OpenID Connect consumers. Inheriting classes should also inherit OAuth2Test. """ client_key = 'a-key' client_secret = 'a-secret-key' issuer = None # id_token issuer openid_config_body = None key = None def setUp(self): super(OpenIdConnectTestMixin, self).setUp() self.key = JWK_KEY.copy() self.public_key = JWK_PUBLIC_KEY.copy() HTTPretty.register_uri(HTTPretty.GET, self.backend.OIDC_ENDPOINT + '/.well-known/openid-configuration', status=200, body=self.openid_config_body ) oidc_config = json.loads(self.openid_config_body) def jwks(_request, _uri, headers): return 200, headers, json.dumps({'keys': [self.key]}) HTTPretty.register_uri(HTTPretty.GET, oidc_config.get('jwks_uri'), status=200, body=json.dumps({'keys': [self.public_key]})) def extra_settings(self): settings = super(OpenIdConnectTestMixin, self).extra_settings() settings.update({ 'SOCIAL_AUTH_{0}_KEY'.format(self.name): self.client_key, 'SOCIAL_AUTH_{0}_SECRET'.format(self.name): self.client_secret, 'SOCIAL_AUTH_{0}_ID_TOKEN_DECRYPTION_KEY'.format(self.name): self.client_secret }) return settings def get_id_token(self, client_key=None, expiration_datetime=None, issue_datetime=None, nonce=None, issuer=None): """ Return the id_token to be added to the access token body. """ return { 'iss': issuer, 'nonce': nonce, 'aud': client_key, 'azp': client_key, 'exp': expiration_datetime, 'iat': issue_datetime, 'sub': '1234' } def prepare_access_token_body(self, client_key=None, tamper_message=False, expiration_datetime=None, issue_datetime=None, nonce=None, issuer=None): """ Prepares a provider access token response. Arguments: client_id -- (str) OAuth ID for the client that requested authentication. expiration_time -- (datetime) Date and time after which the response should be considered invalid. """ body = {'access_token': 'foobar', 'token_type': 'bearer'} client_key = client_key or self.client_key now = datetime.datetime.utcnow() expiration_datetime = expiration_datetime or \ (now + datetime.timedelta(seconds=30)) issue_datetime = issue_datetime or now nonce = nonce or 'a-nonce' issuer = issuer or self.issuer id_token = self.get_id_token( client_key, timegm(expiration_datetime.utctimetuple()), timegm(issue_datetime.utctimetuple()), nonce, issuer ) body['id_token'] = jwt.encode( id_token, key=dict(self.key, iat=timegm(issue_datetime.utctimetuple()), nonce=nonce), algorithm='RS256', access_token='foobar' ) if tamper_message: header, msg, sig = body['id_token'].split('.') id_token['sub'] = '1235' msg = base64.encodestring(json.dumps(id_token).encode()).decode() body['id_token'] = '.'.join([header, msg, sig]) return json.dumps(body) def authtoken_raised(self, expected_message, **access_token_kwargs): self.access_token_body = self.prepare_access_token_body( **access_token_kwargs ) with self.assertRaisesRegexp(AuthTokenError, expected_message): self.do_login() def test_invalid_signature(self): self.authtoken_raised( 'Token error: Signature verification failed', tamper_message=True ) def test_expired_signature(self): expiration_datetime = datetime.datetime.utcnow() - \ datetime.timedelta(seconds=30) self.authtoken_raised('Token error: Signature has expired', expiration_datetime=expiration_datetime) def test_invalid_issuer(self): self.authtoken_raised('Token error: Invalid issuer', issuer='someone-else') def test_invalid_audience(self): self.authtoken_raised('Token error: Invalid audience', client_key='someone-else') def test_invalid_issue_time(self): expiration_datetime = datetime.datetime.utcnow() - \ datetime.timedelta(hours=1) self.authtoken_raised('Token error: Incorrect id_token: iat', issue_datetime=expiration_datetime) def test_invalid_nonce(self): self.authtoken_raised( 'Token error: Incorrect id_token: nonce', nonce='something-wrong' ) social-auth-core-3.1.0/social_core/tests/backends/test_universe.py0000644000175000017500000000163513424575563025222 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class UniverseAuth2Test(OAuth2Test): backend_path = 'social_core.backends.universe.UniverseOAuth2' user_data_url = 'https://www.universe.com/api/v2/current_user' expected_username = 'scott+awesome@universe.com' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps( { 'current_user': { 'id': '123456', 'slug': 'scott-vitale', 'first_name': 'Scott', 'last_name': 'Vitale', 'created_at': '2019-01-08T15:49:42.514Z', 'updated_at': '2019-01-17T19:41:39.711Z', 'email': 'scott+awesome@universe.com', } } ) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/backends/test_phabricator.py0000644000175000017500000000455413336542652025656 0ustar omabomab00000000000000import json from .oauth import OAuth2Test class PhabricatorOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.phabricator.PhabricatorOAuth2' user_data_url = 'https://secure.phabricator.com/api/user.whoami' expected_username = 'user' access_token_body = json.dumps({ 'access_token': 'loremipsumdolorsitametenim', 'token_type': 'Bearer', 'expires_in': 7200, 'refresh_token': 'foobar', }) user_data_body = json.dumps({ 'phid': 'PHID-USER-jbfcsj7c6nkt2tv3trb6', 'userName': 'user', 'realName': 'FirstName LastName', 'image': 'https://secure.phabricator.com/file/data/4qjdpmvca4wwkfw2wevc' '/PHID-FILE-t37vxezr54fjuvbrblkp/alphanumeric_lato-white_U.png' '-_3f674d-255%2C255%2C255%2C0.7.png', 'uri': 'https://secure.phabricator.com/p/user/', 'roles': ['admin', 'verified', 'approved', 'activated'], 'primaryEmail': 'user@example.com', }) def test_login(self): self.do_login() def test_partial_pipeline(self): self.do_partial_pipeline() class PhabricatorCustomDomainOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.phabricator.PhabricatorOAuth2' user_data_url = 'https://example.com/api/user.whoami' expected_username = 'user' access_token_body = json.dumps({ 'access_token': 'loremipsumdolorsitametenim', 'token_type': 'Bearer', 'expires_in': 7200, 'refresh_token': 'foobar', }) user_data_body = json.dumps({ 'phid': 'PHID-USER-jbfcsj7c6nkt2tv3trb6', 'userName': 'user', 'realName': 'FirstName LastName', 'image': 'https://example.com/file/data/4qjdpmvca4wwkfw2wevc/PHID-FILE-' 't37vxezr54fjuvbrblkp/alphanumeric_lato-white_U.png-_3f674d-25' '5%2C255%2C255%2C0.7.png', 'uri': 'https://example.com/p/user/', 'roles': ['admin', 'verified', 'approved', 'activated'], 'primaryEmail': 'user@example.com', }) def test_login(self): self.strategy.set_settings({ 'SOCIAL_AUTH_PHABRICATOR_API_URL': 'https://example.com', }) self.do_login() def test_partial_pipeline(self): self.strategy.set_settings({ 'SOCIAL_AUTH_PHABRICATOR_API_URL': 'https://example.com', }) self.do_partial_pipeline() social-auth-core-3.1.0/social_core/tests/test_pipeline.py0000644000175000017500000001632313336542652023410 0ustar omabomab00000000000000import json from ..utils import PARTIAL_TOKEN_SESSION_NAME from ..exceptions import AuthException from .models import TestUserSocialAuth, TestStorage, User from .strategy import TestStrategy from .actions.actions import BaseActionTest class IntegrityError(Exception): pass class UnknownError(Exception): pass class IntegrityErrorUserSocialAuth(TestUserSocialAuth): @classmethod def create_social_auth(cls, user, uid, provider): raise IntegrityError() @classmethod def get_social_auth(cls, provider, uid): if not hasattr(cls, '_called_times'): cls._called_times = 0 cls._called_times += 1 if cls._called_times == 2: user = list(User.cache.values())[0] return IntegrityErrorUserSocialAuth(user, provider, uid) else: return super(IntegrityErrorUserSocialAuth, cls).get_social_auth( provider, uid ) class IntegrityErrorStorage(TestStorage): user = IntegrityErrorUserSocialAuth @classmethod def is_integrity_error(cls, exception): """Check if given exception flags an integrity error in the DB""" return isinstance(exception, IntegrityError) class UnknownErrorUserSocialAuth(TestUserSocialAuth): @classmethod def create_social_auth(cls, user, uid, provider): raise UnknownError() class UnknownErrorStorage(IntegrityErrorStorage): user = UnknownErrorUserSocialAuth class IntegrityErrorOnLoginTest(BaseActionTest): def setUp(self): self.strategy = TestStrategy(IntegrityErrorStorage) super(IntegrityErrorOnLoginTest, self).setUp() def test_integrity_error(self): self.do_login() class UnknownErrorOnLoginTest(BaseActionTest): def setUp(self): self.strategy = TestStrategy(UnknownErrorStorage) super(UnknownErrorOnLoginTest, self).setUp() def test_unknown_error(self): with self.assertRaises(UnknownError): self.do_login() class EmailAsUsernameTest(BaseActionTest): expected_username = 'foo@bar.com' def test_email_as_username(self): self.strategy.set_settings({ 'SOCIAL_AUTH_USERNAME_IS_FULL_EMAIL': True }) self.do_login() class RandomUsernameTest(BaseActionTest): user_data_body = json.dumps({ 'id': 1, 'avatar_url': 'https://github.com/images/error/foobar_happy.gif', 'gravatar_id': 'somehexcode', 'url': 'https://api.github.com/users/foobar', 'name': 'monalisa foobar', 'company': 'GitHub', 'blog': 'https://github.com/blog', 'location': 'San Francisco', 'email': 'foo@bar.com', 'hireable': False, 'bio': 'There once was...', 'public_repos': 2, 'public_gists': 1, 'followers': 20, 'following': 0, 'html_url': 'https://github.com/foobar', 'created_at': '2008-01-14T04:33:35Z', 'type': 'User', 'total_private_repos': 100, 'owned_private_repos': 100, 'private_gists': 81, 'disk_usage': 10000, 'collaborators': 8, 'plan': { 'name': 'Medium', 'space': 400, 'collaborators': 10, 'private_repos': 20 } }) def test_random_username(self): self.do_login(after_complete_checks=False) class SluggedUsernameTest(BaseActionTest): expected_username = 'foo-bar' user_data_body = json.dumps({ 'login': 'Foo Bar', 'id': 1, 'avatar_url': 'https://github.com/images/error/foobar_happy.gif', 'gravatar_id': 'somehexcode', 'url': 'https://api.github.com/users/foobar', 'name': 'monalisa foobar', 'company': 'GitHub', 'blog': 'https://github.com/blog', 'location': 'San Francisco', 'email': 'foo@bar.com', 'hireable': False, 'bio': 'There once was...', 'public_repos': 2, 'public_gists': 1, 'followers': 20, 'following': 0, 'html_url': 'https://github.com/foobar', 'created_at': '2008-01-14T04:33:35Z', 'type': 'User', 'total_private_repos': 100, 'owned_private_repos': 100, 'private_gists': 81, 'disk_usage': 10000, 'collaborators': 8, 'plan': { 'name': 'Medium', 'space': 400, 'collaborators': 10, 'private_repos': 20 } }) def test_random_username(self): self.strategy.set_settings({ 'SOCIAL_AUTH_CLEAN_USERNAMES': False, 'SOCIAL_AUTH_SLUGIFY_USERNAMES': True }) self.do_login() class RepeatedUsernameTest(BaseActionTest): def test_random_username(self): User(username='foobar') self.do_login(after_complete_checks=False) self.assertTrue(self.strategy.session_get('username') .startswith('foobar')) class AssociateByEmailTest(BaseActionTest): def test_multiple_accounts_with_same_email(self): user = User(username='foobar1') user.email = 'foo@bar.com' self.do_login(after_complete_checks=False) self.assertTrue(self.strategy.session_get('username') .startswith('foobar')) class MultipleAccountsWithSameEmailTest(BaseActionTest): def test_multiple_accounts_with_same_email(self): user1 = User(username='foobar1') user2 = User(username='foobar2') user1.email = 'foo@bar.com' user2.email = 'foo@bar.com' with self.assertRaises(AuthException): self.do_login(after_complete_checks=False) class UserPersistsInPartialPipeline(BaseActionTest): def test_user_persists_in_partial_pipeline_kwargs(self): user = User(username='foobar1') user.email = 'foo@bar.com' self.strategy.set_settings({ 'SOCIAL_AUTH_PIPELINE': ( 'social_core.pipeline.social_auth.social_details', 'social_core.pipeline.social_auth.social_uid', 'social_core.pipeline.social_auth.associate_by_email', 'social_core.tests.pipeline.set_user_from_kwargs' ) }) self.do_login(after_complete_checks=False) # Handle the partial pipeline self.strategy.session_set('attribute', 'testing') token = self.strategy.session_pop(PARTIAL_TOKEN_SESSION_NAME) partial = self.strategy.partial_load(token) self.backend.continue_pipeline(partial) def test_user_persists_in_partial_pipeline(self): user = User(username='foobar1') user.email = 'foo@bar.com' self.strategy.set_settings({ 'SOCIAL_AUTH_PIPELINE': ( 'social_core.pipeline.social_auth.social_details', 'social_core.pipeline.social_auth.social_uid', 'social_core.pipeline.social_auth.associate_by_email', 'social_core.tests.pipeline.set_user_from_args' ) }) self.do_login(after_complete_checks=False) # Handle the partial pipeline self.strategy.session_set('attribute', 'testing') token = self.strategy.session_pop(PARTIAL_TOKEN_SESSION_NAME) partial = self.strategy.partial_load(token) self.backend.continue_pipeline(partial) social-auth-core-3.1.0/social_core/tests/requirements-python2.txt0000644000175000017500000000005013336542652025045 0ustar omabomab00000000000000-r requirements-base.txt unittest2 mock social-auth-core-3.1.0/social_core/tests/test_exceptions.py0000644000175000017500000000734213336542652023765 0ustar omabomab00000000000000import unittest2 as unittest from ..exceptions import SocialAuthBaseException, WrongBackend, \ AuthFailed, AuthTokenError, \ AuthMissingParameter, AuthStateMissing, \ NotAllowedToDisconnect, AuthException, \ AuthCanceled, AuthUnknownError, \ AuthStateForbidden, AuthAlreadyAssociated, \ AuthTokenRevoked, AuthForbidden, \ AuthUnreachableProvider, InvalidEmail, \ MissingBackend class BaseExceptionTestCase(unittest.TestCase): exception = None expected_message = '' def test_exception_message(self): if self.exception is None and self.expected_message == '': return try: raise self.exception except SocialAuthBaseException as err: self.assertEqual(str(err), self.expected_message) class WrongBackendTest(BaseExceptionTestCase): exception = WrongBackend('foobar') expected_message = 'Incorrect authentication service "foobar"' class AuthFailedTest(BaseExceptionTestCase): exception = AuthFailed('foobar', 'wrong_user') expected_message = 'Authentication failed: wrong_user' class AuthFailedDeniedTest(BaseExceptionTestCase): exception = AuthFailed('foobar', 'access_denied') expected_message = 'Authentication process was canceled' class AuthTokenErrorTest(BaseExceptionTestCase): exception = AuthTokenError('foobar', 'Incorrect tokens') expected_message = 'Token error: Incorrect tokens' class AuthMissingParameterTest(BaseExceptionTestCase): exception = AuthMissingParameter('foobar', 'username') expected_message = 'Missing needed parameter username' class AuthStateMissingTest(BaseExceptionTestCase): exception = AuthStateMissing('foobar') expected_message = 'Session value state missing.' class NotAllowedToDisconnectTest(BaseExceptionTestCase): exception = NotAllowedToDisconnect() expected_message = '' class AuthExceptionTest(BaseExceptionTestCase): exception = AuthException('foobar', 'message') expected_message = 'message' class AuthCanceledTest(BaseExceptionTestCase): exception = AuthCanceled('foobar') expected_message = 'Authentication process canceled' class AuthCanceledWithExtraMessageTest(BaseExceptionTestCase): exception = AuthCanceled('foobar', 'error_message') expected_message = 'Authentication process canceled: error_message' class AuthUnknownErrorTest(BaseExceptionTestCase): exception = AuthUnknownError('foobar', 'some error') expected_message = 'An unknown error happened while ' \ 'authenticating some error' class AuthStateForbiddenTest(BaseExceptionTestCase): exception = AuthStateForbidden('foobar') expected_message = 'Wrong state parameter given.' class AuthAlreadyAssociatedTest(BaseExceptionTestCase): exception = AuthAlreadyAssociated('foobar') expected_message = '' class AuthTokenRevokedTest(BaseExceptionTestCase): exception = AuthTokenRevoked('foobar') expected_message = 'User revoke access to the token' class AuthForbiddenTest(BaseExceptionTestCase): exception = AuthForbidden('foobar') expected_message = 'Your credentials aren\'t allowed' class AuthUnreachableProviderTest(BaseExceptionTestCase): exception = AuthUnreachableProvider('foobar') expected_message = 'The authentication provider could not be reached' class InvalidEmailTest(BaseExceptionTestCase): exception = InvalidEmail('foobar') expected_message = 'Email couldn\'t be validated' class MissingBackendTest(BaseExceptionTestCase): exception = MissingBackend('backend') expected_message = 'Missing backend "backend" entry' social-auth-core-3.1.0/social_core/tests/actions/0000755000175000017500000000000013433256445021625 5ustar omabomab00000000000000social-auth-core-3.1.0/social_core/tests/actions/test_associate.py0000644000175000017500000000551213365410437025211 0ustar omabomab00000000000000import json from ...exceptions import AuthAlreadyAssociated from ..models import User from .actions import BaseActionTest class AssociateActionTest(BaseActionTest): expected_username = 'foobar' def setUp(self): super(AssociateActionTest, self).setUp() self.user = User(username='foobar', email='foo@bar.com') self.backend.strategy.session_set('username', self.user.username) def test_associate(self): self.do_login() self.assertTrue(len(self.user.social), 1) self.assertEqual(self.user.social[0].provider, 'github') def test_associate_with_partial_pipeline(self): self.do_login_with_partial_pipeline() self.assertEqual(len(self.user.social), 1) self.assertEqual(self.user.social[0].provider, 'github') class MultipleAccountsTest(AssociateActionTest): alternative_user_data_body = json.dumps({ 'login': 'foobar2', 'id': 2, 'avatar_url': 'https://github.com/images/error/foobar2_happy.gif', 'gravatar_id': 'somehexcode', 'url': 'https://api.github.com/users/foobar2', 'name': 'monalisa foobar2', 'company': 'GitHub', 'blog': 'https://github.com/blog', 'location': 'San Francisco', 'email': 'foo@bar.com', 'hireable': False, 'bio': 'There once was...', 'public_repos': 2, 'public_gists': 1, 'followers': 20, 'following': 0, 'html_url': 'https://github.com/foobar2', 'created_at': '2008-01-14T04:33:35Z', 'type': 'User', 'total_private_repos': 100, 'owned_private_repos': 100, 'private_gists': 81, 'disk_usage': 10000, 'collaborators': 8, 'plan': { 'name': 'Medium', 'space': 400, 'collaborators': 10, 'private_repos': 20 } }) def test_multiple_social_accounts(self): self.do_login() self.do_login(user_data_body=self.alternative_user_data_body) self.assertEqual(len(self.user.social), 2) self.assertEqual(self.user.social[0].provider, 'github') self.assertEqual(self.user.social[1].provider, 'github') class AlreadyAssociatedErrorTest(BaseActionTest): def setUp(self): super(AlreadyAssociatedErrorTest, self).setUp() self.user1 = User(username='foobar', email='foo@bar.com') self.user = None def tearDown(self): super(AlreadyAssociatedErrorTest, self).tearDown() self.user1 = None self.user = None def test_already_associated_error(self): self.user = self.user1 self.do_login() self.user = User(username='foobar2', email='foo2@bar2.com') with self.assertRaisesRegexp(AuthAlreadyAssociated, 'This account is already in use.'): self.do_login() social-auth-core-3.1.0/social_core/tests/actions/test_disconnect.py0000644000175000017500000000502113336542652025365 0ustar omabomab00000000000000import requests from httpretty import HTTPretty from ...actions import do_disconnect from ...exceptions import NotAllowedToDisconnect from ...utils import parse_qs from ..models import User, TestUserSocialAuth from .actions import BaseActionTest class DisconnectActionTest(BaseActionTest): def test_not_allowed_to_disconnect(self): self.do_login() user = User.get(self.expected_username) with self.assertRaises(NotAllowedToDisconnect): do_disconnect(self.backend, user) def test_disconnect(self): self.do_login() user = User.get(self.expected_username) user.password = 'password' do_disconnect(self.backend, user) self.assertEqual(len(user.social), 0) def test_disconnect_with_association_id(self): self.do_login() user = User.get(self.expected_username) user.password = 'password' association_id = user.social[0].id second_usa = TestUserSocialAuth(user, user.social[0].provider, "uid2") self.assertEqual(len(user.social), 2) do_disconnect(self.backend, user, association_id) self.assertEqual(len(user.social), 1) self.assertEqual(user.social[0], second_usa) def test_disconnect_with_partial_pipeline(self): self.strategy.set_settings({ 'SOCIAL_AUTH_DISCONNECT_PIPELINE': ( 'social_core.tests.pipeline.ask_for_password', 'social_core.tests.pipeline.set_password', 'social_core.pipeline.disconnect.allowed_to_disconnect', 'social_core.pipeline.disconnect.get_entries', 'social_core.pipeline.disconnect.revoke_tokens', 'social_core.pipeline.disconnect.disconnect' ) }) self.do_login() user = User.get(self.expected_username) redirect = do_disconnect(self.backend, user) url = self.strategy.build_absolute_uri('/password') self.assertEqual(redirect.url, url) HTTPretty.register_uri(HTTPretty.GET, redirect.url, status=200, body='foobar') HTTPretty.register_uri(HTTPretty.POST, redirect.url, status=200) password = 'foobar' requests.get(url) requests.post(url, data={'password': password}) data = parse_qs(HTTPretty.last_request.body) self.assertEqual(data['password'], password) self.strategy.session_set('password', data['password']) redirect = do_disconnect(self.backend, user) self.assertEqual(len(user.social), 0) social-auth-core-3.1.0/social_core/tests/actions/test_login.py0000644000175000017500000000531013336542652024345 0ustar omabomab00000000000000from ...utils import PARTIAL_TOKEN_SESSION_NAME from ..models import User from .actions import BaseActionTest class LoginActionTest(BaseActionTest): def test_login(self): self.do_login() def test_login_with_partial_pipeline(self): self.do_login_with_partial_pipeline() def test_fields_stored_in_session(self): self.strategy.set_settings({ 'SOCIAL_AUTH_FIELDS_STORED_IN_SESSION': ['foo', 'bar'] }) self.strategy.set_request_data({'foo': '1', 'bar': '2'}, self.backend) self.do_login() self.assertEqual(self.strategy.session_get('foo'), '1') self.assertEqual(self.strategy.session_get('bar'), '2') def test_redirect_value(self): self.strategy.set_request_data({'next': '/after-login'}, self.backend) redirect = self.do_login(after_complete_checks=False) self.assertEqual(redirect.url, '/after-login') def test_login_with_invalid_partial_pipeline(self): def before_complete(): partial_token = self.strategy.session_get( PARTIAL_TOKEN_SESSION_NAME ) partial = self.strategy.storage.partial.load(partial_token) partial.data['backend'] = 'foobar' self.do_login_with_partial_pipeline(before_complete) def test_new_user(self): self.strategy.set_settings({ 'SOCIAL_AUTH_NEW_USER_REDIRECT_URL': '/new-user' }) redirect = self.do_login(after_complete_checks=False) self.assertEqual(redirect.url, '/new-user') def test_inactive_user(self): self.strategy.set_settings({ 'SOCIAL_AUTH_INACTIVE_USER_URL': '/inactive' }) User.set_active(False) redirect = self.do_login(after_complete_checks=False) self.assertEqual(redirect.url, '/inactive') def test_invalid_user(self): self.strategy.set_settings({ 'SOCIAL_AUTH_LOGIN_ERROR_URL': '/error', 'SOCIAL_AUTH_PIPELINE': ( 'social_core.pipeline.social_auth.social_details', 'social_core.pipeline.social_auth.social_uid', 'social_core.pipeline.social_auth.auth_allowed', 'social_core.pipeline.social_auth.social_user', 'social_core.pipeline.user.get_username', 'social_core.pipeline.user.create_user', 'social_core.pipeline.social_auth.associate_user', 'social_core.pipeline.social_auth.load_extra_data', 'social_core.pipeline.user.user_details', 'social_core.tests.pipeline.remove_user' ) }) redirect = self.do_login(after_complete_checks=False) self.assertEqual(redirect.url, '/error') social-auth-core-3.1.0/social_core/tests/actions/__init__.py0000644000175000017500000000000013336542652023724 0ustar omabomab00000000000000social-auth-core-3.1.0/social_core/tests/actions/actions.py0000644000175000017500000002050113336542652023635 0ustar omabomab00000000000000import json import requests import unittest2 as unittest from httpretty import HTTPretty from six.moves.urllib_parse import urlparse from ...utils import parse_qs, module_member from ...actions import do_auth, do_complete from ..models import TestStorage, User, TestUserSocialAuth, TestNonce, \ TestAssociation from ..strategy import TestStrategy class BaseActionTest(unittest.TestCase): user_data_url = 'https://api.github.com/user' login_redirect_url = '/success' expected_username = 'foobar' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps({ 'login': 'foobar', 'id': 1, 'avatar_url': 'https://github.com/images/error/foobar_happy.gif', 'gravatar_id': 'somehexcode', 'url': 'https://api.github.com/users/foobar', 'name': 'monalisa foobar', 'company': 'GitHub', 'blog': 'https://github.com/blog', 'location': 'San Francisco', 'email': 'foo@bar.com', 'hireable': False, 'bio': 'There once was...', 'public_repos': 2, 'public_gists': 1, 'followers': 20, 'following': 0, 'html_url': 'https://github.com/foobar', 'created_at': '2008-01-14T04:33:35Z', 'type': 'User', 'total_private_repos': 100, 'owned_private_repos': 100, 'private_gists': 81, 'disk_usage': 10000, 'collaborators': 8, 'plan': { 'name': 'Medium', 'space': 400, 'collaborators': 10, 'private_repos': 20 } }) def __init__(self, *args, **kwargs): self.strategy = None super(BaseActionTest, self).__init__(*args, **kwargs) def setUp(self): HTTPretty.enable() User.reset_cache() TestUserSocialAuth.reset_cache() TestNonce.reset_cache() TestAssociation.reset_cache() Backend = module_member('social_core.backends.github.GithubOAuth2') self.strategy = self.strategy or TestStrategy(TestStorage) self.backend = Backend(self.strategy, redirect_uri='/complete/github') self.user = None def tearDown(self): self.backend = None self.strategy = None self.user = None User.reset_cache() User.set_active(True) TestUserSocialAuth.reset_cache() TestNonce.reset_cache() TestAssociation.reset_cache() HTTPretty.disable() def do_login(self, after_complete_checks=True, user_data_body=None, expected_username=None): self.strategy.set_settings({ 'SOCIAL_AUTH_GITHUB_KEY': 'a-key', 'SOCIAL_AUTH_GITHUB_SECRET': 'a-secret-key', 'SOCIAL_AUTH_LOGIN_REDIRECT_URL': self.login_redirect_url, 'SOCIAL_AUTH_AUTHENTICATION_BACKENDS': ( 'social_core.backends.github.GithubOAuth2', ) }) start_url = do_auth(self.backend).url target_url = self.strategy.build_absolute_uri( '/complete/github/?code=foobar' ) start_query = parse_qs(urlparse(start_url).query) location_url = target_url + ('&' if '?' in target_url else '?') + \ 'state=' + start_query['state'] location_query = parse_qs(urlparse(location_url).query) HTTPretty.register_uri(HTTPretty.GET, start_url, status=301, location=location_url) HTTPretty.register_uri(HTTPretty.GET, location_url, status=200, body='foobar') response = requests.get(start_url) self.assertEqual(response.url, location_url) self.assertEqual(response.text, 'foobar') HTTPretty.register_uri(HTTPretty.POST, uri=self.backend.ACCESS_TOKEN_URL, status=200, body=self.access_token_body or '', content_type='text/json') if self.user_data_url: user_data_body = user_data_body or self.user_data_body or '' HTTPretty.register_uri(HTTPretty.GET, self.user_data_url, body=user_data_body, content_type='text/json') self.strategy.set_request_data(location_query, self.backend) def _login(backend, user, social_user): backend.strategy.session_set('username', user.username) redirect = do_complete(self.backend, user=self.user, login=_login) if after_complete_checks: self.assertEqual(self.strategy.session_get('username'), expected_username or self.expected_username) self.assertEqual(redirect.url, self.login_redirect_url) return redirect def do_login_with_partial_pipeline(self, before_complete=None): self.strategy.set_settings({ 'SOCIAL_AUTH_GITHUB_KEY': 'a-key', 'SOCIAL_AUTH_GITHUB_SECRET': 'a-secret-key', 'SOCIAL_AUTH_LOGIN_REDIRECT_URL': self.login_redirect_url, 'SOCIAL_AUTH_AUTHENTICATION_BACKENDS': ( 'social_core.backends.github.GithubOAuth2', ), 'SOCIAL_AUTH_PIPELINE': ( 'social_core.pipeline.social_auth.social_details', 'social_core.pipeline.social_auth.social_uid', 'social_core.pipeline.social_auth.auth_allowed', 'social_core.tests.pipeline.ask_for_password', 'social_core.pipeline.social_auth.social_user', 'social_core.pipeline.user.get_username', 'social_core.pipeline.user.create_user', 'social_core.pipeline.social_auth.associate_user', 'social_core.pipeline.social_auth.load_extra_data', 'social_core.tests.pipeline.set_password', 'social_core.pipeline.user.user_details' ) }) start_url = do_auth(self.backend).url target_url = self.strategy.build_absolute_uri( '/complete/github/?code=foobar' ) start_query = parse_qs(urlparse(start_url).query) location_url = target_url + ('&' if '?' in target_url else '?') + \ 'state=' + start_query['state'] location_query = parse_qs(urlparse(location_url).query) HTTPretty.register_uri(HTTPretty.GET, start_url, status=301, location=location_url) HTTPretty.register_uri(HTTPretty.GET, location_url, status=200, body='foobar') response = requests.get(start_url) self.assertEqual(response.url, location_url) self.assertEqual(response.text, 'foobar') HTTPretty.register_uri(HTTPretty.GET, uri=self.backend.ACCESS_TOKEN_URL, status=200, body=self.access_token_body or '', content_type='text/json') if self.user_data_url: HTTPretty.register_uri(HTTPretty.GET, self.user_data_url, body=self.user_data_body or '', content_type='text/json') self.strategy.set_request_data(location_query, self.backend) def _login(backend, user, social_user): backend.strategy.session_set('username', user.username) redirect = do_complete(self.backend, user=self.user, login=_login) url = self.strategy.build_absolute_uri('/password') self.assertEqual(redirect.url, url) HTTPretty.register_uri(HTTPretty.GET, redirect.url, status=200, body='foobar') HTTPretty.register_uri(HTTPretty.POST, redirect.url, status=200) password = 'foobar' requests.get(url) requests.post(url, data={'password': password}) data = parse_qs(HTTPretty.last_request.body) self.assertEqual(data['password'], password) self.strategy.session_set('password', data['password']) if before_complete: before_complete() redirect = do_complete(self.backend, user=self.user, login=_login) self.assertEqual(self.strategy.session_get('username'), self.expected_username) self.assertEqual(redirect.url, self.login_redirect_url) social-auth-core-3.1.0/social_core/tests/test_utils.py0000644000175000017500000001560313336542652022743 0ustar omabomab00000000000000import sys import unittest2 as unittest try: from unittest.mock import Mock except ImportError: from mock import Mock from ..utils import sanitize_redirect, user_is_authenticated, \ user_is_active, slugify, build_absolute_uri, \ partial_pipeline_data from .models import TestPartial PY3 = sys.version_info[0] == 3 class SanitizeRedirectTest(unittest.TestCase): def test_none_redirect(self): self.assertEqual(sanitize_redirect(['myapp.com'], None), None) def test_empty_redirect(self): self.assertEqual(sanitize_redirect(['myapp.com'], ''), None) def test_dict_redirect(self): self.assertEqual(sanitize_redirect(['myapp.com'], {}), None) def test_invalid_redirect(self): self.assertEqual(sanitize_redirect(['myapp.com'], {'foo': 'bar'}), None) def test_wrong_path_redirect(self): self.assertEqual( sanitize_redirect(['myapp.com'], 'http://notmyapp.com/path/'), None ) def test_invalid_evil_redirect(self): self.assertEqual(sanitize_redirect(['myapp.com'], '///evil.com'), None) def test_valid_absolute_redirect(self): self.assertEqual( sanitize_redirect(['myapp.com'], 'http://myapp.com/path/'), 'http://myapp.com/path/' ) def test_valid_relative_redirect(self): self.assertEqual(sanitize_redirect(['myapp.com'], '/path/'), '/path/') def test_multiple_hosts(self): allowed_hosts = ['myapp1.com', 'myapp2.com'] for host in allowed_hosts: url = 'http://{}/path/'.format(host) self.assertEqual(sanitize_redirect(allowed_hosts, url), url) def test_multiple_hosts_wrong_host(self): self.assertEqual(sanitize_redirect( ['myapp1.com', 'myapp2.com'], 'http://notmyapp.com/path/'), None) class UserIsAuthenticatedTest(unittest.TestCase): def test_user_is_none(self): self.assertEqual(user_is_authenticated(None), False) def test_user_is_not_none(self): self.assertEqual(user_is_authenticated(object()), True) def test_user_has_is_authenticated(self): class User(object): is_authenticated = True self.assertEqual(user_is_authenticated(User()), True) def test_user_has_is_authenticated_callable(self): class User(object): def is_authenticated(self): return True self.assertEqual(user_is_authenticated(User()), True) class UserIsActiveTest(unittest.TestCase): def test_user_is_none(self): self.assertEqual(user_is_active(None), False) def test_user_is_not_none(self): self.assertEqual(user_is_active(object()), True) def test_user_has_is_active(self): class User(object): is_active = True self.assertEqual(user_is_active(User()), True) def test_user_has_is_active_callable(self): class User(object): def is_active(self): return True self.assertEqual(user_is_active(User()), True) class SlugifyTest(unittest.TestCase): def test_slugify_formats(self): if PY3: self.assertEqual(slugify('FooBar'), 'foobar') self.assertEqual(slugify('Foo Bar'), 'foo-bar') self.assertEqual(slugify('Foo (Bar)'), 'foo-bar') else: self.assertEqual(slugify('FooBar'.decode('utf-8')), 'foobar') self.assertEqual(slugify('Foo Bar'.decode('utf-8')), 'foo-bar') self.assertEqual(slugify('Foo (Bar)'.decode('utf-8')), 'foo-bar') class BuildAbsoluteURITest(unittest.TestCase): def setUp(self): self.host = 'http://foobar.com' def tearDown(self): self.host = None def test_path_none(self): self.assertEqual(build_absolute_uri(self.host), self.host) def test_path_empty(self): self.assertEqual(build_absolute_uri(self.host, ''), self.host) def test_path_http(self): self.assertEqual(build_absolute_uri(self.host, 'http://barfoo.com'), 'http://barfoo.com') def test_path_https(self): self.assertEqual(build_absolute_uri(self.host, 'https://barfoo.com'), 'https://barfoo.com') def test_host_ends_with_slash_and_path_starts_with_slash(self): self.assertEqual(build_absolute_uri(self.host + '/', '/foo/bar'), 'http://foobar.com/foo/bar') def test_absolute_uri(self): self.assertEqual(build_absolute_uri(self.host, '/foo/bar'), 'http://foobar.com/foo/bar') class PartialPipelineData(unittest.TestCase): def test_returns_partial_when_uid_and_email_do_match(self): email = 'foo@example.com' backend = self._backend({'uid': email}) backend.strategy.request_data.return_value = { backend.ID_KEY: email } key, val = ('foo', 'bar') partial = partial_pipeline_data(backend, None, *(), **dict([(key, val)])) self.assertTrue(key in partial.kwargs) self.assertEqual(partial.kwargs[key], val) self.assertEqual(backend.strategy.clean_partial_pipeline.call_count, 0) def test_clean_pipeline_when_uid_does_not_match(self): backend = self._backend({'uid': 'foo@example.com'}) backend.strategy.request_data.return_value = { backend.ID_KEY: 'bar@example.com' } key, val = ('foo', 'bar') partial = partial_pipeline_data(backend, None, *(), **dict([(key, val)])) self.assertIsNone(partial) self.assertEqual(backend.strategy.clean_partial_pipeline.call_count, 1) def test_kwargs_included_in_result(self): backend = self._backend() key, val = ('foo', 'bar') partial = partial_pipeline_data(backend, None, *(), **dict([(key, val)])) self.assertTrue(key in partial.kwargs) self.assertEqual(partial.kwargs[key], val) self.assertEqual(backend.strategy.clean_partial_pipeline.call_count, 0) def test_update_user(self): user = object() backend = self._backend(session_kwargs={'user': None}) partial = partial_pipeline_data(backend, user) self.assertTrue('user' in partial.kwargs) self.assertEqual(partial.kwargs['user'], user) self.assertEqual(backend.strategy.clean_partial_pipeline.call_count, 0) def _backend(self, session_kwargs=None): backend = Mock() backend.ID_KEY = 'email' backend.name = 'mock-backend' strategy = Mock() strategy.request = None strategy.request_data.return_value = {} strategy.session_get.return_value = object() strategy.partial_load.return_value = TestPartial.prepare(backend.name, 0, { 'args': [], 'kwargs': session_kwargs or {} }) backend.strategy = strategy return backend social-auth-core-3.1.0/social_core/tests/__init__.py0000644000175000017500000000000013336542652022264 0ustar omabomab00000000000000social-auth-core-3.1.0/social_core/tests/testkey.pem0000644000175000017500000000321713336542652022363 0ustar omabomab00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEAtavb41y83n7xY/sIS23Rv+TEwI85xmML57BT9dyb9JeAFPYG RIaN/kWYW8pR0CZDu2nLwU2lUA0d0wKTOMaHQFs6L/rPN1zOx13HYO+TKybgU+e8 TaOncKmJeyp3MP/OM6KBYp7Cx6eLlyvw3HJh6nT+Y5vj3rwS4PYjX1hUcr51IOcq Ls5Sx/UQ4OUVHJLlcUxP6k2n44nGoDYK8zRc5dEBxbTL4yKzCR9unWAlDHVQYXOx vzxxFtUID7dy38e0MZ+3RnmIY2wl7/PIRCsbIKrsAiONnLkJQitMVkU8jTEPrXNC Lj5Hzgf0kDD+4ukQdUPCXlv9y668anaxabXFIwIDAQABAoIBAQCUAfT+UiKpnoBp dS53zXvSO740Jzvpre4YD2cH6njAl+FIsnu+vmTWoLqVJcrrqxEJkhLnZKvnARio PfPVkKDGRMyuJEdIEnuAn519ExIyWNTwZt7Z3hffSLByQNGkgPPsy1tgDnogERc/ zRPJdgLh8fkDDBFk+JLk5oNA/YSnibV5MtriNPoDONxaHsguJ7JvikgLdQs5nyuQ loMx4JhqP+fXsGIU0DFwaLPvEEntKodaRZXvAMLOuGi3O83OGkhevVvEVFSzCeS1 fYnKwySQgNctztoBpf09zATeL9xt8tipVnyBKOIBJyf/BQV7fESXp0R8d+rW1cs0 21qakP0pAoGBAOUFPT/pLP3w676dGvggK0kDy8gjgDHl2/x3fhbeua/6yfetVpZE CuFez/9E24kMS4/GCi3WgPA6DHDYBH5t1S4e0/cgGJ8sjuX8ELLgy4q+WmvgTwDS FbXKqY929Hlu65hUvl2xVP2MYUGLHiUE8yKk4RKdZO+3GkWXHhq9A+mvAoGBAMsS rBYyAt9XeI8W8w2dWgZnBp+7WQeMUAZfCpq65urycD9on9R3gJ5zbOfq6CG2FXML kslY1R4CVYge+HoWITFECvwPdbDYx/QIVvuhRH8omrG5iYY+ljGuF6wlzETgW117 vYTSf5xXJ4LiuFD6TY4rxZ0UdjKba9bJfsQb45zNAoGBAMm/zM4xVn46LyLS+YAd rqP6oRklFPhf+mQ0y7HP8Royvprea0milUcmI+tHOHJJj4MPPcZVkW6OZIk/8u2B SewAIlAwSSBnu1akr/00hHor6DHh/xbE+3UTHD4S15jl+stN1Alrf2iAsuhvalXI P3uEbfXdC58U2SL21gFOA1VnAoGBAIv93EoUwewrhb3GyVlaXyRIw8U7xv0wGj66 KDpQnD3dUco0mvJCS5Vv5uTeCJasyo6brN5G8ewVGdeT2iF3vfwTdysakTyPxRAS 67veCbVLTZE1YXv9C1dGN6WCDRZyQCnq0tSMtFIXtvJAz2VrmClpPXqzD2SFxkq0 b0JMI5YFAoGAL+1E+JUfPHf69K8XDXY+zrZL/KRPuXHcGk4qm7JXCDxr90zMVJYj RWTQZHgGCEZGGlChJD8JwGiWEtsn0zlhn4H4dPWzeS7D1fsJK30lhYpDp3TuOY0x lQAatQXzunglAxXwlOOazX4uoVtiFLjY3U4G6gCwKYEtjP1r0GULlk0= -----END RSA PRIVATE KEY----- social-auth-core-3.1.0/social_core/tests/requirements-pypy.txt0000644000175000017500000000006613336542652024452 0ustar omabomab00000000000000-r requirements-base.txt unittest2==0.5.1 mock==1.0.1 social-auth-core-3.1.0/social_core/exceptions.py0000644000175000017500000000655213336542652021566 0ustar omabomab00000000000000class SocialAuthBaseException(ValueError): """Base class for pipeline exceptions.""" pass class WrongBackend(SocialAuthBaseException): def __init__(self, backend_name): self.backend_name = backend_name def __str__(self): return 'Incorrect authentication service "{0}"'.format( self.backend_name ) class MissingBackend(WrongBackend): def __str__(self): return 'Missing backend "{0}" entry'.format(self.backend_name) class NotAllowedToDisconnect(SocialAuthBaseException): """User is not allowed to disconnect it's social account.""" pass class AuthException(SocialAuthBaseException): """Auth process exception.""" def __init__(self, backend, *args, **kwargs): self.backend = backend super(AuthException, self).__init__(*args, **kwargs) class AuthFailed(AuthException): """Auth process failed for some reason.""" def __str__(self): msg = super(AuthFailed, self).__str__() if msg == 'access_denied': return 'Authentication process was canceled' return 'Authentication failed: {0}'.format(msg) class AuthCanceled(AuthException): """Auth process was canceled by user.""" def __init__(self, *args, **kwargs): self.response = kwargs.pop('response', None) super(AuthCanceled, self).__init__(*args, **kwargs) def __str__(self): msg = super(AuthCanceled, self).__str__() if msg: return 'Authentication process canceled: {0}'.format(msg) return 'Authentication process canceled' class AuthUnknownError(AuthException): """Unknown auth process error.""" def __str__(self): msg = super(AuthUnknownError, self).__str__() return 'An unknown error happened while authenticating {0}'.format(msg) class AuthTokenError(AuthException): """Auth token error.""" def __str__(self): msg = super(AuthTokenError, self).__str__() return 'Token error: {0}'.format(msg) class AuthMissingParameter(AuthException): """Missing parameter needed to start or complete the process.""" def __init__(self, backend, parameter, *args, **kwargs): self.parameter = parameter super(AuthMissingParameter, self).__init__(backend, *args, **kwargs) def __str__(self): return 'Missing needed parameter {0}'.format(self.parameter) class AuthStateMissing(AuthException): """State parameter is incorrect.""" def __str__(self): return 'Session value state missing.' class AuthStateForbidden(AuthException): """State parameter is incorrect.""" def __str__(self): return 'Wrong state parameter given.' class AuthAlreadyAssociated(AuthException): """A different user has already associated the target social account""" pass class AuthTokenRevoked(AuthException): """User revoked the access_token in the provider.""" def __str__(self): return 'User revoke access to the token' class AuthForbidden(AuthException): """Authentication for this user is forbidden""" def __str__(self): return 'Your credentials aren\'t allowed' class AuthUnreachableProvider(AuthException): """Cannot reach the provider""" def __str__(self): return 'The authentication provider could not be reached' class InvalidEmail(AuthException): def __str__(self): return 'Email couldn\'t be validated' social-auth-core-3.1.0/social_core/storage.py0000644000175000017500000002446413336542652021053 0ustar omabomab00000000000000"""Models mixins for Social Auth""" import re import time import base64 import uuid import warnings from datetime import datetime, timedelta import six from openid.association import Association as OpenIdAssociation from .exceptions import MissingBackend from .backends.utils import get_backend NO_ASCII_REGEX = re.compile(r'[^\x00-\x7F]+') NO_SPECIAL_REGEX = re.compile(r'[^\w.@+_-]+', re.UNICODE) class UserMixin(object): # Consider tokens that expire in 5 seconds as already expired ACCESS_TOKEN_EXPIRED_THRESHOLD = 5 user = '' provider = '' uid = None extra_data = None def get_backend(self, strategy): return get_backend(strategy.get_backends(), self.provider) def get_backend_instance(self, strategy): try: backend_class = self.get_backend(strategy) except MissingBackend: return None else: return backend_class(strategy=strategy) @property def access_token(self): """Return access_token stored in extra_data or None""" return self.extra_data.get('access_token') @property def tokens(self): warnings.warn('tokens is deprecated, use access_token instead') return self.access_token def refresh_token(self, strategy, *args, **kwargs): token = self.extra_data.get('refresh_token') or \ self.extra_data.get('access_token') backend = self.get_backend(strategy) if token and backend and hasattr(backend, 'refresh_token'): backend = backend(strategy=strategy) response = backend.refresh_token(token, *args, **kwargs) extra_data = backend.extra_data(self, self.uid, response, self.extra_data) if self.set_extra_data(extra_data): self.save() def expiration_timedelta(self): """Return provider session live seconds. Returns a timedelta ready to use with session.set_expiry(). If provider returns a timestamp instead of session seconds to live, the timedelta is inferred from current time (using UTC timezone). None is returned if there's no value stored or it's invalid. """ if self.extra_data and 'expires' in self.extra_data: try: expires = int(self.extra_data.get('expires')) except (ValueError, TypeError): return None now = datetime.utcnow() # Detect if expires is a timestamp if expires > time.mktime(now.timetuple()): # expires is a datetime, return the remaining difference return datetime.utcfromtimestamp(expires) - now else: # expires is the time to live seconds since creation, # check against auth_time if present, otherwise return # the value auth_time = self.extra_data.get('auth_time') if auth_time: reference = datetime.utcfromtimestamp(auth_time) return (reference + timedelta(seconds=expires)) - now else: return timedelta(seconds=expires) def expiration_datetime(self): # backward compatible alias return self.expiration_timedelta() def access_token_expired(self): """Return true / false if access token is already expired""" expiration = self.expiration_timedelta() return expiration and \ expiration.total_seconds() <= self.ACCESS_TOKEN_EXPIRED_THRESHOLD def get_access_token(self, strategy): """Returns a valid access token.""" if self.access_token_expired(): self.refresh_token(strategy) return self.access_token def set_extra_data(self, extra_data=None): if extra_data and self.extra_data != extra_data: if self.extra_data and not isinstance( self.extra_data, six.string_types): self.extra_data.update(extra_data) else: self.extra_data = extra_data return True @classmethod def clean_username(cls, value): """Clean username removing any unsupported character""" value = NO_ASCII_REGEX.sub('', value) value = NO_SPECIAL_REGEX.sub('', value) return value @classmethod def changed(cls, user): """The given user instance is ready to be saved""" raise NotImplementedError('Implement in subclass') @classmethod def get_username(cls, user): """Return the username for given user""" raise NotImplementedError('Implement in subclass') @classmethod def user_model(cls): """Return the user model""" raise NotImplementedError('Implement in subclass') @classmethod def username_max_length(cls): """Return the max length for username""" raise NotImplementedError('Implement in subclass') @classmethod def allowed_to_disconnect(cls, user, backend_name, association_id=None): """Return if it's safe to disconnect the social account for the given user""" raise NotImplementedError('Implement in subclass') @classmethod def disconnect(cls, entry): """Disconnect the social account for the given user""" raise NotImplementedError('Implement in subclass') @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. """ raise NotImplementedError('Implement in subclass') @classmethod def create_user(cls, *args, **kwargs): """Create a user instance""" raise NotImplementedError('Implement in subclass') @classmethod def get_user(cls, pk): """Return user instance for given id""" raise NotImplementedError('Implement in subclass') @classmethod def get_users_by_email(cls, email): """Return users instances for given email address""" raise NotImplementedError('Implement in subclass') @classmethod def get_social_auth(cls, provider, uid): """Return UserSocialAuth for given provider and uid""" raise NotImplementedError('Implement in subclass') @classmethod def get_social_auth_for_user(cls, user, provider=None, id=None): """Return all the UserSocialAuth instances for given user""" raise NotImplementedError('Implement in subclass') @classmethod def create_social_auth(cls, user, uid, provider): """Create a UserSocialAuth instance for given user""" raise NotImplementedError('Implement in subclass') class NonceMixin(object): """One use numbers""" server_url = '' timestamp = 0 salt = '' @classmethod def use(cls, server_url, timestamp, salt): """Create a Nonce instance""" raise NotImplementedError('Implement in subclass') class AssociationMixin(object): """OpenId account association""" server_url = '' handle = '' secret = '' issued = 0 lifetime = 0 assoc_type = '' @classmethod def oids(cls, server_url, handle=None): kwargs = {'server_url': server_url} if handle is not None: kwargs['handle'] = handle return sorted([ (assoc.id, cls.openid_association(assoc)) for assoc in cls.get(**kwargs) ], key=lambda x: x[1].issued, reverse=True) @classmethod def openid_association(cls, assoc): secret = assoc.secret if not isinstance(secret, six.binary_type): secret = secret.encode() return OpenIdAssociation(assoc.handle, base64.decodestring(secret), assoc.issued, assoc.lifetime, assoc.assoc_type) @classmethod def store(cls, server_url, association): """Create an Association instance""" raise NotImplementedError('Implement in subclass') @classmethod def get(cls, *args, **kwargs): """Get an Association instance""" raise NotImplementedError('Implement in subclass') @classmethod def remove(cls, ids_to_delete): """Remove an Association instance""" raise NotImplementedError('Implement in subclass') class CodeMixin(object): email = '' code = '' verified = False def verify(self): self.verified = True self.save() @classmethod def generate_code(cls): return uuid.uuid4().hex @classmethod def make_code(cls, email): code = cls() code.email = email code.code = cls.generate_code() code.verified = False code.save() return code @classmethod def get_code(cls, code): raise NotImplementedError('Implement in subclass') class PartialMixin(object): token = '' data = '' next_step = '' backend = '' @property def args(self): return self.data.get('args', []) @args.setter def args(self, value): self.data['args'] = value @property def kwargs(self): return self.data.get('kwargs', {}) @kwargs.setter def kwargs(self, value): self.data['kwargs'] = value def extend_kwargs(self, values): self.data['kwargs'].update(values) @classmethod def generate_token(cls): return uuid.uuid4().hex @classmethod def load(cls, token): raise NotImplementedError('Implement in subclass') @classmethod def destroy(cls, token): raise NotImplementedError('Implement in subclass') @classmethod def prepare(cls, backend, next_step, data): partial = cls() partial.backend = backend partial.next_step = next_step partial.data = data partial.token = cls.generate_token() return partial @classmethod def store(cls, partial): partial.save() return partial class BaseStorage(object): user = UserMixin nonce = NonceMixin association = AssociationMixin code = CodeMixin partial = PartialMixin @classmethod def is_integrity_error(cls, exception): """Check if given exception flags an integrity error in the DB""" raise NotImplementedError('Implement in subclass') social-auth-core-3.1.0/social_core/__init__.py0000644000175000017500000000002613433256332021125 0ustar omabomab00000000000000__version__ = '3.1.0' social-auth-core-3.1.0/social_core/pipeline/0000755000175000017500000000000013433256445020630 5ustar omabomab00000000000000social-auth-core-3.1.0/social_core/pipeline/mail.py0000644000175000017500000000247013336542652022127 0ustar omabomab00000000000000from ..exceptions import InvalidEmail from .partial import partial @partial def mail_validation(backend, details, is_new=False, *args, **kwargs): requires_validation = backend.REQUIRES_EMAIL_VALIDATION or \ backend.setting('FORCE_EMAIL_VALIDATION', False) send_validation = details.get('email') and \ (is_new or backend.setting('PASSWORDLESS', False)) if requires_validation and send_validation: data = backend.strategy.request_data() if 'verification_code' in data: backend.strategy.session_pop('email_validation_address') if not backend.strategy.validate_email(details['email'], data['verification_code']): raise InvalidEmail(backend) else: current_partial = kwargs.get('current_partial') backend.strategy.send_email_validation(backend, details['email'], current_partial.token) backend.strategy.session_set('email_validation_address', details['email']) return backend.strategy.redirect( backend.strategy.setting('EMAIL_VALIDATION_URL') ) social-auth-core-3.1.0/social_core/pipeline/social_auth.py0000644000175000017500000000636113365410437023500 0ustar omabomab00000000000000from ..exceptions import AuthAlreadyAssociated, AuthException, AuthForbidden def social_details(backend, details, response, *args, **kwargs): return {'details': dict(backend.get_user_details(response), **details)} def social_uid(backend, details, response, *args, **kwargs): return {'uid': backend.get_user_id(details, response)} def auth_allowed(backend, details, response, *args, **kwargs): if not backend.auth_allowed(response, details): raise AuthForbidden(backend) def social_user(backend, uid, user=None, *args, **kwargs): provider = backend.name social = backend.strategy.storage.user.get_social_auth(provider, uid) if social: if user and social.user != user: msg = 'This account is already in use.' raise AuthAlreadyAssociated(backend, msg) elif not user: user = social.user return {'social': social, 'user': user, 'is_new': user is None, 'new_association': social is None} def associate_user(backend, uid, user=None, social=None, *args, **kwargs): if user and not social: try: social = backend.strategy.storage.user.create_social_auth( user, uid, backend.name ) except Exception as err: if not backend.strategy.storage.is_integrity_error(err): raise # Protect for possible race condition, those bastard with FTL # clicking capabilities, check issue #131: # https://github.com/omab/django-social-auth/issues/131 return social_user(backend, uid, user, *args, **kwargs) else: return {'social': social, 'user': social.user, 'new_association': True} def associate_by_email(backend, details, user=None, *args, **kwargs): """ Associate current auth with a user with the same email address in the DB. This pipeline entry is not 100% secure unless you know that the providers enabled enforce email verification on their side, otherwise a user can attempt to take over another user account by using the same (not validated) email address on some provider. This pipeline entry is disabled by default. """ if user: return None email = details.get('email') if email: # Try to associate accounts registered with the same email address, # only if it's a single object. AuthException is raised if multiple # objects are returned. users = list(backend.strategy.storage.user.get_users_by_email(email)) if len(users) == 0: return None elif len(users) > 1: raise AuthException( backend, 'The given email address is associated with another account' ) else: return {'user': users[0], 'is_new': False} def load_extra_data(backend, details, response, uid, user, *args, **kwargs): social = kwargs.get('social') or \ backend.strategy.storage.user.get_social_auth(backend.name, uid) if social: extra_data = backend.extra_data(user, uid, response, details, *args, **kwargs) social.set_extra_data(extra_data) social-auth-core-3.1.0/social_core/pipeline/utils.py0000644000175000017500000000475613336542652022356 0ustar omabomab00000000000000import six SERIALIZABLE_TYPES = (dict, list, tuple, set, bool, type(None)) + \ six.integer_types + six.string_types + \ (six.text_type, six.binary_type,) def is_dict_type(value): """Treat any dict, MergeDict, MultiDict instance as dict type""" # Check by class name to avoid importing Django MergeDict or # Werkzeug MultiDict return isinstance(value, dict) or \ value.__class__.__name__ in ('MergeDict', 'MultiDict') def partial_prepare(strategy, backend, next_step, user=None, social=None, *args, **kwargs): kwargs.update({ 'response': kwargs.get('response') or {}, 'details': kwargs.get('details') or {}, 'username': kwargs.get('username'), 'uid': kwargs.get('uid'), 'is_new': kwargs.get('is_new') or False, 'new_association': kwargs.get('new_association') or False, 'user': user and user.id or None, 'social': social and { 'provider': social.provider, 'uid': social.uid } or None }) clean_args = [strategy.to_session_value(val) for val in args] # Clean any MergeDict data type from the values clean_kwargs = {} for name, value in kwargs.items(): value = dict(value) if is_dict_type(value) else value if isinstance(value, SERIALIZABLE_TYPES): clean_kwargs[name] = strategy.to_session_value(value) return strategy.storage.partial.prepare(backend.name, next_step, { 'args': clean_args, 'kwargs': clean_kwargs }) def partial_store(strategy, backend, next_step, user=None, social=None, *args, **kwargs): partial = partial_prepare(strategy, backend, next_step, user=user, social=social, *args, **kwargs) return strategy.storage.partial.store(partial) def partial_load(strategy, token): partial = strategy.storage.partial.load(token) if partial: args = partial.args kwargs = partial.kwargs.copy() user = kwargs.get('user') social = kwargs.get('social') if isinstance(social, dict): kwargs['social'] = strategy.storage.user.get_social_auth(**social) if user: kwargs['user'] = strategy.storage.user.get_user(user) partial.args = [strategy.from_session_value(val) for val in args] partial.kwargs = dict((key, strategy.from_session_value(val)) for key, val in kwargs.items()) return partial social-auth-core-3.1.0/social_core/pipeline/__init__.py0000644000175000017500000000463613336542652022752 0ustar omabomab00000000000000DEFAULT_AUTH_PIPELINE = ( # Get the information we can about the user and return it in a simple # format to create the user instance later. On some cases the details are # already part of the auth response from the provider, but sometimes this # could hit a provider API. 'social_core.pipeline.social_auth.social_details', # Get the social uid from whichever service we're authing thru. The uid is # the unique identifier of the given user in the provider. 'social_core.pipeline.social_auth.social_uid', # Verifies that the current auth process is valid within the current # project, this is where emails and domains whitelists are applied (if # defined). 'social_core.pipeline.social_auth.auth_allowed', # Checks if the current social-account is already associated in the site. 'social_core.pipeline.social_auth.social_user', # Make up a username for this person, appends a random string at the end if # there's any collision. 'social_core.pipeline.user.get_username', # Send a validation email to the user to verify its email address. # 'social_core.pipeline.mail.mail_validation', # Associates the current social details with another user account with # a similar email address. # 'social_core.pipeline.social_auth.associate_by_email', # Create a user account if we haven't found one yet. 'social_core.pipeline.user.create_user', # Create the record that associated the social account with this user. 'social_core.pipeline.social_auth.associate_user', # Populate the extra_data field in the social record with the values # specified by settings (and the default ones like access_token, etc). 'social_core.pipeline.social_auth.load_extra_data', # Update the user record with any changed info from the auth service. 'social_core.pipeline.user.user_details' ) DEFAULT_DISCONNECT_PIPELINE = ( # Verifies that the social association can be disconnected from the current # user (ensure that the user login mechanism is not compromised by this # disconnection). 'social_core.pipeline.disconnect.allowed_to_disconnect', # Collects the social associations to disconnect. 'social_core.pipeline.disconnect.get_entries', # Revoke any access_token when possible. 'social_core.pipeline.disconnect.revoke_tokens', # Removes the social associations. 'social_core.pipeline.disconnect.disconnect' ) social-auth-core-3.1.0/social_core/pipeline/debug.py0000644000175000017500000000037413336542652022274 0ustar omabomab00000000000000from pprint import pprint def debug(response, details, *args, **kwargs): print('=' * 80) pprint(response) print('=' * 80) pprint(details) print('=' * 80) pprint(args) print('=' * 80) pprint(kwargs) print('=' * 80) social-auth-core-3.1.0/social_core/pipeline/partial.py0000644000175000017500000000343013336542652022636 0ustar omabomab00000000000000from functools import wraps from .utils import partial_prepare from ..utils import PARTIAL_TOKEN_SESSION_NAME def partial_step(save_to_session): """Wraps func to behave like a partial pipeline step, any output that's not None or {} will be considered a response object and will be returned to user. The pipeline function will receive a current_partial object, it contains the partial pipeline data and a token that is used to identify it when it's continued, this is useful to build links with the token. The default value for this parameter is partial_token, but can be overridden by SOCIAL_AUTH_PARTIAL_PIPELINE_TOKEN_NAME setting. The token is also stored in the session under the PARTIAL_TOKEN_SESSION_NAME (partial_pipeline_token) key when the save_to_session parameter is True. """ def decorator(func): @wraps(func) def wrapper(strategy, backend, pipeline_index, *args, **kwargs): current_partial = partial_prepare(strategy, backend, pipeline_index, *args, **kwargs) out = func(strategy=strategy, backend=backend, pipeline_index=pipeline_index, current_partial=current_partial, *args, **kwargs) or {} if not isinstance(out, dict): strategy.storage.partial.store(current_partial) if save_to_session: strategy.session_set(PARTIAL_TOKEN_SESSION_NAME, current_partial.token) return out return wrapper return decorator # Backward compatible partial decorator, that stores the token in the session partial = partial_step(save_to_session=True) social-auth-core-3.1.0/social_core/pipeline/disconnect.py0000644000175000017500000000206513336542652023336 0ustar omabomab00000000000000from ..exceptions import NotAllowedToDisconnect def allowed_to_disconnect(strategy, user, name, user_storage, association_id=None, *args, **kwargs): if not user_storage.allowed_to_disconnect(user, name, association_id): raise NotAllowedToDisconnect() def get_entries(strategy, user, name, user_storage, association_id=None, *args, **kwargs): return { 'entries': user_storage.get_social_auth_for_user( user, name, association_id ) } def revoke_tokens(strategy, entries, *args, **kwargs): revoke_tokens = strategy.setting('REVOKE_TOKENS_ON_DISCONNECT', False) if revoke_tokens: for entry in entries: if 'access_token' in entry.extra_data: backend = entry.get_backend(strategy)(strategy) backend.revoke_token(entry.extra_data['access_token'], entry.uid) def disconnect(strategy, entries, user_storage, *args, **kwargs): for entry in entries: user_storage.disconnect(entry) social-auth-core-3.1.0/social_core/pipeline/user.py0000644000175000017500000000722213417122006022146 0ustar omabomab00000000000000from uuid import uuid4 from ..utils import slugify, module_member USER_FIELDS = ['username', 'email'] def get_username(strategy, details, backend, user=None, *args, **kwargs): if 'username' not in backend.setting('USER_FIELDS', USER_FIELDS): return storage = strategy.storage if not user: email_as_username = strategy.setting('USERNAME_IS_FULL_EMAIL', False) uuid_length = strategy.setting('UUID_LENGTH', 16) max_length = storage.user.username_max_length() do_slugify = strategy.setting('SLUGIFY_USERNAMES', False) do_clean = strategy.setting('CLEAN_USERNAMES', True) if do_clean: override_clean = strategy.setting('CLEAN_USERNAME_FUNCTION') if override_clean: clean_func = module_member(override_clean) else: clean_func = storage.user.clean_username else: clean_func = lambda val: val if do_slugify: override_slug = strategy.setting('SLUGIFY_FUNCTION') if override_slug: slug_func = module_member(override_slug) else: slug_func = slugify else: slug_func = lambda val: val if email_as_username and details.get('email'): username = details['email'] elif details.get('username'): username = details['username'] else: username = uuid4().hex short_username = (username[:max_length - uuid_length] if max_length is not None else username) final_username = slug_func(clean_func(username[:max_length])) # Generate a unique username for current user using username # as base but adding a unique hash at the end. Original # username is cut to avoid any field max_length. # The final_username may be empty and will skip the loop. while not final_username or \ storage.user.user_exists(username=final_username): username = short_username + uuid4().hex[:uuid_length] final_username = slug_func(clean_func(username[:max_length])) else: final_username = storage.user.get_username(user) return {'username': final_username} def create_user(strategy, details, backend, user=None, *args, **kwargs): if user: return {'is_new': False} fields = dict((name, kwargs.get(name, details.get(name))) for name in backend.setting('USER_FIELDS', USER_FIELDS)) if not fields: return return { 'is_new': True, 'user': strategy.create_user(**fields) } def user_details(strategy, details, user=None, *args, **kwargs): """Update user details using data from provider.""" if not user: return changed = False # flag to track changes protected = ('username', 'id', 'pk', 'email') + \ tuple(strategy.setting('PROTECTED_USER_FIELDS', [])) # Update user model attributes with the new data sent by the current # provider. Update on some attributes is disabled by default, for # example username and id fields. It's also possible to disable update # on fields defined in SOCIAL_AUTH_PROTECTED_USER_FIELDS. for name, value in details.items(): if value is None or not hasattr(user, name) or name in protected: continue # Check https://github.com/omab/python-social-auth/issues/671 current_value = getattr(user, name, None) if current_value or current_value == value: continue changed = True setattr(user, name, value) if changed: strategy.storage.user.changed(user) social-auth-core-3.1.0/social_core/actions.py0000644000175000017500000001276513374654570021055 0ustar omabomab00000000000000from six.moves.urllib_parse import quote from .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): allowed_hosts = backend.setting('ALLOWED_REDIRECT_HOSTS', []) + \ [backend.strategy.request_host()] redirect_uri = sanitize_redirect(allowed_hosts, 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): data = backend.strategy.request_data() is_authenticated = user_is_authenticated(user) user = user if is_authenticated else None partial = partial_pipeline_data(backend, user, *args, **kwargs) if partial: user = backend.continue_pipeline(partial) # clean partial data after usage backend.strategy.clean_partial_pipeline(partial.token) else: user = backend.complete(user=user, *args, **kwargs) # pop redirect value before the session is trashed on login(), but after # the pipeline so that the pipeline can change the redirect if needed redirect_value = backend.strategy.session_get(redirect_name, '') or \ data.get(redirect_name, '') # check if the output value is something else than a user and just # return it to the client 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: if backend.setting('INACTIVE_USER_LOGIN', False): social_user = user.social_user login(backend, user, social_user) 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 += ('&' if '?' in url else '?') + \ '{0}={1}'.format(redirect_name, redirect_value) if backend.setting('SANITIZE_REDIRECTS', True): allowed_hosts = backend.setting('ALLOWED_REDIRECT_HOSTS', []) + \ [backend.strategy.request_host()] url = sanitize_redirect(allowed_hosts, 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: if association_id and not partial.kwargs.get('association_id'): partial.extend_kwargs({ 'association_id': association_id }) response = backend.disconnect(*partial.args, **partial.kwargs) # clean partial data after usage backend.strategy.clean_partial_pipeline(partial.token) else: response = backend.disconnect(user=user, association_id=association_id, *args, **kwargs) if isinstance(response, dict): url = backend.strategy.absolute_uri( backend.strategy.request_data().get(redirect_name, '') or backend.setting('DISCONNECT_REDIRECT_URL') or backend.setting('LOGIN_REDIRECT_URL') ) if backend.setting('SANITIZE_REDIRECTS', True): allowed_hosts = backend.setting('ALLOWED_REDIRECT_HOSTS', []) + \ [backend.strategy.request_host()] url = sanitize_redirect(allowed_hosts, url) or \ backend.setting('DISCONNECT_REDIRECT_URL') or \ backend.setting('LOGIN_REDIRECT_URL') response = backend.strategy.redirect(url) return response social-auth-core-3.1.0/setup.py0000644000175000017500000000742013432552635016255 0ustar omabomab00000000000000# -*- coding: utf-8 -*- import sys import re import os from os.path import join, dirname from setuptools import setup VERSION_RE = re.compile(r'__version__ = \'([\d\.]+)\'') LONG_DESCRIPTION = """ Python Social Auth is an easy to setup social authentication/registration mechanism with support for several frameworks and auth providers. It implements a common interface to define new authentication providers from third parties. And to bring support for more frameworks and ORMs. """ def long_description(): try: return open(join(dirname(__file__), 'README.md')).read() except IOError: return None def read_version(): with open('social_core/__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 read_requirements(filename): with open(filename, 'r') as file: return [line for line in file.readlines() if not line.startswith('-')] def read_tests_requirements(filename): return read_requirements('social_core/tests/{0}'.format(filename)) PY = os.environ.get("BUILD_VERSION") or sys.version_info[0] requirements = read_requirements('requirements-base.txt') # May be able to just use environment markers in requirements-base.txt # at least on setuptools 36.2.0 and up. requirements_py2 = read_requirements('requirements-python2.txt') requirements_py3 = read_requirements('requirements-python3.txt') requirements_openidconnect = read_requirements('requirements-openidconnect.txt') requirements_saml = read_requirements('requirements-saml-python%s.txt' % PY) requirements_azuread = read_requirements('requirements-azuread.txt') tests_requirements_base = read_tests_requirements('requirements-base.txt') tests_requirements = tests_requirements_base + \ read_tests_requirements('requirements-python%s.txt' % PY) requirements_all = requirements_openidconnect + \ requirements_saml + \ requirements_azuread tests_requirements = tests_requirements + requirements_all setup( name='social-auth-core', version=read_version(), author='Matias Aguirre', author_email='matiasaguirre@gmail.com', description='Python social authentication made simple.', license='BSD', keywords='openid, oauth, saml, social auth', url='https://github.com/python-social-auth/social-core', packages=[ 'social_core', 'social_core.backends', 'social_core.pipeline', 'social_core.tests', 'social_core.tests.actions', 'social_core.tests.backends', 'social_core.tests.backends.data' ], long_description=long_description() or LONG_DESCRIPTION, install_requires=requirements, extras_require={ 'openidconnect': [requirements_openidconnect], 'saml': [requirements_saml], 'azuread': [requirements_azuread], 'all': [requirements_all], 'allpy2': [requirements_all, requirements_py2], 'allpy3': [requirements_all, requirements_py3], ':python_version < "3.0"': [requirements_py2], ':python_version >= "3.0"': [requirements_py3], }, 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' ], package_data={ 'social_core/tests': [ 'social_core/tests/*.txt', 'social_core/tests/testkey.pem' ] }, include_package_data=True, tests_require=tests_requirements, test_suite='social_core.tests', zip_safe=False ) social-auth-core-3.1.0/requirements-openidconnect.txt0000644000175000017500000000004013432613345022640 0ustar omabomab00000000000000python-jose>=3.0.0 pyjwt>=1.7.1 social-auth-core-3.1.0/social_auth_core.egg-info/0000755000175000017500000000000013433256445021536 5ustar omabomab00000000000000social-auth-core-3.1.0/social_auth_core.egg-info/not-zip-safe0000644000175000017500000000000113433256435023763 0ustar omabomab00000000000000 social-auth-core-3.1.0/social_auth_core.egg-info/requires.txt0000644000175000017500000000114613433256444024137 0ustar omabomab00000000000000requests>=2.9.1 oauthlib>=1.0.3 requests-oauthlib>=0.6.1 six>=1.10.0 PyJWT>=1.4.0 [:python_version < "3.0"] python-openid>=2.2.5 [:python_version >= "3.0"] defusedxml>=0.5.0rc1 python3-openid>=3.0.10 [all] python-jose>=3.0.0 pyjwt>=1.7.1 python-saml>=2.2.0 cryptography>=2.1.1 [allpy2] python-jose>=3.0.0 pyjwt>=1.7.1 python-saml>=2.2.0 cryptography>=2.1.1 python-openid>=2.2.5 [allpy3] python-jose>=3.0.0 pyjwt>=1.7.1 python-saml>=2.2.0 cryptography>=2.1.1 defusedxml>=0.5.0rc1 python3-openid>=3.0.10 [azuread] cryptography>=2.1.1 [openidconnect] python-jose>=3.0.0 pyjwt>=1.7.1 [saml] python-saml>=2.2.0 social-auth-core-3.1.0/social_auth_core.egg-info/dependency_links.txt0000644000175000017500000000000113433256444025603 0ustar omabomab00000000000000 social-auth-core-3.1.0/social_auth_core.egg-info/top_level.txt0000644000175000017500000000001413433256444024262 0ustar omabomab00000000000000social_core social-auth-core-3.1.0/social_auth_core.egg-info/PKG-INFO0000644000175000017500000000540013433256444022631 0ustar omabomab00000000000000Metadata-Version: 2.1 Name: social-auth-core Version: 3.1.0 Summary: Python social authentication made simple. Home-page: https://github.com/python-social-auth/social-core Author: Matias Aguirre Author-email: matiasaguirre@gmail.com License: BSD Description: # Python Social Auth - Core [![Build Status](https://travis-ci.org/python-social-auth/social-core.svg?branch=master)](https://travis-ci.org/python-social-auth/social-core) [![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) Python Social Auth is an easy to setup social authentication/registration mechanism with support for several frameworks and auth providers. ## Description This is the core component of the python-social-auth ecosystem, it implements the common interface to define new authentication backends to third parties services, implement integrations with web frameworks and storage solutions. ## Documentation Project documentation is available at http://python-social-auth.readthedocs.org/. ## Setup ```shell $ pip install social-auth-core ``` ## 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 maintened 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) Keywords: openid,oauth,saml,social auth Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Topic :: Internet Classifier: License :: OSI Approved :: BSD License Classifier: Intended Audience :: Developers Classifier: Environment :: Web Environment Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Provides-Extra: openidconnect Provides-Extra: saml Provides-Extra: allpy2 Provides-Extra: azuread Provides-Extra: allpy3 Provides-Extra: all social-auth-core-3.1.0/social_auth_core.egg-info/SOURCES.txt0000644000175000017500000002572413433256445023434 0ustar omabomab00000000000000CHANGELOG.md LICENSE MANIFEST.in README.md requirements-azuread.txt requirements-base.txt requirements-openidconnect.txt requirements-python2.txt requirements-python3.txt requirements-saml-python2.txt requirements-saml-python3.txt setup.cfg setup.py social_auth_core.egg-info/PKG-INFO social_auth_core.egg-info/SOURCES.txt social_auth_core.egg-info/dependency_links.txt social_auth_core.egg-info/not-zip-safe social_auth_core.egg-info/requires.txt social_auth_core.egg-info/top_level.txt social_core/__init__.py social_core/actions.py social_core/exceptions.py social_core/storage.py social_core/store.py social_core/strategy.py social_core/utils.py social_core/backends/__init__.py social_core/backends/amazon.py social_core/backends/angel.py social_core/backends/aol.py social_core/backends/appsfuel.py social_core/backends/arcgis.py social_core/backends/asana.py social_core/backends/atlassian.py social_core/backends/auth0.py social_core/backends/azuread.py social_core/backends/azuread_b2c.py social_core/backends/azuread_tenant.py social_core/backends/base.py social_core/backends/battlenet.py social_core/backends/beats.py social_core/backends/behance.py social_core/backends/belgiumeid.py social_core/backends/bitbucket.py social_core/backends/box.py social_core/backends/bungie.py social_core/backends/changetip.py social_core/backends/chatwork.py social_core/backends/classlink.py social_core/backends/clef.py social_core/backends/coding.py social_core/backends/coinbase.py social_core/backends/coursera.py social_core/backends/dailymotion.py social_core/backends/deezer.py social_core/backends/digitalocean.py social_core/backends/discord.py social_core/backends/disqus.py social_core/backends/docker.py social_core/backends/douban.py social_core/backends/dribbble.py social_core/backends/drip.py social_core/backends/dropbox.py social_core/backends/echosign.py social_core/backends/edmodo.py social_core/backends/elixir.py social_core/backends/email.py social_core/backends/eventbrite.py social_core/backends/eveonline.py social_core/backends/evernote.py social_core/backends/exacttarget.py social_core/backends/facebook.py social_core/backends/fedora.py social_core/backends/fitbit.py social_core/backends/five_hundred_px.py social_core/backends/flat.py social_core/backends/flickr.py social_core/backends/foursquare.py social_core/backends/gae.py social_core/backends/github.py social_core/backends/github_enterprise.py social_core/backends/gitlab.py social_core/backends/globus.py social_core/backends/goclio.py social_core/backends/goclioeu.py social_core/backends/google.py social_core/backends/google_openidconnect.py social_core/backends/instagram.py social_core/backends/itembase.py social_core/backends/jawbone.py social_core/backends/justgiving.py social_core/backends/kakao.py social_core/backends/keycloak.py social_core/backends/khanacademy.py social_core/backends/lastfm.py social_core/backends/launchpad.py social_core/backends/legacy.py social_core/backends/line.py social_core/backends/linkedin.py social_core/backends/live.py social_core/backends/livejournal.py social_core/backends/loginradius.py social_core/backends/lyft.py social_core/backends/mailchimp.py social_core/backends/mailru.py social_core/backends/mapmyfitness.py social_core/backends/mediawiki.py social_core/backends/meetup.py social_core/backends/mendeley.py social_core/backends/microsoft.py social_core/backends/mineid.py social_core/backends/mixcloud.py social_core/backends/monzo.py social_core/backends/moves.py social_core/backends/nationbuilder.py social_core/backends/naver.py social_core/backends/ngpvan.py social_core/backends/nk.py social_core/backends/oauth.py social_core/backends/odnoklassniki.py social_core/backends/open_id.py social_core/backends/open_id_connect.py social_core/backends/openshift.py social_core/backends/openstreetmap.py social_core/backends/orbi.py social_core/backends/orcid.py social_core/backends/patreon.py social_core/backends/persona.py social_core/backends/phabricator.py social_core/backends/pinterest.py social_core/backends/pixelpin.py social_core/backends/pocket.py social_core/backends/podio.py social_core/backends/professionali.py social_core/backends/pushbullet.py social_core/backends/qiita.py social_core/backends/qq.py social_core/backends/quizlet.py social_core/backends/rdio.py social_core/backends/readability.py social_core/backends/reddit.py social_core/backends/runkeeper.py social_core/backends/salesforce.py social_core/backends/saml.py social_core/backends/scistarter.py social_core/backends/shimmering.py social_core/backends/shopify.py social_core/backends/sketchfab.py social_core/backends/skyrock.py social_core/backends/slack.py social_core/backends/soundcloud.py social_core/backends/spotify.py social_core/backends/stackoverflow.py social_core/backends/steam.py social_core/backends/stocktwits.py social_core/backends/strava.py social_core/backends/stripe.py social_core/backends/suse.py social_core/backends/taobao.py social_core/backends/telegram.py social_core/backends/thisismyjam.py social_core/backends/trello.py social_core/backends/tripit.py social_core/backends/tumblr.py social_core/backends/twilio.py social_core/backends/twitch.py social_core/backends/twitter.py social_core/backends/uber.py social_core/backends/ubuntu.py social_core/backends/udata.py social_core/backends/universe.py social_core/backends/untappd.py social_core/backends/upwork.py social_core/backends/username.py social_core/backends/utils.py social_core/backends/vend.py social_core/backends/vimeo.py social_core/backends/vk.py social_core/backends/weibo.py social_core/backends/weixin.py social_core/backends/withings.py social_core/backends/wunderlist.py social_core/backends/xing.py social_core/backends/yahoo.py social_core/backends/yammer.py social_core/backends/yandex.py social_core/backends/zotero.py social_core/pipeline/__init__.py social_core/pipeline/debug.py social_core/pipeline/disconnect.py social_core/pipeline/mail.py social_core/pipeline/partial.py social_core/pipeline/social_auth.py social_core/pipeline/user.py social_core/pipeline/utils.py social_core/tests/__init__.py social_core/tests/models.py social_core/tests/pipeline.py social_core/tests/requirements-base.txt social_core/tests/requirements-pypy.txt social_core/tests/requirements-python2.txt social_core/tests/requirements-python3.txt social_core/tests/requirements.txt social_core/tests/strategy.py social_core/tests/test_exceptions.py social_core/tests/test_partial.py social_core/tests/test_pipeline.py social_core/tests/test_storage.py social_core/tests/test_utils.py social_core/tests/testkey.pem social_core/tests/actions/__init__.py social_core/tests/actions/actions.py social_core/tests/actions/test_associate.py social_core/tests/actions/test_disconnect.py social_core/tests/actions/test_login.py social_core/tests/backends/__init__.py social_core/tests/backends/base.py social_core/tests/backends/legacy.py social_core/tests/backends/oauth.py social_core/tests/backends/open_id.py social_core/tests/backends/open_id_connect.py social_core/tests/backends/test_amazon.py social_core/tests/backends/test_angel.py social_core/tests/backends/test_arcgis.py social_core/tests/backends/test_asana.py social_core/tests/backends/test_atlassian.py social_core/tests/backends/test_auth0.py social_core/tests/backends/test_azuread.py social_core/tests/backends/test_azuread_b2c.py social_core/tests/backends/test_behance.py social_core/tests/backends/test_bitbucket.py social_core/tests/backends/test_box.py social_core/tests/backends/test_broken.py social_core/tests/backends/test_chatwork.py social_core/tests/backends/test_clef.py social_core/tests/backends/test_coinbase.py social_core/tests/backends/test_coursera.py social_core/tests/backends/test_dailymotion.py social_core/tests/backends/test_deezer.py social_core/tests/backends/test_digitalocean.py social_core/tests/backends/test_disqus.py social_core/tests/backends/test_dribbble.py social_core/tests/backends/test_drip.py social_core/tests/backends/test_dropbox.py social_core/tests/backends/test_dummy.py social_core/tests/backends/test_edmodo.py social_core/tests/backends/test_elixir.py social_core/tests/backends/test_email.py social_core/tests/backends/test_eventbrite.py social_core/tests/backends/test_evernote.py social_core/tests/backends/test_facebook.py social_core/tests/backends/test_fitbit.py social_core/tests/backends/test_five_hundred_px.py social_core/tests/backends/test_flat.py social_core/tests/backends/test_flickr.py social_core/tests/backends/test_foursquare.py social_core/tests/backends/test_github.py social_core/tests/backends/test_github_enterprise.py social_core/tests/backends/test_gitlab.py social_core/tests/backends/test_globus.py social_core/tests/backends/test_google.py social_core/tests/backends/test_instagram.py social_core/tests/backends/test_itembase.py social_core/tests/backends/test_kakao.py social_core/tests/backends/test_keycloak.py social_core/tests/backends/test_khanacademy.py social_core/tests/backends/test_linkedin.py social_core/tests/backends/test_live.py social_core/tests/backends/test_livejournal.py social_core/tests/backends/test_lyft.py social_core/tests/backends/test_mapmyfitness.py social_core/tests/backends/test_mineid.py social_core/tests/backends/test_mixcloud.py social_core/tests/backends/test_nationbuilder.py social_core/tests/backends/test_naver.py social_core/tests/backends/test_ngpvan.py social_core/tests/backends/test_orbi.py social_core/tests/backends/test_patreon.py social_core/tests/backends/test_phabricator.py social_core/tests/backends/test_pinterest.py social_core/tests/backends/test_podio.py social_core/tests/backends/test_qiita.py social_core/tests/backends/test_quizlet.py social_core/tests/backends/test_readability.py social_core/tests/backends/test_reddit.py social_core/tests/backends/test_saml.py social_core/tests/backends/test_scistarter.py social_core/tests/backends/test_sketchfab.py social_core/tests/backends/test_skyrock.py social_core/tests/backends/test_slack.py social_core/tests/backends/test_soundcloud.py social_core/tests/backends/test_spotify.py social_core/tests/backends/test_stackoverflow.py social_core/tests/backends/test_steam.py social_core/tests/backends/test_stocktwits.py social_core/tests/backends/test_strava.py social_core/tests/backends/test_stripe.py social_core/tests/backends/test_taobao.py social_core/tests/backends/test_thisismyjam.py social_core/tests/backends/test_tripit.py social_core/tests/backends/test_tumblr.py social_core/tests/backends/test_twitch.py social_core/tests/backends/test_twitter.py social_core/tests/backends/test_uber.py social_core/tests/backends/test_udata.py social_core/tests/backends/test_universe.py social_core/tests/backends/test_upwork.py social_core/tests/backends/test_username.py social_core/tests/backends/test_utils.py social_core/tests/backends/test_vk.py social_core/tests/backends/test_wunderlist.py social_core/tests/backends/test_xing.py social_core/tests/backends/test_yahoo.py social_core/tests/backends/test_yammer.py social_core/tests/backends/test_yandex.py social_core/tests/backends/test_zotero.py social_core/tests/backends/data/saml_response.txtsocial-auth-core-3.1.0/setup.cfg0000644000175000017500000000041713433256445016364 0ustar omabomab00000000000000[flake8] max-line-length = 119 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_core rednose = 1 where = social_core/tests [egg_info] tag_build = tag_date = 0 social-auth-core-3.1.0/README.md0000644000175000017500000000317513336542652016026 0ustar omabomab00000000000000# Python Social Auth - Core [![Build Status](https://travis-ci.org/python-social-auth/social-core.svg?branch=master)](https://travis-ci.org/python-social-auth/social-core) [![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) Python Social Auth is an easy to setup social authentication/registration mechanism with support for several frameworks and auth providers. ## Description This is the core component of the python-social-auth ecosystem, it implements the common interface to define new authentication backends to third parties services, implement integrations with web frameworks and storage solutions. ## Documentation Project documentation is available at http://python-social-auth.readthedocs.org/. ## Setup ```shell $ pip install social-auth-core ``` ## 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 maintened 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)