##// END OF EJS Templates
vendor-libs: synced authomatic code with latest changes
super-admin -
r5040:444b916d default
parent child Browse files
Show More
@@ -1,4 +1,4 b''
1 # -*- coding: utf-8 -*-
1
2
2
3 # Copyright (C) 2012-2020 RhodeCode GmbH
3 # Copyright (C) 2012-2020 RhodeCode GmbH
4 #
4 #
@@ -1,4 +1,4 b''
1 # -*- coding: utf-8 -*-
1
2 """
2 """
3 Helper functions for use with :class:`Authomatic`.
3 Helper functions for use with :class:`Authomatic`.
4
4
@@ -1,4 +1,4 b''
1 # -*- coding: utf-8 -*-
1
2 """
2 """
3 Adapters
3 Adapters
4 --------
4 --------
@@ -271,6 +271,7 b' class WerkzeugAdapter(BaseAdapter):'
271 self.response = response
271 self.response = response
272
272
273 def write(self, value):
273 def write(self, value):
274 #self.response.data = self.response.data.decode('utf-8') + value
274 self.response.data = self.response.data + value
275 self.response.data = self.response.data + value
275
276
276 def set_header(self, key, value):
277 def set_header(self, key, value):
@@ -1,4 +1,4 b''
1 # -*- coding: utf-8 -*-
1
2
2
3 import collections
3 import collections
4 import copy
4 import copy
@@ -7,7 +7,10 b' import hashlib'
7 import hmac
7 import hmac
8 import json
8 import json
9 import logging
9 import logging
10 import pickle
10 try:
11 import cPickle as pickle
12 except ImportError:
13 import pickle
11 import sys
14 import sys
12 import threading
15 import threading
13 import time
16 import time
@@ -914,7 +917,7 b' class Credentials(ReprMixin):'
914
917
915 deserialized = Credentials(config)
918 deserialized = Credentials(config)
916
919
917 deserialized.provider_id = provider_id
920 deserialized.provider_id = int(split[0])
918 deserialized.provider_type = ProviderClass.get_type()
921 deserialized.provider_type = ProviderClass.get_type()
919 deserialized.provider_type_id = split[1]
922 deserialized.provider_type_id = split[1]
920 deserialized.provider_class = ProviderClass
923 deserialized.provider_class = ProviderClass
@@ -1141,8 +1144,8 b' class Response(ReprMixin):'
1141 Return true if string is binary data.
1144 Return true if string is binary data.
1142 """
1145 """
1143
1146
1144 textchars = (bytearray([7, 8, 9, 10, 12, 13, 27]) +
1147 textchars = (bytearray([7, 8, 9, 10, 12, 13, 27])
1145 bytearray(range(0x20, 0x100)))
1148 + bytearray(range(0x20, 0x100)))
1146 return bool(content.translate(None, textchars))
1149 return bool(content.translate(None, textchars))
1147
1150
1148 @property
1151 @property
@@ -1156,6 +1159,7 b' class Response(ReprMixin):'
1156 if self.is_binary_string(content):
1159 if self.is_binary_string(content):
1157 self._content = content
1160 self._content = content
1158 else:
1161 else:
1162 #self._content = content.decode('utf-8')
1159 self._content = content
1163 self._content = content
1160 return self._content
1164 return self._content
1161
1165
@@ -1714,7 +1718,7 b' class Authomatic(object):'
1714 jsonp = params.get('callback')
1718 jsonp = params.get('callback')
1715
1719
1716 # JSONP is possible only with GET method.
1720 # JSONP is possible only with GET method.
1717 if ProviderClass.supports_jsonp and method is 'GET':
1721 if ProviderClass.supports_jsonp and method == 'GET':
1718 request_type = 'elements'
1722 request_type = 'elements'
1719 else:
1723 else:
1720 # Remove the JSONP callback
1724 # Remove the JSONP callback
@@ -1,4 +1,4 b''
1 # -*- coding: utf-8 -*-
1
2 """
2 """
3 Provides various exception types for the library.
3 Provides various exception types for the library.
4 """
4 """
@@ -1,4 +1,4 b''
1 # -*- coding: utf-8 -*-
1
2 """
2 """
3 |flask| Extras
3 |flask| Extras
4 --------------
4 --------------
@@ -8,7 +8,7 b' Utilities you can use when using this li'
8 Thanks to `Mark Steve Samson <http://marksteve.com>`_.
8 Thanks to `Mark Steve Samson <http://marksteve.com>`_.
9 """
9 """
10
10
11
11 from __future__ import absolute_import
12 from functools import wraps
12 from functools import wraps
13
13
14 from authomatic.adapters import WerkzeugAdapter
14 from authomatic.adapters import WerkzeugAdapter
@@ -44,4 +44,5 b' class FlaskAuthomatic(Authomatic):'
44 return decorator
44 return decorator
45
45
46 def session_saver(self):
46 def session_saver(self):
47 session.modified = True
47 # FIXME: pylint false positive - see https://github.com/pallets/flask/issues/4020
48 session.modified = True # pylint: disable=assigning-non-slot
@@ -1,4 +1,4 b''
1 # -*- coding: utf-8 -*-
1
2 """
2 """
3 |gae| Extras
3 |gae| Extras
4 ------------
4 ------------
@@ -6,13 +6,20 b''
6 Utilities you can use when using this library on |gae|_.
6 Utilities you can use when using this library on |gae|_.
7 """
7 """
8
8
9 from google.appengine.ext import ndb
10 from webapp2_extras import sessions
11
9
12 from authomatic import exceptions
10 from authomatic import exceptions
13 from authomatic.extras import interfaces
11 from authomatic.extras import interfaces
14 from authomatic.extras.gae.openid import NDBOpenIDStore
12 from authomatic.extras.gae.openid import NDBOpenIDStore
13 import logging
15
14
15 logger = logging.getLogger(__name__)
16
17 try:
18 from google.appengine.ext import ndb
19 from webapp2_extras import sessions
20 except ImportError:
21 logger.exception("FATAL: google.appengine 1st Gen. not installed!")
22 raise
16
23
17 __all__ = ['ndb_config', 'Webapp2Session']
24 __all__ = ['ndb_config', 'Webapp2Session']
18
25
@@ -1,13 +1,19 b''
1 # -*- coding: utf-8 -*-
1
2
2
3 # We need absolute import to import from openid library which has the same
3 # We need absolute import to import from openid library which has the same
4 # name as this module
4 # name as this module
5
5 from __future__ import absolute_import
6 import logging
6 import logging
7 import datetime
7 import datetime
8 import openid.store.interface
8
9
9 from google.appengine.ext import ndb
10 logger = logging.getLogger(__name__)
10 import openid.store.interface
11
12 try:
13 from google.appengine.ext import ndb
14 except ImportError:
15 logger.exception("FATAL: google.appengine 1st Gen. not installed!")
16 raise
11
17
12
18
13 class NDBOpenIDStore(ndb.Expando, openid.store.interface.OpenIDStore):
19 class NDBOpenIDStore(ndb.Expando, openid.store.interface.OpenIDStore):
@@ -1,4 +1,4 b''
1 # -*- coding: utf-8 -*-
1
2 """
2 """
3 Interfaces
3 Interfaces
4 ^^^^^^^^^^
4 ^^^^^^^^^^
@@ -1,4 +1,4 b''
1 # -*- coding: utf-8 -*-
1
2 """
2 """
3 Abstract Classes for Providers
3 Abstract Classes for Providers
4 ------------------------------
4 ------------------------------
@@ -503,8 +503,10 b' class BaseProvider(object):'
503 self.user.last_name))
503 self.user.last_name))
504 else:
504 else:
505 # Or use one of these.
505 # Or use one of these.
506 self.user.name = (self.user.username or self.user.nickname or
506 self.user.name = (self.user.username
507 self.user.first_name or self.user.last_name)
507 or self.user.nickname
508 or self.user.first_name
509 or self.user.last_name)
508
510
509 if not self.user.location:
511 if not self.user.location:
510 if self.user.city and self.user.country:
512 if self.user.city and self.user.country:
@@ -1,4 +1,4 b''
1 # -*- coding: utf-8 -*-
1
2 """
2 """
3 Google App Engine OpenID Providers
3 Google App Engine OpenID Providers
4 ----------------------------------
4 ----------------------------------
@@ -25,14 +25,20 b' Google App Engine OpenID Providers'
25
25
26 """
26 """
27
27
28 import logging
29
30 from google.appengine.api import users
31
32 import authomatic.core as core
28 import authomatic.core as core
33 from authomatic import providers
29 from authomatic import providers
34 from authomatic.exceptions import FailureError
30 from authomatic.exceptions import FailureError
35
31
32 import logging
33
34 logger = logging.getLogger(__name__)
35
36 try:
37 from google.appengine.api import users
38 except ImportError:
39 logger.exception("FATAL: google.appengine 1st Gen. not installed!")
40 users = None
41
36
42
37 __all__ = ['GAEOpenID', 'Yahoo', 'Google']
43 __all__ = ['GAEOpenID', 'Yahoo', 'Google']
38
44
@@ -1,4 +1,4 b''
1 # -*- coding: utf-8 -*-
1
2 """
2 """
3 |oauth1| Providers
3 |oauth1| Providers
4 --------------------
4 --------------------
@@ -358,7 +358,7 b' class OAuth1(providers.AuthorizationProv'
358 params['oauth_consumer_key'] = consumer_key
358 params['oauth_consumer_key'] = consumer_key
359 else:
359 else:
360 raise OAuth1Error(
360 raise OAuth1Error(
361 'Credentials with valid consumer_key, ' +
361 'Credentials with valid consumer_key, '
362 'consumer_secret, token and token_secret are required '
362 'consumer_secret, token and token_secret are required '
363 'to create Protected Resources URL!')
363 'to create Protected Resources URL!')
364
364
@@ -1,4 +1,4 b''
1 # -*- coding: utf-8 -*-
1
2 """
2 """
3 |oauth2| Providers
3 |oauth2| Providers
4 -------------------
4 -------------------
@@ -20,6 +20,7 b' Providers which implement the |oauth2|_ '
20 GitHub
20 GitHub
21 Google
21 Google
22 LinkedIn
22 LinkedIn
23 MicrosoftOnline
23 PayPal
24 PayPal
24 Reddit
25 Reddit
25 Viadeo
26 Viadeo
@@ -55,6 +56,7 b' import authomatic.core as core'
55 'GitHub',
56 'GitHub',
56 'Google',
57 'Google',
57 'LinkedIn',
58 'LinkedIn',
59 'MicrosoftOnline',
58 'PayPal',
60 'PayPal',
59 'Reddit',
61 'Reddit',
60 'Viadeo',
62 'Viadeo',
@@ -298,7 +300,7 b' class OAuth2(providers.AuthorizationProv'
298 # unquote Cast to str to void b64decode translation error. Base64
300 # unquote Cast to str to void b64decode translation error. Base64
299 # should be str compatible.
301 # should be str compatible.
300 return json.loads(base64.urlsafe_b64decode(
302 return json.loads(base64.urlsafe_b64decode(
301 unquote(str(state))))[param]
303 unquote(str(state))).decode('utf-8'))[param]
302 else:
304 else:
303 return state if param == 'csrf' else ''
305 return state if param == 'csrf' else ''
304
306
@@ -490,9 +492,9 b' class OAuth2(providers.AuthorizationProv'
490 url=self.user_authorization_url)
492 url=self.user_authorization_url)
491
493
492 elif (
494 elif (
493 not self.params or
495 not self.params
494 len(self.params) == 1 and
496 or len(self.params) == 1
495 'user_state' in self.params
497 and 'user_state' in self.params
496 ):
498 ):
497 # =================================================================
499 # =================================================================
498 # Phase 1 before redirect
500 # Phase 1 before redirect
@@ -1214,6 +1216,7 b' class GitHub(OAuth2):'
1214 'consumer_key': '#####',
1216 'consumer_key': '#####',
1215 'consumer_secret': '#####',
1217 'consumer_secret': '#####',
1216 'access_headers': {'User-Agent': 'Awesome-Octocat-App'},
1218 'access_headers': {'User-Agent': 'Awesome-Octocat-App'},
1219 'scope': ['user:email']
1217 }
1220 }
1218 }
1221 }
1219
1222
@@ -1272,6 +1275,38 b' class GitHub(OAuth2):'
1272 credentials.token_type = cls.BEARER
1275 credentials.token_type = cls.BEARER
1273 return credentials
1276 return credentials
1274
1277
1278 def access(self, url, **kwargs):
1279 # https://developer.github.com/v3/#user-agent-required
1280 # GitHub requires that all API requests MUST include a valid ``User-Agent`` header.
1281 headers = kwargs["headers"] = kwargs.get("headers", {})
1282 if not headers.get("User-Agent"):
1283 headers["User-Agent"] = self.settings.config[self.name]["consumer_key"]
1284
1285 def parent_access(url):
1286 return super(GitHub, self).access(url, **kwargs)
1287
1288 response = parent_access(url)
1289
1290 # additional action to get email is required:
1291 # https://developer.github.com/v3/users/emails/
1292 if response.status == 200:
1293 email_response = parent_access(url + "/emails")
1294 if email_response.status == 200:
1295 response.data["emails"] = email_response.data
1296
1297 # find first or primary email
1298 primary_email = None
1299 for item in email_response.data:
1300 is_primary = item["primary"]
1301 if not primary_email or is_primary:
1302 primary_email = item["email"]
1303
1304 if is_primary:
1305 break
1306
1307 response.data["email"] = primary_email
1308 return response
1309
1275
1310
1276 class Google(OAuth2):
1311 class Google(OAuth2):
1277 """
1312 """
@@ -1487,13 +1522,89 b' class LinkedIn(OAuth2):'
1487 return user
1522 return user
1488
1523
1489
1524
1525 class MicrosoftOnline(OAuth2):
1526 """
1527 Microsoft Online |oauth2| provider.
1528
1529 Supported :class:`.User` properties:
1530
1531 * email
1532 * first_name
1533 * id
1534 * last_name
1535 * location
1536 * name
1537 * phone
1538 * picture
1539 * username
1540
1541 Unsupported :class:`.User` properties:
1542
1543 * birth_date
1544 * city
1545 * country
1546 * gender
1547 * link
1548 * locale
1549 * nickname
1550 * postal_code
1551 * timezone
1552
1553 """
1554
1555 user_authorization_url = 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize'
1556 access_token_url = 'https://login.microsoftonline.com/common/oauth2/v2.0/token'
1557 user_info_url = "https://graph.microsoft.com/v1.0/me"
1558
1559 user_info_scope = ['openid profile']
1560
1561 supported_user_attributes = core.SupportedUserAttributes(
1562 id=True,
1563 email=True,
1564 first_name=True,
1565 last_name=True,
1566 location=True,
1567 name=True,
1568 phone=True,
1569 picture=True,
1570 username=True,
1571 )
1572
1573 def __init__(self, *args, **kwargs):
1574 super(MicrosoftOnline, self).__init__(*args, **kwargs)
1575 auth = args[0]
1576 provider_name = kwargs.get('provider_name')
1577 domain = auth.config.get(provider_name, {}).get('domain')
1578 if domain is not None:
1579 self.user_authorization_url = MicrosoftOnline.user_authorization_url.replace('/common/', '/%s/' % domain)
1580 self.access_token_url = MicrosoftOnline.access_token_url.replace('/common/', '/%s/' % domain)
1581
1582 @classmethod
1583 def _x_credentials_parser(cls, credentials, data):
1584 if data.get('token_type') == 'bearer':
1585 credentials.token_type = cls.BEARER
1586 return credentials
1587
1588 @staticmethod
1589 def _x_user_parser(user, data):
1590 user.id = data.get('id')
1591 user.name = data.get('displayName', '')
1592 user.first_name = data.get('givenName', '')
1593 user.last_name = data.get('surname', '')
1594 user.email = data.get('mail', '')
1595 user.location = data.get('officeLocation', '')
1596 user.phone = data.get('mobilePhone', '')
1597 user.picture = data.get('picture', '')
1598 user.username = data.get('userPrincipalName', '')
1599 return user
1600
1601
1490 class PayPal(OAuth2):
1602 class PayPal(OAuth2):
1491 """
1603 """
1492 PayPal |oauth2| provider.
1604 PayPal |oauth2| provider.
1493
1605
1494 * Dashboard: https://developer.paypal.com/webapps/developer/applications
1606 * Dashboard: https://developer.paypal.com/webapps/developer/applications
1495 * Docs:
1607 * Docs: https://developer.paypal.com/webapps/developer/docs/integration/direct/make-your-first-call/
1496 https://developer.paypal.com/webapps/developer/docs/integration/direct/make-your-first-call/
1497 * API reference: https://developer.paypal.com/webapps/developer/docs/api/
1608 * API reference: https://developer.paypal.com/webapps/developer/docs/api/
1498
1609
1499 .. note::
1610 .. note::
@@ -1540,8 +1651,8 b' class Reddit(OAuth2):'
1540
1651
1541 .. note::
1652 .. note::
1542
1653
1543 According to Reddit API
1654 According to
1544 `docs <https://github.com/reddit/reddit/wiki/API#rules>`_,
1655 `Reddit API docs <https://github.com/reddit/reddit/wiki/API#rules>`_,
1545 you have to include a `User-Agent` header in each API call.
1656 you have to include a `User-Agent` header in each API call.
1546
1657
1547 You can apply a default ``User-Agent`` header for all API calls in the
1658 You can apply a default ``User-Agent`` header for all API calls in the
@@ -1961,8 +2072,7 b' class Yandex(OAuth2):'
1961 Yandex |oauth2| provider.
2072 Yandex |oauth2| provider.
1962
2073
1963 * Dashboard: https://oauth.yandex.com/client/my
2074 * Dashboard: https://oauth.yandex.com/client/my
1964 * Docs:
2075 * Docs: http://api.yandex.com/oauth/doc/dg/reference/obtain-access-token.xml
1965 http://api.yandex.com/oauth/doc/dg/reference/obtain-access-token.xml
1966 * API reference:
2076 * API reference:
1967
2077
1968 Supported :class:`.User` properties:
2078 Supported :class:`.User` properties:
@@ -2042,6 +2152,7 b' PROVIDER_ID_MAP = ['
2042 GitHub,
2152 GitHub,
2043 Google,
2153 Google,
2044 LinkedIn,
2154 LinkedIn,
2155 MicrosoftOnline,
2045 OAuth2,
2156 OAuth2,
2046 PayPal,
2157 PayPal,
2047 Reddit,
2158 Reddit,
@@ -1,4 +1,4 b''
1 # -*- coding: utf-8 -*-
1
2 """
2 """
3 |openid| Providers
3 |openid| Providers
4 ----------------------------------
4 ----------------------------------
@@ -20,7 +20,7 b' Providers which implement the |openid|_ '
20
20
21 # We need absolute import to import from openid library which has the same
21 # We need absolute import to import from openid library which has the same
22 # name as this module
22 # name as this module
23
23 from __future__ import absolute_import
24 import datetime
24 import datetime
25 import logging
25 import logging
26 import time
26 import time
@@ -1,4 +1,4 b''
1 # -*- coding: utf-8 -*-
1
2 from authomatic import providers
2 from authomatic import providers
3
3
4
4
@@ -1,4 +1,4 b''
1 # -*- coding: utf-8 -*-
1
2 """Utilities for writing code that runs on Python 2 and 3"""
2 """Utilities for writing code that runs on Python 2 and 3"""
3
3
4 # Copyright (c) 2010-2015 Benjamin Peterson
4 # Copyright (c) 2010-2015 Benjamin Peterson
@@ -21,7 +21,7 b''
21 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 # SOFTWARE.
22 # SOFTWARE.
23
23
24
24 from __future__ import absolute_import
25
25
26 import functools
26 import functools
27 import itertools
27 import itertools
@@ -89,7 +89,7 b' class _LazyDescr(object):'
89
89
90 def __get__(self, obj, tp):
90 def __get__(self, obj, tp):
91 result = self._resolve()
91 result = self._resolve()
92 setattr(obj, self.name, result) # Invokes __set__.
92 setattr(obj, self.name, result) # Invokes __set__.
93 try:
93 try:
94 # This is a bit ugly, but it avoids running this again by
94 # This is a bit ugly, but it avoids running this again by
95 # removing this descriptor.
95 # removing this descriptor.
@@ -733,8 +733,8 b' if print_ is None:'
733 want_unicode = True
733 want_unicode = True
734 break
734 break
735 if want_unicode:
735 if want_unicode:
736 newline = unicode("\n")
736 newline = str("\n")
737 space = unicode(" ")
737 space = str(" ")
738 else:
738 else:
739 newline = "\n"
739 newline = "\n"
740 space = " "
740 space = " "
General Comments 0
You need to be logged in to leave comments. Login now