vendor-libs: synced authomatic code with latest changes

This commit is contained in:
RhodeCode Admin 2023-07-17 15:03:15 +02:00
parent dc2b299dcc
commit 87dc66d2db
16 changed files with 188 additions and 50 deletions

View file

@ -1,4 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2012-2020 RhodeCode GmbH
#

View file

@ -1,4 +1,4 @@
# -*- coding: utf-8 -*-
"""
Helper functions for use with :class:`Authomatic`.

View file

@ -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):

View file

@ -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

View file

@ -1,4 +1,4 @@
# -*- coding: utf-8 -*-
"""
Provides various exception types for the library.
"""

View file

@ -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

View file

@ -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']

View file

@ -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):
"""

View file

@ -1,4 +1,4 @@
# -*- coding: utf-8 -*-
"""
Interfaces
^^^^^^^^^^

View file

@ -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:

View file

@ -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']

View file

@ -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!')

View file

@ -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,

View file

@ -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

View file

@ -1,4 +1,4 @@
# -*- coding: utf-8 -*-
from authomatic import providers

View file

@ -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 = " "