diff --git a/rhodecode/lib/_vendor/__init__.py b/rhodecode/lib/_vendor/__init__.py index 87c01b6e..563f2287 100644 --- a/rhodecode/lib/_vendor/__init__.py +++ b/rhodecode/lib/_vendor/__init__.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- + # Copyright (C) 2012-2020 RhodeCode GmbH # diff --git a/rhodecode/lib/_vendor/authomatic/__init__.py b/rhodecode/lib/_vendor/authomatic/__init__.py index 7139e800..9ebd06cd 100755 --- a/rhodecode/lib/_vendor/authomatic/__init__.py +++ b/rhodecode/lib/_vendor/authomatic/__init__.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- + """ Helper functions for use with :class:`Authomatic`. diff --git a/rhodecode/lib/_vendor/authomatic/adapters.py b/rhodecode/lib/_vendor/authomatic/adapters.py index d3740bb5..09253a2e 100755 --- a/rhodecode/lib/_vendor/authomatic/adapters.py +++ b/rhodecode/lib/_vendor/authomatic/adapters.py @@ -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): diff --git a/rhodecode/lib/_vendor/authomatic/core.py b/rhodecode/lib/_vendor/authomatic/core.py index 7bbd50db..9a31e0e0 100755 --- a/rhodecode/lib/_vendor/authomatic/core.py +++ b/rhodecode/lib/_vendor/authomatic/core.py @@ -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 diff --git a/rhodecode/lib/_vendor/authomatic/exceptions.py b/rhodecode/lib/_vendor/authomatic/exceptions.py index cfb3e372..0511418b 100755 --- a/rhodecode/lib/_vendor/authomatic/exceptions.py +++ b/rhodecode/lib/_vendor/authomatic/exceptions.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- + """ Provides various exception types for the library. """ diff --git a/rhodecode/lib/_vendor/authomatic/extras/flask.py b/rhodecode/lib/_vendor/authomatic/extras/flask.py index 6b7634aa..1d6bd89f 100755 --- a/rhodecode/lib/_vendor/authomatic/extras/flask.py +++ b/rhodecode/lib/_vendor/authomatic/extras/flask.py @@ -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 `_. """ - +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 diff --git a/rhodecode/lib/_vendor/authomatic/extras/gae/__init__.py b/rhodecode/lib/_vendor/authomatic/extras/gae/__init__.py index 11190665..5e231db5 100755 --- a/rhodecode/lib/_vendor/authomatic/extras/gae/__init__.py +++ b/rhodecode/lib/_vendor/authomatic/extras/gae/__init__.py @@ -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'] diff --git a/rhodecode/lib/_vendor/authomatic/extras/gae/openid.py b/rhodecode/lib/_vendor/authomatic/extras/gae/openid.py index d8911def..06c45eed 100755 --- a/rhodecode/lib/_vendor/authomatic/extras/gae/openid.py +++ b/rhodecode/lib/_vendor/authomatic/extras/gae/openid.py @@ -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): """ diff --git a/rhodecode/lib/_vendor/authomatic/extras/interfaces.py b/rhodecode/lib/_vendor/authomatic/extras/interfaces.py index f807bcd9..dcd63279 100755 --- a/rhodecode/lib/_vendor/authomatic/extras/interfaces.py +++ b/rhodecode/lib/_vendor/authomatic/extras/interfaces.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- + """ Interfaces ^^^^^^^^^^ diff --git a/rhodecode/lib/_vendor/authomatic/providers/__init__.py b/rhodecode/lib/_vendor/authomatic/providers/__init__.py index 1c8bed98..c3892c10 100755 --- a/rhodecode/lib/_vendor/authomatic/providers/__init__.py +++ b/rhodecode/lib/_vendor/authomatic/providers/__init__.py @@ -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: diff --git a/rhodecode/lib/_vendor/authomatic/providers/gaeopenid.py b/rhodecode/lib/_vendor/authomatic/providers/gaeopenid.py index 524cd0db..600b2bef 100755 --- a/rhodecode/lib/_vendor/authomatic/providers/gaeopenid.py +++ b/rhodecode/lib/_vendor/authomatic/providers/gaeopenid.py @@ -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'] diff --git a/rhodecode/lib/_vendor/authomatic/providers/oauth1.py b/rhodecode/lib/_vendor/authomatic/providers/oauth1.py index 615df031..e8948056 100755 --- a/rhodecode/lib/_vendor/authomatic/providers/oauth1.py +++ b/rhodecode/lib/_vendor/authomatic/providers/oauth1.py @@ -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!') diff --git a/rhodecode/lib/_vendor/authomatic/providers/oauth2.py b/rhodecode/lib/_vendor/authomatic/providers/oauth2.py index e40e3156..bfa65958 100755 --- a/rhodecode/lib/_vendor/authomatic/providers/oauth2.py +++ b/rhodecode/lib/_vendor/authomatic/providers/oauth2.py @@ -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 `_, + According to + `Reddit API docs `_, 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, diff --git a/rhodecode/lib/_vendor/authomatic/providers/openid.py b/rhodecode/lib/_vendor/authomatic/providers/openid.py index 77edb839..6d39301e 100755 --- a/rhodecode/lib/_vendor/authomatic/providers/openid.py +++ b/rhodecode/lib/_vendor/authomatic/providers/openid.py @@ -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 diff --git a/rhodecode/lib/_vendor/authomatic/providers/persona.py b/rhodecode/lib/_vendor/authomatic/providers/persona.py index 25125432..6ce94280 100755 --- a/rhodecode/lib/_vendor/authomatic/providers/persona.py +++ b/rhodecode/lib/_vendor/authomatic/providers/persona.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- + from authomatic import providers diff --git a/rhodecode/lib/_vendor/authomatic/six.py b/rhodecode/lib/_vendor/authomatic/six.py index 13ed8526..c024aac1 100755 --- a/rhodecode/lib/_vendor/authomatic/six.py +++ b/rhodecode/lib/_vendor/authomatic/six.py @@ -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 = " "