vendor-libs: synced authomatic code with latest changes
This commit is contained in:
parent
dc2b299dcc
commit
87dc66d2db
16 changed files with 188 additions and 50 deletions
|
|
@ -1,4 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
# Copyright (C) 2012-2020 RhodeCode GmbH
|
||||
#
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Helper functions for use with :class:`Authomatic`.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Adapters
|
||||
--------
|
||||
|
|
@ -271,6 +271,7 @@ class WerkzeugAdapter(BaseAdapter):
|
|||
self.response = response
|
||||
|
||||
def write(self, value):
|
||||
#self.response.data = self.response.data.decode('utf-8') + value
|
||||
self.response.data = self.response.data + value
|
||||
|
||||
def set_header(self, key, value):
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
import collections
|
||||
import copy
|
||||
|
|
@ -7,7 +7,10 @@ import hashlib
|
|||
import hmac
|
||||
import json
|
||||
import logging
|
||||
import pickle
|
||||
try:
|
||||
import cPickle as pickle
|
||||
except ImportError:
|
||||
import pickle
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
|
|
@ -914,7 +917,7 @@ class Credentials(ReprMixin):
|
|||
|
||||
deserialized = Credentials(config)
|
||||
|
||||
deserialized.provider_id = provider_id
|
||||
deserialized.provider_id = int(split[0])
|
||||
deserialized.provider_type = ProviderClass.get_type()
|
||||
deserialized.provider_type_id = split[1]
|
||||
deserialized.provider_class = ProviderClass
|
||||
|
|
@ -1141,8 +1144,8 @@ class Response(ReprMixin):
|
|||
Return true if string is binary data.
|
||||
"""
|
||||
|
||||
textchars = (bytearray([7, 8, 9, 10, 12, 13, 27]) +
|
||||
bytearray(range(0x20, 0x100)))
|
||||
textchars = (bytearray([7, 8, 9, 10, 12, 13, 27])
|
||||
+ bytearray(range(0x20, 0x100)))
|
||||
return bool(content.translate(None, textchars))
|
||||
|
||||
@property
|
||||
|
|
@ -1156,6 +1159,7 @@ class Response(ReprMixin):
|
|||
if self.is_binary_string(content):
|
||||
self._content = content
|
||||
else:
|
||||
#self._content = content.decode('utf-8')
|
||||
self._content = content
|
||||
return self._content
|
||||
|
||||
|
|
@ -1714,7 +1718,7 @@ class Authomatic(object):
|
|||
jsonp = params.get('callback')
|
||||
|
||||
# JSONP is possible only with GET method.
|
||||
if ProviderClass.supports_jsonp and method is 'GET':
|
||||
if ProviderClass.supports_jsonp and method == 'GET':
|
||||
request_type = 'elements'
|
||||
else:
|
||||
# Remove the JSONP callback
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Provides various exception types for the library.
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
|flask| Extras
|
||||
--------------
|
||||
|
|
@ -8,7 +8,7 @@ Utilities you can use when using this library with the |flask|_ framework.
|
|||
Thanks to `Mark Steve Samson <http://marksteve.com>`_.
|
||||
"""
|
||||
|
||||
|
||||
from __future__ import absolute_import
|
||||
from functools import wraps
|
||||
|
||||
from authomatic.adapters import WerkzeugAdapter
|
||||
|
|
@ -44,4 +44,5 @@ class FlaskAuthomatic(Authomatic):
|
|||
return decorator
|
||||
|
||||
def session_saver(self):
|
||||
session.modified = True
|
||||
# FIXME: pylint false positive - see https://github.com/pallets/flask/issues/4020
|
||||
session.modified = True # pylint: disable=assigning-non-slot
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
|gae| Extras
|
||||
------------
|
||||
|
|
@ -6,13 +6,20 @@
|
|||
Utilities you can use when using this library on |gae|_.
|
||||
"""
|
||||
|
||||
from google.appengine.ext import ndb
|
||||
from webapp2_extras import sessions
|
||||
|
||||
from authomatic import exceptions
|
||||
from authomatic.extras import interfaces
|
||||
from authomatic.extras.gae.openid import NDBOpenIDStore
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
from google.appengine.ext import ndb
|
||||
from webapp2_extras import sessions
|
||||
except ImportError:
|
||||
logger.exception("FATAL: google.appengine 1st Gen. not installed!")
|
||||
raise
|
||||
|
||||
__all__ = ['ndb_config', 'Webapp2Session']
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
# We need absolute import to import from openid library which has the same
|
||||
# name as this module
|
||||
|
||||
from __future__ import absolute_import
|
||||
import logging
|
||||
import datetime
|
||||
|
||||
from google.appengine.ext import ndb
|
||||
import openid.store.interface
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
from google.appengine.ext import ndb
|
||||
except ImportError:
|
||||
logger.exception("FATAL: google.appengine 1st Gen. not installed!")
|
||||
raise
|
||||
|
||||
|
||||
class NDBOpenIDStore(ndb.Expando, openid.store.interface.OpenIDStore):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Interfaces
|
||||
^^^^^^^^^^
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Abstract Classes for Providers
|
||||
------------------------------
|
||||
|
|
@ -503,8 +503,10 @@ class BaseProvider(object):
|
|||
self.user.last_name))
|
||||
else:
|
||||
# Or use one of these.
|
||||
self.user.name = (self.user.username or self.user.nickname or
|
||||
self.user.first_name or self.user.last_name)
|
||||
self.user.name = (self.user.username
|
||||
or self.user.nickname
|
||||
or self.user.first_name
|
||||
or self.user.last_name)
|
||||
|
||||
if not self.user.location:
|
||||
if self.user.city and self.user.country:
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Google App Engine OpenID Providers
|
||||
----------------------------------
|
||||
|
|
@ -25,14 +25,20 @@ Google App Engine OpenID Providers
|
|||
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
from google.appengine.api import users
|
||||
|
||||
import authomatic.core as core
|
||||
from authomatic import providers
|
||||
from authomatic.exceptions import FailureError
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
from google.appengine.api import users
|
||||
except ImportError:
|
||||
logger.exception("FATAL: google.appengine 1st Gen. not installed!")
|
||||
users = None
|
||||
|
||||
|
||||
__all__ = ['GAEOpenID', 'Yahoo', 'Google']
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
|oauth1| Providers
|
||||
--------------------
|
||||
|
|
@ -358,7 +358,7 @@ class OAuth1(providers.AuthorizationProvider):
|
|||
params['oauth_consumer_key'] = consumer_key
|
||||
else:
|
||||
raise OAuth1Error(
|
||||
'Credentials with valid consumer_key, ' +
|
||||
'Credentials with valid consumer_key, '
|
||||
'consumer_secret, token and token_secret are required '
|
||||
'to create Protected Resources URL!')
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
|oauth2| Providers
|
||||
-------------------
|
||||
|
|
@ -20,6 +20,7 @@ Providers which implement the |oauth2|_ protocol.
|
|||
GitHub
|
||||
Google
|
||||
LinkedIn
|
||||
MicrosoftOnline
|
||||
PayPal
|
||||
Reddit
|
||||
Viadeo
|
||||
|
|
@ -55,6 +56,7 @@ __all__ = [
|
|||
'GitHub',
|
||||
'Google',
|
||||
'LinkedIn',
|
||||
'MicrosoftOnline',
|
||||
'PayPal',
|
||||
'Reddit',
|
||||
'Viadeo',
|
||||
|
|
@ -298,7 +300,7 @@ class OAuth2(providers.AuthorizationProvider):
|
|||
# unquote Cast to str to void b64decode translation error. Base64
|
||||
# should be str compatible.
|
||||
return json.loads(base64.urlsafe_b64decode(
|
||||
unquote(str(state))))[param]
|
||||
unquote(str(state))).decode('utf-8'))[param]
|
||||
else:
|
||||
return state if param == 'csrf' else ''
|
||||
|
||||
|
|
@ -490,9 +492,9 @@ class OAuth2(providers.AuthorizationProvider):
|
|||
url=self.user_authorization_url)
|
||||
|
||||
elif (
|
||||
not self.params or
|
||||
len(self.params) == 1 and
|
||||
'user_state' in self.params
|
||||
not self.params
|
||||
or len(self.params) == 1
|
||||
and 'user_state' in self.params
|
||||
):
|
||||
# =================================================================
|
||||
# Phase 1 before redirect
|
||||
|
|
@ -1214,6 +1216,7 @@ class GitHub(OAuth2):
|
|||
'consumer_key': '#####',
|
||||
'consumer_secret': '#####',
|
||||
'access_headers': {'User-Agent': 'Awesome-Octocat-App'},
|
||||
'scope': ['user:email']
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1272,6 +1275,38 @@ class GitHub(OAuth2):
|
|||
credentials.token_type = cls.BEARER
|
||||
return credentials
|
||||
|
||||
def access(self, url, **kwargs):
|
||||
# https://developer.github.com/v3/#user-agent-required
|
||||
# GitHub requires that all API requests MUST include a valid ``User-Agent`` header.
|
||||
headers = kwargs["headers"] = kwargs.get("headers", {})
|
||||
if not headers.get("User-Agent"):
|
||||
headers["User-Agent"] = self.settings.config[self.name]["consumer_key"]
|
||||
|
||||
def parent_access(url):
|
||||
return super(GitHub, self).access(url, **kwargs)
|
||||
|
||||
response = parent_access(url)
|
||||
|
||||
# additional action to get email is required:
|
||||
# https://developer.github.com/v3/users/emails/
|
||||
if response.status == 200:
|
||||
email_response = parent_access(url + "/emails")
|
||||
if email_response.status == 200:
|
||||
response.data["emails"] = email_response.data
|
||||
|
||||
# find first or primary email
|
||||
primary_email = None
|
||||
for item in email_response.data:
|
||||
is_primary = item["primary"]
|
||||
if not primary_email or is_primary:
|
||||
primary_email = item["email"]
|
||||
|
||||
if is_primary:
|
||||
break
|
||||
|
||||
response.data["email"] = primary_email
|
||||
return response
|
||||
|
||||
|
||||
class Google(OAuth2):
|
||||
"""
|
||||
|
|
@ -1487,13 +1522,89 @@ class LinkedIn(OAuth2):
|
|||
return user
|
||||
|
||||
|
||||
class MicrosoftOnline(OAuth2):
|
||||
"""
|
||||
Microsoft Online |oauth2| provider.
|
||||
|
||||
Supported :class:`.User` properties:
|
||||
|
||||
* email
|
||||
* first_name
|
||||
* id
|
||||
* last_name
|
||||
* location
|
||||
* name
|
||||
* phone
|
||||
* picture
|
||||
* username
|
||||
|
||||
Unsupported :class:`.User` properties:
|
||||
|
||||
* birth_date
|
||||
* city
|
||||
* country
|
||||
* gender
|
||||
* link
|
||||
* locale
|
||||
* nickname
|
||||
* postal_code
|
||||
* timezone
|
||||
|
||||
"""
|
||||
|
||||
user_authorization_url = 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize'
|
||||
access_token_url = 'https://login.microsoftonline.com/common/oauth2/v2.0/token'
|
||||
user_info_url = "https://graph.microsoft.com/v1.0/me"
|
||||
|
||||
user_info_scope = ['openid profile']
|
||||
|
||||
supported_user_attributes = core.SupportedUserAttributes(
|
||||
id=True,
|
||||
email=True,
|
||||
first_name=True,
|
||||
last_name=True,
|
||||
location=True,
|
||||
name=True,
|
||||
phone=True,
|
||||
picture=True,
|
||||
username=True,
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(MicrosoftOnline, self).__init__(*args, **kwargs)
|
||||
auth = args[0]
|
||||
provider_name = kwargs.get('provider_name')
|
||||
domain = auth.config.get(provider_name, {}).get('domain')
|
||||
if domain is not None:
|
||||
self.user_authorization_url = MicrosoftOnline.user_authorization_url.replace('/common/', '/%s/' % domain)
|
||||
self.access_token_url = MicrosoftOnline.access_token_url.replace('/common/', '/%s/' % domain)
|
||||
|
||||
@classmethod
|
||||
def _x_credentials_parser(cls, credentials, data):
|
||||
if data.get('token_type') == 'bearer':
|
||||
credentials.token_type = cls.BEARER
|
||||
return credentials
|
||||
|
||||
@staticmethod
|
||||
def _x_user_parser(user, data):
|
||||
user.id = data.get('id')
|
||||
user.name = data.get('displayName', '')
|
||||
user.first_name = data.get('givenName', '')
|
||||
user.last_name = data.get('surname', '')
|
||||
user.email = data.get('mail', '')
|
||||
user.location = data.get('officeLocation', '')
|
||||
user.phone = data.get('mobilePhone', '')
|
||||
user.picture = data.get('picture', '')
|
||||
user.username = data.get('userPrincipalName', '')
|
||||
return user
|
||||
|
||||
|
||||
class PayPal(OAuth2):
|
||||
"""
|
||||
PayPal |oauth2| provider.
|
||||
|
||||
* Dashboard: https://developer.paypal.com/webapps/developer/applications
|
||||
* Docs:
|
||||
https://developer.paypal.com/webapps/developer/docs/integration/direct/make-your-first-call/
|
||||
* Docs: https://developer.paypal.com/webapps/developer/docs/integration/direct/make-your-first-call/
|
||||
* API reference: https://developer.paypal.com/webapps/developer/docs/api/
|
||||
|
||||
.. note::
|
||||
|
|
@ -1540,8 +1651,8 @@ class Reddit(OAuth2):
|
|||
|
||||
.. note::
|
||||
|
||||
According to Reddit API
|
||||
`docs <https://github.com/reddit/reddit/wiki/API#rules>`_,
|
||||
According to
|
||||
`Reddit API docs <https://github.com/reddit/reddit/wiki/API#rules>`_,
|
||||
you have to include a `User-Agent` header in each API call.
|
||||
|
||||
You can apply a default ``User-Agent`` header for all API calls in the
|
||||
|
|
@ -1961,8 +2072,7 @@ class Yandex(OAuth2):
|
|||
Yandex |oauth2| provider.
|
||||
|
||||
* Dashboard: https://oauth.yandex.com/client/my
|
||||
* Docs:
|
||||
http://api.yandex.com/oauth/doc/dg/reference/obtain-access-token.xml
|
||||
* Docs: http://api.yandex.com/oauth/doc/dg/reference/obtain-access-token.xml
|
||||
* API reference:
|
||||
|
||||
Supported :class:`.User` properties:
|
||||
|
|
@ -2042,6 +2152,7 @@ PROVIDER_ID_MAP = [
|
|||
GitHub,
|
||||
Google,
|
||||
LinkedIn,
|
||||
MicrosoftOnline,
|
||||
OAuth2,
|
||||
PayPal,
|
||||
Reddit,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
|openid| Providers
|
||||
----------------------------------
|
||||
|
|
@ -20,7 +20,7 @@ Providers which implement the |openid|_ protocol based on the
|
|||
|
||||
# We need absolute import to import from openid library which has the same
|
||||
# name as this module
|
||||
|
||||
from __future__ import absolute_import
|
||||
import datetime
|
||||
import logging
|
||||
import time
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from authomatic import providers
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""Utilities for writing code that runs on Python 2 and 3"""
|
||||
|
||||
# Copyright (c) 2010-2015 Benjamin Peterson
|
||||
|
|
@ -21,7 +21,7 @@
|
|||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import functools
|
||||
import itertools
|
||||
|
|
@ -89,7 +89,7 @@ class _LazyDescr(object):
|
|||
|
||||
def __get__(self, obj, tp):
|
||||
result = self._resolve()
|
||||
setattr(obj, self.name, result) # Invokes __set__.
|
||||
setattr(obj, self.name, result) # Invokes __set__.
|
||||
try:
|
||||
# This is a bit ugly, but it avoids running this again by
|
||||
# removing this descriptor.
|
||||
|
|
@ -733,8 +733,8 @@ if print_ is None:
|
|||
want_unicode = True
|
||||
break
|
||||
if want_unicode:
|
||||
newline = unicode("\n")
|
||||
space = unicode(" ")
|
||||
newline = str("\n")
|
||||
space = str(" ")
|
||||
else:
|
||||
newline = "\n"
|
||||
space = " "
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue